Webhooks & License Verification
Webhooks: subscribe to events
A plugin (or external integration) can subscribe to events emitted by medusa-backend.
Available event groups:
order.*— placed, paid, fulfilled, shipped, refunded, cancelled.cart.*— created, updated, abandoned (after 1h idle).customer.*— created, updated, deleted.product.*— created, updated, deleted.plugin.*— installed, uninstalled, updated, license_failed.
Subscribe in your plugin:
async register({ medusa }) {
medusa.events.on("order.placed", async ({ order }) => {
// your handler
})
}
Or via the admin API for external integrations:
curl -X POST https://shop.example.com/admin/webhooks \
-H "Authorization: Bearer $TOKEN" \
-d '{
"url": "https://my-service.com/hook",
"events": ["order.placed", "order.paid"],
"signing_secret": "shared-secret"
}'
Webhook payload + signature
POST /hook HTTP/1.1
Content-Type: application/json
X-Carpha-Event: order.placed
X-Carpha-Signature: t=1745000000,v1=ab12cd34...
X-Carpha-Delivery-Id: dly_01ABC...
{ "id": "order_01XYZ", ... }
Verify in your handler:
import crypto from "crypto"
function verify(rawBody, header, secret) {
const [t, v1] = header.split(",").map(s => s.split("=")[1])
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex")
return crypto.timingSafeEqual(
Buffer.from(v1, "hex"),
Buffer.from(expected, "hex")
)
}
Reject if signature fails or t is older than 5 minutes.
Retries
Failed deliveries (non-2xx response, or no response within 30s) retry with exponential backoff: 1m, 5m, 30m, 2h, 12h, then dead-letter. View delivery history in Settings → Webhooks → [endpoint] → Deliveries.
License verification
For paid plugins, call the marketplace verifier on every backend startup:
curl -X POST https://carphacom.com/store/marketplace/license/verify \
-H "Content-Type: application/json" \
-d '{
"license_key": "CC-XXXX-XXXX-XXXX",
"marketplace_id": "@vendor/plugin",
"instance_id": "inst_01ABC..."
}'
Response:
{
"valid": true,
"expires_at": "2027-04-20T00:00:00Z",
"tier": "pro",
"renewed_at": "2026-04-20T00:00:00Z"
}
If valid: false, the response includes reason: "revoked"|"expired"|"wrong_instance"|"unknown".
Offline grace period
If carphacom.com is unreachable, the plugin should accept the last successful verification
result for up to 7 days. Cache the response in the database and re-check on every startup.