Lifecycle & webhooks
ShopiMind drives your integration by sending it lifecycle webhooks. The kit receives them, verifies their signature and dispatches them — you write no reception or security code. You react, when needed, through hooks.
Two inbound flows, not to be confused
This page covers the webhooks emitted by ShopiMind toward your integration (lifecycle). The inbound calls emitted by your own application toward your integration (to push data in real time) are described in Inbound middleware — they use a different, per-installation signature.
The lifecycle
Install → (Configure) → Activate → … config_updated … → Deactivate → Uninstall| Event | When | Effect |
|---|---|---|
integration.installed | The merchant installs your integration. | ShopiMind creates the installation and hands you the API key (access_token), revoked for now. |
integration.activated | The merchant activates, after configuration. | ShopiMind un-revokes the key, sends the config; the kit tests the connection, provisions, then triggers the initial sync. |
integration.config_updated | The configuration changes. | The kit replays provisioning (best-effort). |
integration.deactivated | The merchant deactivates. | ShopiMind re-revokes the key. |
integration.uninstalled | The merchant uninstalls. | ShopiMind deletes the configuration and revokes the key. |
The API key is only valid while the status is "active"
Any call to the ShopiMind API before activate or after deactivate returns 401. The kit manages this key for you (encrypted in its store); your calls via ctx.spm only work while the status is active.
The signature
Every incoming webhook (lifecycle, connection test, remote data) is signed by ShopiMind and verified by the kit:
- headers
X-Shopimind-Signature(hexadecimal HMAC-SHA256) andX-Shopimind-Timestamp(seconds); - signature =
HMAC-SHA256("${timestamp}.${raw_body}", webhook_secret); - verified against the raw bytes of the body, in constant time, with an anti-replay window (300 s by default).
The webhook_secret is the one ShopiMind gave you at registration, placed in WEBHOOK_SECRET. That's all: the kit automatically rejects a request that is badly signed, too old or missing its headers. You have no validation code to write.
installation_id — an opaque token
Webhooks carry an installation_id: an opaque token issued by ShopiMind, stable over time (including after uninstall/reinstall). The kit exposes it as ctx.installationId. Never interpret it: it is not a shop identifier, it is the identity of the installation on the ShopiMind side.
Reacting to the lifecycle — hooks
To run your own logic at certain moments, declare hooks in defineIntegration. They all receive the ctx and are optional:
hooks: {
onActivate: async (ctx) => { /* after activation is validated */ },
onConfigUpdated: async (ctx) => { /* after a config change */ },
onDeactivate: async (ctx) => { /* on deactivation */ },
onUninstall: async (ctx) => { /* on uninstall */ },
}No onInstall hook
The kit only runs a hook from activation onward. There is no onInstall: at install time, the API key is still revoked and no call is possible. Do your initialization in onActivate.
A common use in onActivate: register the correlation bridge between the ShopiMind installation and your internal account, and pass to your application the secret for the inbound routes —
onActivate: async (ctx) => {
ctx.setExternalAccount({ id: ctx.settings.account, name: ctx.settings.account });
// ctx.inboundSecret can be shared with your app so it signs its inbound calls
// → see "Inbound middleware"
}The response contract
The kit always responds to ShopiMind with HTTP 200 and { "success": true } when everything is fine (and a controlled failure otherwise). You don't have to handle this contract — it is carried by the runtime.
Going further
- The integration kit — exposed routes,
ctx. - Inbound middleware — the inbound calls emitted by your application.
- Manifest & registration — getting the
webhook_secret. - Configuration —
test_connectionand remote data.