{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Best practices","description":"Find answers to your technical questions and learn how to integrate Mirakl products.","lang":"en-US","siteUrl":"https://developer.mirakl.com","keywords":"mirakl, documentation, api"},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"best-practices","__idx":0},"children":["Best practices"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This page collects the rules that hold across every domain: idempotency, event ordering, retries, and observability."," ","Build a connector to these once, and the same behaviour is correct in every flow."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"idempotency","__idx":1},"children":["Idempotency"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Idempotency applies in both directions, and it works differently in each one."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"events-you-receive-deduplicate-by-id","__idx":2},"children":["Events you receive: deduplicate by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Mirakl Connect gives you no idempotency key to send back."," ","Every event envelope carries a unique ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]}," instead, and that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]}," is the key."]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Deduplicate on the envelope ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]},"."]}," ","Record the ids you have processed, and make a repeat delivery a no-op."," ","This store is the state your connector's correctness depends on most."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Make every handler idempotent even so."]}," ","The deduplication store can race or lose an entry, and a handler that is safe to run twice covers that."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Do not invent an idempotency key of your own."]}," ","There is no header for an idempotency key on the events you receive."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Read ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/developer-guide/receiving-events#delivery-semantics"},"children":["Delivery semantics"]}," for the guarantee these rules answer."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"calls-you-make-upserts-are-idempotent-by-design","__idx":3},"children":["Calls you make: upserts are idempotent by design"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every write operation you call is an upsert, so a retry cannot apply the same change twice."," ","Send the complete state of the entity on every call, because a field you omit is a cleared field, not an untouched one."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A success response means \"accepted\", and not always \"applied\"."]}," ","Mirakl Connect versions the order and the return upserts by ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["channel_updated_at"]},", and it silently ignores an update whose timestamp is older than the value it already holds."," ","A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["204"]}," or a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200"]}," on such a call therefore does not prove that Mirakl Connect applied it."," ","Send a timestamp from the channel that is accurate and that moves forward, so a retry that arrives late cannot overwrite newer data."," ","Read ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/developer-guide/orders/patterns#restate-the-whole-object-and-let-the-newest-version-win"},"children":["Restate the whole object, and let the newest version win"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"ordering","__idx":4},"children":["Ordering"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Mirakl Connect orders the events only for each partition key, and your connector is what turns that guarantee into correct processing."," ","Read ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/developer-guide/receiving-events#ordering-per-destination"},"children":["Ordering per destination"]}," for the key of each event type, and for the configuration each destination needs."]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Preserve the order for each key, and parallelize across keys."]}," ","Walk the events of one key in their arrival order, and let unrelated keys advance independently."," ","A single global queue serializes work that shares no order, so it limits your throughput for no gain."," ","A lock for each key keeps the guarantee Mirakl Connect makes, and nothing more."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Never assume an order across keys."]}," ","Do not rely on the event of one entity arriving before the event of another."," ","Where a fact depends on a referenced entity existing first, such as a return that references an order, or an offer that references its product, establish that prerequisite yourself."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The ordering holds only alongside the deduplication."]}," ","Mirakl Connect can deliver the same event again, so the order for each key means something only once you deduplicate on the envelope ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]},"."," ","Read ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#idempotency"},"children":["Idempotency"]},"."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"retries-and-backoff","__idx":5},"children":["Retries and backoff"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Mirakl Connect owns the retries of the event delivery, and you own the retries of the calls you make."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The event delivery."]}," ","Mirakl Connect retries a failed delivery, then abandons the event once the retries are exhausted, as described in ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/developer-guide/receiving-events#delivery-semantics"},"children":["Delivery semantics"]},"."," ","Recovering the state your connector missed is therefore your work, through reconciliation."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The calls you make."]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Retry the transient failures, not the client errors."]}," ","A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["5xx"]}," or a network timeout is worth a retry, with ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["exponential backoff and jitter"]},"."," ","A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["4xx"]}," other than ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["429"]}," fails the same way on a retry, so fix the request instead."," ","On a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401"]},", refresh the token and retry once."," ","Read ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/developer-guide/authorization"},"children":["Authorization"]},"."," ","On a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["429"]},", wait for the delay of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Retry-After"]}," header."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Retrying an upsert is safe"]},", because upserts are idempotent, as described above."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Stay inside the published call frequency."]}," ","Back off to the cadence each operation publishes, rather than retry in a tight loop."," ","Read ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/developer-guide/calling-the-apis#call-frequency--rate-limiting"},"children":["Call frequency"]},"."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"observability","__idx":6},"children":["Observability"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The delivery is at-least-once and it has no dead-letter queue, so your logs and your correlation ids are how you prove what happened, and how you recover when something did not."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Capture and correlate:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The event ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]}]},", on every event you receive, for both the deduplication and the tracing end to end."," ","Your deduplication store doubles as an audit of what you processed."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The business key, which is the partition key"]},", the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["order_id"]}," or the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["product_id"]}," that the event or the call concerns."," ","It lets you follow everything about one entity, and reason about the order for each key."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["action_id"]}]},", for every command you act on and confirm."," ","It links the command you received to the status report you send back."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The error ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["code"]}," and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extensions"]}]},", on every REST call that failed."," ","Log the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["message"]}," too, but alert and branch on the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["code"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The token refreshes and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["403"]}," responses"]},", to tell a problem of authentication, which you fix with a refresh, from a problem of authorization, which you fix with the entitlements."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Never log a secret or a token."," ","A correlation id for the request and the response, generated for each call you make, makes the tracing and the support conversations faster."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"related-pages","__idx":7},"children":["Related pages"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/developer-guide/authorization"},"children":["Authorization"]},": the token lifecycle behind the handling of a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401"]}," above."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/developer-guide/calling-the-apis"},"children":["Calling the APIs"]},": the base path, synchronous against asynchronous operations, how to read a batch response, and the error model."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/developer-guide/receiving-events"},"children":["Receiving events"]},": the delivery guarantees these rules answer, and the ordering for each destination."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/developer-guide/orders/patterns"},"children":["Order management patterns"]},": the same rules worked through the order and return flows."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/content/product/connect-channel-platform/getting-started/api-and-event-directory"},"children":["API and event directory"]},": the operations and the events these rules apply to."]}]}]},"headings":[{"value":"Best practices","id":"best-practices","depth":1},{"value":"Idempotency","id":"idempotency","depth":2},{"value":"Events you receive: deduplicate by id","id":"events-you-receive-deduplicate-by-id","depth":3},{"value":"Calls you make: upserts are idempotent by design","id":"calls-you-make-upserts-are-idempotent-by-design","depth":3},{"value":"Ordering","id":"ordering","depth":2},{"value":"Retries and backoff","id":"retries-and-backoff","depth":2},{"value":"Observability","id":"observability","depth":2},{"value":"Related pages","id":"related-pages","depth":2}],"frontmatter":{"seo":{"title":"Best practices"}},"lastModified":"2026-11-06T15:01:50.979Z","pagePropGetterError":{"message":"","name":""}},"slug":"/content/product/connect-channel-platform/developer-guide/best-practices","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}