Skip to content

Handle order action events

This guide shows you how to consume OrderActionEvent for the order actions Mirakl Connect requests, which are to accept, ship, cancel, and refund an order, and to upload a document. You perform each action on the sales channel, and you report its outcome so that Mirakl Connect knows the action is done.

Business context

Some things happen to an order because a seller decided so inside Mirakl Connect, not because the channel changed. To accept a new order, to book its shipment, to cancel a line, and to issue a refund all start in Connect.

Mirakl Connect turns each decision into an OrderActionEvent and sends it to your connector to carry out on the channel. Your connector is what makes the seller's decision real on the channel, and its report back is what tells Mirakl Connect that the decision took effect.

When to use this

Handle an OrderActionEvent whenever one arrives with a type on the order side: ACCEPT, CREATE_SHIPMENT, CANCEL, REFUND, or UPLOAD_DOCUMENT.

Returns covers the actions on the return side, which are ACCEPT_RETURN, UPDATE_RETURN_TRACKING_INFORMATION, ACKNOWLEDGE_RETURN_RECEPTION, and CLOSE_RETURN. Their mechanics are the same.

This guide is distinct from Sync orders into Connect. There, you report the state the channel produced. Here, you perform an action Mirakl Connect requested, and then you report both its outcome and the resulting state.

How it works

ChannelMiddlewareMiraklSales channelChannel ConnectorMirakl ConnectSales channelChannel ConnectorMirakl Connectalt[action succeeds][action refused]OrderActionEvent (type, data.action_id)Deduplicate on id, persist action_idPerform the actiondonePUT /v1/orders/actions/{action_id} (SUCCESS)POST /v1/orders (resulting state)refusedPUT /v1/orders/actions/{action_id} (FAILED + ActionError)
ChannelMiddlewareMiraklSales channelChannel ConnectorMirakl ConnectSales channelChannel ConnectorMirakl Connectalt[action succeeds][action refused]OrderActionEvent (type, data.action_id)Deduplicate on id, persist action_idPerform the actiondonePUT /v1/orders/actions/{action_id} (SUCCESS)POST /v1/orders (resulting state)refusedPUT /v1/orders/actions/{action_id} (FAILED + ActionError)

Step by step

1. Receive and deduplicate the event

Every event arrives in the shared envelope, with an id, which is a ULID, a time, a type, and a data body. Deduplicate on the id, so that a repeat delivery does nothing twice. Persist the data.action_id, because it is how you report the outcome, and it must survive a restart.

2. Dispatch on the action type

The type names the action and selects the shape of data. The set of type values can grow, so plan for a value you do not know yet:

  • Map each action to the closest action on the channel, and keep that mapping as accurate as you can.
  • Report FAILED with ACTION_NOT_SUPPORTED only when the channel offers no action for the type.
  • Report an outcome for every action you receive, so that no action stays pending.

The data of each order action carries the identifiers, which are action_id, channel_id, channel_store_id, and channel_order_id, plus what the action needs. It also carries the sub_channel_code when the order belongs to a sub-channel (read Naming the sub-channel of an order).

Examples

ACCEPT accepts or refuses the order's lines:

{
  "type": "ACCEPT",
  "data": {
    "action_id": "1ba5fc4b-c4fd-4251-880b-3052e301bbd4",
    "channel_id": "0024",
    "channel_store_id": "2005",
    "channel_order_id": "0024_COMMERCIAL_ID-A",
    "order_lines": [
      { "channel_order_line_id": "0024_COMMERCIAL_ID-A-1", "accepted": true }
    ]
  }
}

CREATE_SHIPMENT ships the order, or a set of its lines, with a carrier. The carrier is optional, and it holds the display name of the carrier, not the channel_carrier_id you sent to upsertCarriers. warehouse_id is required only when the channel needs it:

{
  "type": "CREATE_SHIPMENT",
  "data": {
    "action_id": "2cb6fd5c-d5fe-4362-991c-4163f412cce5",
    "channel_id": "0024",
    "channel_store_id": "2005",
    "channel_order_id": "0024_COMMERCIAL_ID-A",
    "carrier": "DHL Express",
    "tracking_number": "1Z999AA1234567890",
    "tracking_url": "https://tracking.carrier.com/123456",
    "items": [
      { "channel_order_line_id": "0024_COMMERCIAL_ID-A-1", "id": "SELLER_ID_1", "quantity": 2 }
    ]
  }
}

When the channel requires a carrier and the event carries none, either send a default value the channel accepts, or report FAILED with MISSING_CARRIER.


CANCEL and REFUND carry the affected lines, in cancelations and in refunds. Each line carries a quantity, a reason, and the price and the shipping_price to cancel or refund, tax excluded, with taxes and shipping_taxes listed separately:

{
  "type": "REFUND",
  "data": {
    "action_id": "3dc7fe6d-e60f-4473-aa2d-5274051523f6",
    "channel_id": "0024",
    "channel_store_id": "2005",
    "channel_order_id": "0024_COMMERCIAL_ID-A",
    "refunds": [
      {
        "channel_order_line_id": "0024_COMMERCIAL_ID-A-1",
        "product_id": "SELLER_ID_1",
        "quantity": 1,
        "reason": "DEFECT",
        "price": { "amount": 24.99, "currency": "EUR" },
        "shipping_price": { "amount": 0, "currency": "EUR" }
      }
    ]
  }
}

UPLOAD_DOCUMENT provides a document for you to place on the channel. Mirakl Connect gives a document_name, a document_type, which is RETURN_LABEL or CUSTOMER_INVOICE, and a document_download_url, which is a presigned link that expires. When the document belongs to a return, document_entity names that return, with type: RETURN and the channel_return_id as its id:

{
  "type": "UPLOAD_DOCUMENT",
  "data": {
    "action_id": "4ed8af7e-f710-4584-bb3e-6385162634a7",
    "channel_id": "0024",
    "channel_store_id": "2005",
    "channel_order_id": "0024_COMMERCIAL_ID-A",
    "document_name": "return_label_RET-0024-001.pdf",
    "document_type": "RETURN_LABEL",
    "document_download_url": "https://storage.example.com/documents/4f690afa-6543-4f4b-a89f-81934e65ddd5?expires=1787059200&signature=247a2aa45f169edf4d187d54e7cc46e4",
    "document_entity": { "type": "RETURN", "id": "RET-0024-001" }
  }
}

Download the document from document_download_url before the link expires, then upload it to the channel. A document that belongs to the order itself, and not to a return, carries no document_entity.

3. Perform the action on the channel, idempotently

Carry the action out through the channel's own API. Guard it so that a repeat is safe: if Mirakl Connect delivers the same event twice, the channel operation must not happen twice, so no double shipment and no double refund. Tie the channel operation to the stored action_id, so that you can tell an action you already performed from a new one.

4. Report the outcome

Report the final result once, when the action is complete, with PUT /v1/orders/actions/{action_id}.

On success:

{ "status": "SUCCESS" }

On failure, send FAILED with one or more ActionError entries. Map the channel's refusal to the code that matches, and put the specifics in the message. The code is a bounded set:

  • INVALID_INPUT: the action carries data the channel refuses.
  • DATA_NOT_FOUND: the order, the line, or the return the action names does not exist on the channel.
  • UNEXPECTED: the channel failed for a reason your connector cannot classify.
  • UNAVAILABLE: the channel was unreachable.
  • TOO_MANY_REQUEST: the channel throttled the call.
  • ACTION_NOT_SUPPORTED: the channel offers no action for this type.
  • ORDER_IN_WRONG_STATE: the order's state on the channel does not allow the action.
  • RETURN_IN_WRONG_STATE: the return's state on the channel does not allow the action.
  • CARRIER_NOT_SUPPORTED: the channel does not accept the carrier of the action.
  • MISSING_CARRIER: the channel requires a carrier, and the action carries none.
  • WAREHOUSE_ID_NOT_FOUND: the channel does not know the warehouse_id.
  • WAREHOUSE_ID_REQUIRED: the channel requires a warehouse_id, and the action carries none.
  • OTHER: none of the codes above fits.
{
  "status": "FAILED",
  "errors": [
    { "code": "ORDER_IN_WRONG_STATE", "message": "Cannot ship: order is already cancelled" }
  ]
}

Three responses matter:

  • 204 confirms the report.
  • 400 with ACTION_ALREADY_FINAL means that you already reported the action. Treat it as a benign confirmation, not as an error to retry around.
  • 404 means that the action_id is unknown, because it is incorrect, or because the order it belongs to was deleted. An action never expires. Mirakl Connect deletes it with its order, when the seller unlinks the store or when the order's retention period ends.

5. Re-sync the resulting state

Reporting the outcome of an action does not update the status of the order. After an action changes the order's state, push the order's new state with upsertOrders. The action is not fully handled until you have reported both the outcome and the new state.

Common mistakes

  • To never report FAILED. An action with no reported outcome hangs, and Mirakl Connect waits forever. Report the result of every action, whether it succeeded or failed.
  • To lose or reuse the action_id. You must report the outcome against the exact action_id from the event. Persist it on receipt. Without it, you cannot close the action, and a duplicate event can drive the channel operation twice.
  • To acknowledge the delivery instead of the action. To acknowledge the message on the queue confirms receipt only. It does not report the outcome. updateActionStatus does that, sent once the action is really done.
  • To report the outcome but not re-sync. updateActionStatus does not carry the order's new status. Follow an action that changes the state with upsertOrders, or the copy of the order in Connect drifts from the channel.
  • To collapse every failure into OTHER. The code is what Mirakl Connect and the seller act on. Map the failure to the specific ActionError, such as ORDER_IN_WRONG_STATE, MISSING_CARRIER, or WAREHOUSE_ID_REQUIRED, and reserve OTHER for the failures that genuinely fit none of them.