IdukkiIdukki
Developers

Webhooks

Order webhooks in, merchant webhooks out

Two directions. Your store sends Idukki order events so a sale can be reconciled to the post that earned it. Idukki sends your systems events when content, rights, experiments and attributed purchases change: from the dashboard’s automation webhooks, and (rolling out) as signed webhooks straight from the API.

Order webhooks (store to Idukki)

Attribution is reconciled, not modelled: a completed order is matched back to the add-to-cart the widget recorded. That needs the store to tell Idukki about orders. Where that stands per platform:

PlatformStatusHow it works
ShopifyLiveThe Shopify app registers orders/create (and orders/paid) on install. Deliveries are HMAC-verified over the raw body with X-Shopify-Hmac-Sha256; cart attributes _idk_vid, _idk_biz, _idk_exp, _idk_var come back as note_attributes and are matched to the widget’s impression, click and add-to-cart rows. Deduplicated by order id, and a webhook row replaces any client-side purchase for the same visitor so revenue is never counted twice.
WooCommerceLive (matching rolling out)Pair the store (Settings, Integrations), then add an order.created / order.updated webhook pointing at /webhooks/woocommerce/orders. Deliveries are HMAC-verified with X-WC-Webhook-Signature and land in the same orders list as Shopify. Matching an order back to widget activity uses the customer id, a salted ip and user-agent fingerprint (always marked estimated), and _idk_vid when the store saves the visitor id on the order (the Idukki for WooCommerce plugin, in early access, does this at checkout). That matching step is rolling out now; until it reaches your account, WooCommerce orders are recorded but not yet matched.
BigCommerce, MagentoLive (matching rolling out)Both have order webhooks (/webhooks/bigcommerce/orders, /webhooks/magento/orders/{token}), connected from Settings, Integrations, so orders are recorded alongside Shopify’s. Matching uses the customer id and cart (BigCommerce: cart_id; Magento: quote_id, plus _idk_vid if a store module adds it). Neither order carries a user agent, so there is no fingerprint match. The matching step is rolling out now.
Magento, PrestaShop, SFCC modulesEarly accessThe Idukki store modules send each order to POST /webhooks/partner/orders, authenticated with a store connection key (Authorization: Bearer idk_pc_…), carrying the widget visitor id, customer id and cart id but no shopper name, email or address. Idempotent on the order id. See the install guides.
Custom / headlessManualSend window.idukki.track(‘purchase’, revenue) from your confirmation page for A/B ground truth, or talk to us about a custom order endpoint.

Merchant webhooks (Idukki to you)

Register any HTTPS URL in the dashboard (Settings, Developers, Webhooks): a Make.com custom webhook, an n8n node, a Zapier catch hook or your own endpoint. Pick the events, set an optional signing secret. Every delivery is a POST with the same envelope.

Envelopejson
POST https://your-endpoint.example/idukki
content-type: application/json
x-idukki-signature: sha256=<hex HMAC-SHA256 of the exact body, when a secret is set>

{
  "event": "rights.granted",
  "businessId": "YOUR_BGUID",
  "payload": { … },
  "timestamp": "2026-09-07T10:15:02.412Z"
}
Verify the signature (Node)js
import { createHmac, timingSafeEqual } from 'node:crypto'

export function verifyIdukki(rawBody, header, secret) {
  const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex')
  return header && header.length === expected.length && timingSafeEqual(Buffer.from(header), Buffer.from(expected))
}

Delivery rules

  • Fire-and-forget from the product’s hot paths: a slow or failing endpoint never blocks a lead capture, a rights decision or an order.
  • Eight-second timeout per delivery. Return a 2xx quickly and do the work asynchronously.
  • A 404 or 410 from your endpoint prunes the subscription (the Make.com convention for a deleted scenario). Re-add it in the dashboard.
  • The last delivery time and status are shown per webhook in the dashboard.

Event catalogue

Two families. Product events come from the central dispatcher and are what Slack, Zapier, Teams, the helpdesk integrations and outbound webhooks all receive. Widget events are the ones the dashboard’s webhook screen lists for widget and experiment lifecycle.

EventFires when
ugc.collectedA new post lands in a collection from a connected source, upload or competition entry.
review.createdA review arrives from a connected review source.
rights.grantedA creator consents (form, DM reply or hashtag entry under published terms).
rights.deniedA creator declines, or a reviewer rejects.
rights.expiringA licence is inside its renewal window.
rights.expiredThe licence window closed; the post has left every widget.
collection.archivedA collection was archived.
lead.capturedA lead form on a widget was submitted.
survey.responseA survey answer was recorded.
experiment.winnerA winner was declared on an A/B test.
purchase.attributedAn order webhook was reconciled to a widget interaction (Shopify today).
content.flaggedModeration or a visitor report flagged a post.
Widget eventFires when
widget.impressionA widget rendered for a visitor.
widget.clickA tile, hotspot or product link was clicked.
widget.conversionAn add-to-cart or purchase was attributed to the widget.
widget.config_updatedA widget’s settings were saved.
widget.version_restoredA previous widget version was restored.
experiment.startedAn A/B test started on a widget.
experiment.completedAn A/B test ended.
experiment.variant_winnerA variant was promoted.

Signed API webhooks (rolling out)

A second, stricter channel sent by the Idukki API itself. Each delivery is signed over a timestamp and the raw body, retried on failure, and logged per delivery. It is managed through GraphQL (createOutboundWebhook, updateOutboundWebhook, deleteOutboundWebhook, rotateOutboundWebhookSecret, testOutboundWebhook, outboundWebhooks, outboundWebhookDeliveries), up to 10 webhooks per account. The setup guide at /help/webhooks-guide walks through it.

EventSent when
ugc.createdA shopper upload reaches the review queue, or a post is imported. Both arrive pending.
ugc.approvedA post is approved in the dashboard, or a shopper upload is approved in review.
rights.grantedA creator approves on the consent form, or rights are approved in bulk.
rights.revokedRights are revoked in bulk.
order.attributedA live order from a store order webhook is tied to UGC as proven or in an influence tier. Backfills do not send it.
Requesthttp
POST https://your-endpoint.example/idukki
Content-Type: application/json
X-Idukki-Event: ugc.created
X-Idukki-Delivery: 1234
X-Idukki-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, t + "." + raw body)>

{ "id": "evt_1234", "type": "ugc.created", "createdAt": "2026-10-04T10:15:02.412Z", "data": { … } }
Verify (Node), computed the way the API signsjs
import { createHmac, timingSafeEqual } from 'node:crypto'

export function verifyIdukkiWebhook(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
  if (!signatureHeader) return false
  const parts = Object.fromEntries(
    signatureHeader.split(',').map((p) => p.trim().split('=')).filter((p) => p.length === 2),
  )
  const t = Number(parts.t)
  if (!Number.isInteger(t) || !parts.v1) return false
  if (Math.abs(Math.floor(Date.now() / 1000) - t) > toleranceSeconds) return false
  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
  const a = Buffer.from(expected, 'utf8')
  const b = Buffer.from(parts.v1, 'utf8')
  return a.length === b.length && timingSafeEqual(a, b)
}
  • Any 2xx is delivered; 10-second timeout; redirects are not followed.
  • Failures retry with the same body at the earliest 1 min, 5 min, 30 min, 2 h and 6 h later: six attempts, then failed. X-Idukki-Delivery and the envelope id stay the same across retries, so de-duplicate on them.
  • https only. Private, loopback, link-local, CGNAT and reserved addresses, localhost and internal host names are refused, and every connection re-checks the resolved address.
  • The whsec_ secret is returned once, on create and on rotate. Payloads never carry a shopper email or phone.
  • Not the same format as the dashboard webhooks above, which sign sha256=<hex> over the body alone and use the event names in the catalogue above. Tell them apart by the signature header value.

Webhooks Idukki receives from platforms

For completeness, the inbound routes on the API that platforms call. You do not configure these; the Shopify app and the Stripe integration register them.

MethodPathAuthSummary
GET/payment/checkoutsessionPayment Api
GET/payment/invoicessessionPayment Api
GET/payment/portalsessionPayment Api
POST/payment/webhookpublicPayment Api
POST/resend/webhookpublicMisc Routes Api
GET/shopify/detail/theme-pagesessionShopify Api
GET/shopify/email-configsessionShopify Api
POST/shopify/order-detailpublicShopify Api
POST/shopify/order-statssessionShopify Api
GET/shopify/order/detail/{id}publicShopify Api
POST/shopify/save/email-configsessionShopify Api
POST/shopify/webhook/getcustomerdatapublicShopify Api
POST/shopify/webhook/orderpublicShopify Api
POST/shopify/webhook/removecustomerdatapublicShopify Api
POST/shopify/webhook/shopdatapublicShopify Api
POST/shopify/webhook/uninstallpublicShopify Api

Only the webhook routes matter here; the rest of the Shopify group is on the API reference.

Checked against: nextapp/lib/integrations/events.ts, nextapp/lib/integrations/webhooks.ts, nextapp/redux/slice/webhookSlice.ts, nextapp/pages/api/webhooks/shopify/orders.ts, idukki-serverless-api/src/functions/shopify/README.md, nextapp/pages/api/integrations/woocommerce/, idukki-serverless-api/src/functions/outboundWebhooks/README.md, idukki-serverless-api/src/helpers/outboundWebhookCore.ts. Something here that the product does not do? Tell us and it gets fixed in the doc or the code.

We use cookies

We use essential cookies to run this site and optional analytics cookies to understand how it’s used. You can change your choice anytime in our privacy policy.