# Best practices

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.

## Idempotency

Idempotency applies in both directions, and it works differently in each one.

### Events you receive: deduplicate by `id`

Mirakl Connect gives you no idempotency key to send back.
Every event envelope carries a unique `id` instead, and that `id` is the key.

* **Deduplicate on the envelope `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.
* **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.
* **Do not invent an idempotency key of your own.**
There is no header for an idempotency key on the events you receive.


Read [Delivery semantics](/content/product/connect-channel-platform/developer-guide/receiving-events#delivery-semantics) for the guarantee these rules answer.

### Calls you make: upserts are idempotent by design

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.

**A success response means "accepted", and not always "applied".**
Mirakl Connect versions the order and the return upserts by `channel_updated_at`, and it silently ignores an update whose timestamp is older than the value it already holds.
A `204` or a `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 [Restate the whole object, and let the newest version win](/content/product/connect-channel-platform/developer-guide/orders/patterns#restate-the-whole-object-and-let-the-newest-version-win).

## Ordering

Mirakl Connect orders the events only for each partition key, and your connector is what turns that guarantee into correct processing.
Read [Ordering per destination](/content/product/connect-channel-platform/developer-guide/receiving-events#ordering-per-destination) for the key of each event type, and for the configuration each destination needs.

* **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.
* **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.
* **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 `id`.
Read [Idempotency](#idempotency).


## Retries and backoff

Mirakl Connect owns the retries of the event delivery, and you own the retries of the calls you make.

**The event delivery.**
Mirakl Connect retries a failed delivery, then abandons the event once the retries are exhausted, as described in [Delivery semantics](/content/product/connect-channel-platform/developer-guide/receiving-events#delivery-semantics).
Recovering the state your connector missed is therefore your work, through reconciliation.

**The calls you make.**

* **Retry the transient failures, not the client errors.**
A `5xx` or a network timeout is worth a retry, with **exponential backoff and jitter**.
A `4xx` other than `401` and `429` fails the same way on a retry, so fix the request instead.
On a `401`, refresh the token and retry once.
Read [Authorization](/content/product/connect-channel-platform/developer-guide/authorization).
On a `429`, wait for the delay of the `Retry-After` header.
* **Retrying an upsert is safe**, because upserts are idempotent, as described above.
* **Stay inside the published call frequency.**
Back off to the cadence each operation publishes, rather than retry in a tight loop.
Read [Call frequency](/content/product/connect-channel-platform/developer-guide/calling-the-apis#call-frequency--rate-limiting).


## Observability

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.

Capture and correlate:

* **The event `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.
* **The business key, which is the partition key**, the `order_id` or the `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.
* **The `action_id`**, for every command you act on and confirm.
It links the command you received to the status report you send back.
* **The error `code` and the `extensions`**, on every REST call that failed.
Log the `message` too, but alert and branch on the `code`.
* **The token refreshes and the `401` and `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.


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.

## Related pages

* [Authorization](/content/product/connect-channel-platform/developer-guide/authorization): the token lifecycle behind the handling of a `401` above.
* [Calling the APIs](/content/product/connect-channel-platform/developer-guide/calling-the-apis): the base path, synchronous against asynchronous operations, how to read a batch response, and the error model.
* [Receiving events](/content/product/connect-channel-platform/developer-guide/receiving-events): the delivery guarantees these rules answer, and the ordering for each destination.
* [Order management patterns](/content/product/connect-channel-platform/developer-guide/orders/patterns): the same rules worked through the order and return flows.
* [API and event directory](/content/product/connect-channel-platform/getting-started/api-and-event-directory): the operations and the events these rules apply to.