# MD for: https://www.mercadopago.com.ar/developers/pt/docs/qr-code-ca/migrate-dynamic-qr-model-to-orders.md \# How to migrate from the Dynamic QR Model API to the Orders API The Orders API unifies payment processing with QR Code in Mercado Pago, consolidating the two creation methods of the Dynamic QR Model API into a single endpoint and extending query and cancellation resources to both modes. In addition, all new Mercado Pago features will be developed on the Orders API. The Dynamic QR Model API had two creation methods with distinct behaviors: - \*\*\`POST /qrs\`\*\*, dynamic QR mode: creates an order and returns a unique QR string per transaction in the \`qr\_data\` field, without associating it with any checkout. The application is responsible for converting \`qr\_data\` into a QR Code and displaying it to the customer. - \*\*\`PUT /qrs\`\*\*, hybrid QR mode: creates an order, associates it with the checkout identified by \`{external\_pos\_id}\`, updating the linked static QR, and also returns the dynamic QR in the \`qr\_data\` field. The Orders API unifies these two methods into a single endpoint :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"}, differentiating behavior via the \`config.qr.mode\` parameter: the \`dynamic\` value corresponds to the legacy POST flow and the \`hybrid\` value corresponds to the legacy PUT flow. This distinction also affected query and cancellation resources, which were available in the Dynamic QR Model API only for the PUT flow. In the Orders API, these resources become available for both flows via \`order\_id\`. In addition, in this modality, the Orders API enables cash withdrawal (\*extracash\* and \*cash-out\*), payment method discounts, category discounts, and interest-free installments. For more details, see the \[transaction processing documentation\](https://www.mercadopago.com.ar/developers/en/docs/qr-code-ca/payment-processing). To migrate your integration from the Dynamic QR Model API to the Orders API, follow the steps below. ## Map endpoint changes Before starting the migration steps, review the table below for an overview of all endpoint changes. The Orders API unifies the two creation methods into a single :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"} and extends query and cancellation resources to both modes. | Operation | Dynamic QR Model API | Orders API | |---|---|---| | Create order (dynamic mode) | :TagComponent{tag="API" text="POST /instore/orders/qr/seller/collectors/{user\_id}/pos/{external\_pos\_id}/qrs" href="/developers/en/reference/qr-dynamic/\_instore\_orders\_qr\_seller\_collectors\_user\_id\_pos\_external\_pos\_id\_qrs/post"} | :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"} with \`config.qr.mode: dynamic\` | | Create order (hybrid mode) | :TagComponent{tag="API" text="PUT /instore/orders/qr/seller/collectors/{user\_id}/pos/{external\_pos\_id}/qrs" href="/developers/en/reference/qr-dynamic/\_instore\_orders\_qr\_seller\_collectors\_user\_id\_pos\_external\_pos\_id\_qrs/put"} | :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"} with \`config.qr.mode: hybrid\` | | Get order | :TagComponent{tag="API" text="GET /instore/qr/seller/collectors/{user\_id}/pos/{external\_pos\_id}/orders" href="/developers/en/reference/instore\_orders/\_instore\_qr\_seller\_collectors\_user\_id\_pos\_external\_pos\_id\_orders/get"} (PUT flow only) | :TagComponent{tag="API" text="GET /v1/orders/{order\_id}" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/get-order/get"} (both modes) | | Cancel order | :TagComponent{tag="API" text="DELETE /instore/orders/qr/seller/collectors/{user\_id}/pos/{external\_pos\_id}/qrs" href="/developers/en/reference/qr-dynamic/\_instore\_orders\_qr\_seller\_collectors\_user\_id\_pos\_external\_pos\_id\_qrs/delete"} (PUT flow only) | :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/cancel" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/cancel-order/post"} (both modes) | | Refund order | Via Payments API (no dedicated endpoint in the Dynamic QR Model API) | :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/refund" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/refund-order/post"} | ## Update the status and notifications model One of the most significant changes between the Dynamic QR Model API and the Orders API is the \`status\` model. In the Dynamic QR Model API, the state of a transaction was determined by monitoring notifications from two independent topics , \`payments\` and \`merchant\_orders\`, with different fields and values for each. The Orders API unifies this behavior into a single \`status\` field on the order. See the complete mapping below. In addition, the Orders API also introduces the \`status\_detail\` field, which provides greater detail on the transaction state. The possible values are \`created\`, \`canceled\`, \`accredited\`, \`refunded\`, and \`expired\`. :::AccordionComponent{title="Map status values" pill="1"} In the Dynamic QR Model API, the \`merchant\_order\` is created when the order is generated, so notifications from the \`merchant\_orders\` topic are available from the start of the flow. The \`payment\` object, on the other hand, is only created when a payment attempt is made, whether approved or rejected. Applications that monitored only the \`payments\` topic received no notifications until the user initiated the payment process. In the Orders API, this behavior is unified: the \`status\` field of the order reflects the complete state from creation, without the need to monitor two independent topics. | \`payments\` topic (\`status\`) | \`merchant\_orders\` topic (\`status\` / \`order\_status\`) | Orders API (\`status\`) | Note | |---|---|---|---| | — | \`opened\` (no payment or rejected payment) | \`created\` | In the Dynamic QR Model API, \`opened\` indicated the order had no approved payment yet. In the Orders API, this state is explicit from creation. | | \`approved\` | \`closed\` + \`order\_status: "paid"\` | \`processed\` | Both topics converged on the same result: approved payment. In the Orders API, the state is self-contained. | | \`rejected\` | \`opened\` (the \`merchant\_order\` does not change state and remains open) | No equivalent | In the Dynamic QR Model API, a rejected payment left the \`merchant\_order\` in \`opened\` waiting for a new attempt. In the Orders API, rejected payments are not exposed. The state remains \`created\` until an approved payment is received. | | \`refunded\` | \`closed\` + \`order\_status: "reverted"\` | \`refunded\` | Full refund. In the \`merchant\_orders\` topic, identifiable by \`order\_status: "reverted"\`. | | — | — | \`canceled\` | New status with no equivalent in the Dynamic QR Model API. | | — | — | \`expired\` | New status with no equivalent in the Dynamic QR Model API. | ::: Regarding notifications, in the Legacy API they could be configured via the \`notification\_url\` field in the request body (IPN model) or via webhooks with the \`payments\` and \`merchant\_orders\` topics. In the Orders API, the \`notification\_url\` field does not exist and the \`payments\` and \`merchant\_orders\` topics are not compatible. Configure notifications in \[Your integrations\](https://www.mercadopago.com.ar/developers/panel/app), subscribing to the \*\*"Order (Mercado Pago)"\*\* (\`orders\`) topic, before going to production. For more information, see the \[notifications documentation\](https://www.mercadopago.com.ar/developers/en/docs/qr-code-ca/notifications). ## Adapt headers The Orders API introduces a mandatory header change that affects all endpoints. The Access Token must be sent via the \`Authorization\` header. Apply the following change before testing any other resource. :::AccordionComponent{title="Add the X-Idempotency-Key header" pill="1"} The \`X-Idempotency-Key\` header is required for order creation, cancellation, and refund operations. It ensures that a repeated request with the same key returns the original result without processing the operation again. Send a UUID v4 or a unique random string per request. \*\*GET\*\* operations do not require this header. > WARNING > > If the same \`X-Idempotency-Key\` is reused with a different body, the API will return the \`idempotency\_key\_already\_used\` error. Generate a new key for each distinct operation. ::: ## Migrate order creation The Orders API unifies the two creation methods into a single :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"}, differentiating behavior via the \`config.qr.mode\` parameter. The \`POST /qrs\` endpoint, dynamic QR mode, is equivalent to \`config.qr.mode: dynamic\`, and the \`PUT /qrs\` endpoint, hybrid QR mode, is equivalent to \`config.qr.mode: hybrid\`. In both cases, \`{user\_id}\` is no longer a path parameter, \`{external\_pos\_id}\` moves to the body, and the \`type: "qr"\` field becomes required. Follow the steps below to adapt the request, the response, and error handling for each mode. > NOTE > > In the Dynamic QR Model API, the order identifier was returned in the \`in\_store\_order\_id\` field. In the Orders API, that identifier moves to the \`id\` field, with an alphanumeric format (e.g., \`ORD00001111222233334444555566\`). Capture the \`id\` value from the creation \*response\*, as it is required for subsequent queries, cancellations, and refunds. :::::AccordionComponent{title="Map request body fields" pill="1"} The table below shows the body field mapping for both creation modes. The Dynamic QR Model API column indicates the behavior of each field in the \*\*POST\*\* and \*\*PUT\*\* flows. | Dynamic QR Model API (POST / PUT) | Orders API | Description | Change | |---|---|---|---| | \`{user\_id}\` (path param, both) | Does not exist | User identifier. In the Orders API, identity is obtained directly from the Access Token. | No longer a path parameter. | | \`{external\_pos\_id}\` (path param, both) | \`config.qr.external\_pos\_id\` | External checkout identifier. In the Dynamic QR Model API, it was sent in the path. In the Orders API, it is sent in the body within \`config.qr\`. | Moves from path parameter to body. | | \`external\_reference\` (required in both) | \`external\_reference\` | Reference that can sync with the point-of-sale system. In the Orders API, max. 64 characters, only letters, numbers, \`-\` and \`\_\`. Cannot contain PII data. | No change in requirement. | | \`title\` (required in POST, optional in PUT) | Does not exist | Purchase title. | Removed. The description is sufficient. | | \`description\` (required in POST, optional in PUT) | \`description\` | Purchase description. In the Dynamic QR Model API, it existed both at the root level and inside \`items\[\]\`. In the Orders API, it exists only at the root level. Max. 150 characters. | In the Orders API, becomes optional and is managed only at the root level. | | \`items\[\].description\` | Does not exist | Item description. | Removed. Description is managed only at the order root level. | | \`notification\_url\` | Does not exist | URL to receive notifications. | Removed. Configure webhook notifications in your application within Your integrations, selecting the \*\*"Order (Mercado Pago)"\*\* topic. For more information, see the \[notifications documentation\](https://www.mercadopago.com.ar/developers/en/docs/qr-code/notifications). | | \`expiration\_date\` | \`expiration\_time\` | Order validity. In the Dynamic QR Model API, it was an absolute date. In the Orders API, it is a relative duration in ISO 8601 format. In dynamic mode, the default is \`PT15M\` and the submitted value is honored. In hybrid mode, the dynamic QR defaults to \`PT15M\` (honored). The static QR has a maximum of \`PT10M\` (larger values are ignored). | Changes from absolute date to relative duration in ISO 8601 format. | | \`total\_amount\` (required in both) | \`total\_amount\` | Total order amount. In the Dynamic QR Model API, it was a \`number\`. In the Orders API, it is a decimal string representing the sum of all transactions (including the cash withdrawal amount when applicable), including cash withdrawal when applicable. Accepts two decimal places (e.g., \`"15.00"\`) or none. | Changes from \`number\` to decimal \`string\`. | | \`cash\_out\[\].amount\` | \`transactions.cash\_outs\[\].amount\` | Cash withdrawal amount. Only 1 cash withdrawal transaction per order is allowed. | Moves to the \`transactions.cash\_outs\` node. | | \`sponsor.id\` | \`integration\_data.sponsor.id\` | \`USER\_ID\` of the Mercado Pago account of the integrating system. | Moves to the \`integration\_data.sponsor\` node. | | \`items\[\].sku\_number\` | \`items\[\].external\_code\` | Item code in the external system. | Renamed to \`items\[\].external\_code\`. | | \`items\[\].category\` | Does not exist | Item category. | Removed. No equivalent in the Orders API. | | \`items\[\].title\` (required in both) | \`items\[\].title\` | Item name. Max. 150 characters. | No change in requirement. | | \`items\[\].unit\_price\` (required in both) | \`items\[\].unit\_price\` | Unit price. In the Dynamic QR Model API, it was a \`number\`. In the Orders API, it is a decimal \`string\`. Accepts two decimal places (e.g., \`"15.00"\`) or none. | Changes from \`number\` to decimal \`string\`. | | \`items\[\].quantity\` (required in both) | \`items\[\].quantity\` | Item quantity. In the Dynamic QR Model API, it was a \`number\`. In the Orders API, it is an \`integer\`. | Changes from \`number\` to \`integer\`. | | \`items\[\].unit\_measure\` (required in both) | \`items\[\].unit\_measure\` | Item unit of measure. Max. 10 characters. | No change in requirement. | | \`items\[\].total\_amount\` (required in both) | Does not exist | Item total (unit price × quantity). | Removed. No equivalent in the Orders API. | | \`items\[\].currency\_id\` | Does not exist | Item currency identifier. | Removed. Currency is determined automatically by the seller account. | | \`items\[\].external\_categories\[\].id\` | \`items\[\].external\_categories\[\].id\` | Item category identifier. Enables category-based discounts. Cannot be used together with \`discounts\`. | No change. | | \`taxes\[\].type\` | Does not exist | Tax type. | Removed. The new API does not support this field in the request. | | \`taxes\[\].value\` | Does not exist | Tax amount. | Removed. | | \`taxes\[\].percentage\` | Does not exist | Tax percentage. | Removed. | | \`marketplace\` | Does not exist | Marketplace identifier. | Removed. The marketplace is identified by the OAuth Access Token. | | \`internal\_metadata\` | Does not exist | Internal metadata. | Removed. | | \`customization\` | Does not exist | Customization object. | Removed. | | \`X-platform-id\` (\*header\*) | \`integration\_data.platform\_id\` | Platform identifier, assigned by Mercado Pago. | Moves from \*header\* to \*body\*. | | \`X-integrator-id\` (\*header\*) | \`integration\_data.integrator\_id\` | Integrator identifier, assigned by Mercado Pago. Must have the \`dev\_\` prefix. | Moves from \*header\* to \*body\*. | The Orders API also introduces the following fields with no equivalent in the Dynamic QR Model API. | Field | Description | |---|---| | \`type\` | Order type. For QR Code payments, the only valid value is \`"qr"\`. \*\*Required.\*\* | | \`config.qr.mode\` | QR mode. \`dynamic\` for the flow equivalent to the POST legacy (unique QR per transaction). \`hybrid\` for the flow equivalent to the PUT legacy. Queues the order at the checkout and returns the dynamic QR in the \`qr\_data\` field. | | \`transactions.payments\[\].amount\` | Payment amount within the \`transactions\` node. Accepts two decimal places (e.g., \`"15.00"\`) or none. Only 1 payment transaction per order. | | \`marketplace\_fee\` | Marketplace fee amount. Exclusive to OAuth integrations. Accepts two decimal places (e.g., \`"15.00"\`) or none. | | \`discounts.payment\_methods\[\].new\_total\_amount\` | New total order amount when a discount is applied. Accepts two decimal places (e.g., \`"15.00"\`) or none. Cannot be used together with \`items\[\].external\_categories\`. | | \`discounts.payment\_methods\[\].type\` | Payment method to which the discount applies. The possible values are \`debit\_card\`, \`credit\_card\`, \`account\_money\`, \`prepaid\_card\`. Up to 4 methods can be defined. | > SUCCESS\_MESSAGE > > See all available parameters in the :TagComponent{tag="API" text="API Reference" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"}. ::::: :::AccordionComponent{title="Adapt creation response fields" pill="2"} The table below shows the response field mapping. The columns indicate the origin in each flow of the Dynamic QR Model API. | Dynamic QR Model API | Orders API | Description | Change | |---|---|---|---| | \`qr\_data\` (POST and PUT) | \`type\_response.qr\_data\` | EMVCo QR string for conversion into a QR Code. In the Orders API, present only when \`config.qr.mode\` is \`dynamic\` or \`hybrid\`. | Renamed and moved to the \`type\_response\` node. | | \`in\_store\_order\_id\` (POST and PUT) | \`id\` | Identifier of the created order. In the Dynamic QR Model API, it was a UUID (e.g., \`d4e8ca59-...\`). In the Orders API, it is an alphanumeric ID with the \`ORD\` prefix (e.g., \`ORD00001111222233334444555566\`). | Renamed from \`in\_store\_order\_id\`. Format changes from UUID to alphanumeric ID. | The Orders API also introduces the following fields in the creation response. | Field | Description | |---|---| | \`user\_id\` | Identifier of the user who created the order. | | \`type\` | Order type. For QR Code: always \`qr\`. | | \`external\_reference\` | External reference of the order. | | \`description\` | Product or service description. | | \`expiration\_time\` | Expiration duration in ISO 8601 format. | | \`processing\_mode\` | Processing mode. For QR Code: always \`automatic\`. | | \`total\_amount\` | Total order amount. | | \`country\_code\` | Site/country identifier (e.g., \`"AR"\`). | | \`marketplace\_fee\` | Marketplace fee. Exclusive to OAuth integrations. | | \`status\` | Current order status. When created it is \`created\`. | | \`status\_detail\` | Current order status detail. The possible values are \`created\`, \`canceled\`, \`accredited\`, \`refunded\`, \`expired\`. | | \`currency\` | Order currency. The possible values are \`ARS\`, \`BRL\`, \`CLP\`, \`UYU\`. | | \`created\_date\` | Creation date. Format \`yyyy-MM-ddTHH:mm:ss.sssZ\`. | | \`last\_updated\_date\` | Last update date. Format \`yyyy-MM-ddTHH:mm:ss.sssZ\`. | | \`config.qr.external\_pos\_id\` | External identifier of the checkout associated with the order. | | \`config.qr.mode\` | Configured QR mode (\`dynamic\` or \`hybrid\`). | | \`transactions.payments\[\].id\` | Payment transaction identifier. | | \`transactions.payments\[\].amount\` | Payment amount. | | \`transactions.payments\[\].status\` | Payment status. When created it is \`created\`. | | \`transactions.payments\[\].status\_detail\` | Payment status detail. When created it is \`ready\_to\_process\`. | | \`transactions.cash\_outs\[\].id\` | Cash withdrawal transaction identifier, generated by Mercado Pago. | | \`transactions.cash\_outs\[\].amount\` | Cash withdrawal amount. | | \`transactions.cash\_outs\[\].status\` | Cash withdrawal status. When created it is \`created\`. The possible values are \`created\`, \`canceled\`, \`processed\`, \`expired\`. | | \`transactions.cash\_outs\[\].status\_detail\` | Cash withdrawal status detail. When created it is \`ready\_to\_process\`. | | \`integration\_data.application\_id\` | Mercado Pago application identifier. | | \`integration\_data.platform\_id\` | Platform identifier. | | \`integration\_data.integrator\_id\` | Integrator identifier. | | \`integration\_data.sponsor.id\` | \`USER\_ID\` of the integrating system. | ::: :::AccordionComponent{title="Update error handling on creation" pill="3"} \*\*POST\*\* and \*\*PUT\*\* flow errors are identical in the Dynamic QR Model API. The Orders API consolidates most field-specific validation errors into the generic codes \`property\_value\` and \`property\_type\`, with the field detail in the response body. Additionally, response messages are more descriptive, making it easier to identify and resolve issues. See the tables below for details. ### Errors that change behavior The following errors exist in both APIs but with different behavior in the Orders API. | HTTP | Dynamic QR Model API | Orders API | Note | |---|---|---|---| | \`400\` | Multiple: \`invalid\_collectorId\`, \`invalid\_externalPosId\`, \`invalid\_external\_reference\`, \`invalid\_total\_amount\`, \`invalid\_items.\*\`, \`invalid\_sponsor.\*\` and others | \`property\_value\` | \`property\_value\` consolidates field validation errors. The field with the error is indicated in the response detail. | | \`400\` → \`401\` | (authentication via middleware, \`400\`) | \`unauthorized\` | In the Dynamic QR Model API, it was \`400\`. In the Orders API, it is an explicit \`401\`. | | \`400\` | \`invalid\_cash\_out.amount\`, \`invalid\_amount\` (cash\_out) | \`property\_value\` | An invalid value was sent for \`transactions.cash\_outs\[\].amount\`. | | \`400\` | \`error\_creating\_seller\_qr\_order\` (cash withdrawal without items) | \`bad\_request\` | It is not possible to send a cash withdrawal without items. This business rule also applies in the new API. | ### Errors that remain unchanged The following error has the same behavior in both APIs. | HTTP | Error | Note | |---|---|---| | \`500\` | Generic | Internal error. Check the response and retry the request. | ### Errors introduced by the Orders API The following errors have no equivalent in the Dynamic QR Model API. | HTTP | Error | Note | |---|---|---| | \`400\` | \`sponsor\_id\_not\_valid\` | Invalid value for \`integration\_data.sponsor.id\`. | | \`400\` | \`marketplace\_not\_valid\` | The Access Token was not obtained via OAuth. | | \`400\` | \`property\_type\` | An invalid data type was sent for a property (e.g.: \`integer\` instead of \`string\`). Check the message to identify which one. | | \`400\` | \`empty\_required\_header\` | \`X-Idempotency-Key\` is missing. | | \`400\` | \`unsupported\_site\` | Country not supported. | | \`400\` | \`unsupported\_properties\` | Unsupported property in the body. The field with the error is indicated in the response detail. | | \`400\` | \`seller\_configuration\` | The seller is not authorized to perform cash withdrawal transactions. Contact the Mercado Pago commercial team to enable this feature. | | \`401\` | \`unauthorized\` | Invalid or expired Access Token. | | \`404\` | \`pos\_not\_found\` | The \`external\_pos\_id\` does not belong to any checkout. | | \`404\` | \`marketplace\_fee\_not\_allowed\` | Sending \`marketplace\_fee\` is not allowed. The marketplace was not found. | | \`409\` | \`idempotency\_key\_already\_used\` | The \`X-Idempotency-Key\` was already used with a different request in the last 24 hours. | ::: ## Update order queries The :TagComponent{tag="API" text="GET /v1/orders/{order\_id}" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/get-order/get"} query endpoint is available for both dynamic and hybrid modes in the Orders API. In the Dynamic QR Model API, queries were available only for the \*\*PUT flow\*\* and queried by checkout, not by order. The POST flow had no query endpoint. State was obtained exclusively via \`payments\` and \`merchant\_orders\` notifications. The Orders API allows querying the complete order state directly by \`order\_id\`, including status, payments, refunds, and payment method data. \`\`\`curl curl -X GET \\ 'https://api.mercadopago.com/v1/orders/{{ORDER\_ID}}' \\ -H 'Authorization: Bearer {{ACCESS\_TOKEN}}' \`\`\` :::AccordionComponent{title="Adapt query response fields" pill="1"} The \*legacy\* fields in this table correspond to the \*\*PUT flow\*\* query endpoint only. In the \*\*POST flow\*\*, all response fields are new. The table below shows the PUT flow fields that existed in both APIs and changed during migration. | Dynamic QR Model API, PUT flow | Orders API | Description | Change | |---|---|---|---| | Does not exist | \`id\` | Order identifier. | New field. The legacy query endpoint did not return the order ID. | | \`external\_reference\` | \`external\_reference\` | External reference of the order. | No change. | | \`description\` | \`description\` | Purchase description. | No change. | | \`title\` | Does not exist | Purchase title. | Removed. | | \`notification\_url\` | Does not exist | URL configured for notifications. | Removed. | | \`expiration\_date\` | \`expiration\_time\` | Order validity. In the Dynamic QR Model API, it was an absolute date. In the Orders API, it is a relative duration in ISO 8601\. In dynamic mode, the default is \`PT15M\`. In hybrid mode, the dynamic QR has \`PT15M\` and the static QR has a maximum of \`PT10M\`. | Changes from absolute date to relative duration. | | \`total\_amount\` | \`total\_amount\` | Total order amount. In the Dynamic QR Model API, it was a \`number\`. In the Orders API, it is a decimal \`string\`. | Changes from \`number\` to decimal \`string\`. | | \`marketplace\_fee\` | \`marketplace\_fee\` | Marketplace fee. In the Dynamic QR Model API, it was a \`number\`. In the Orders API, it is a decimal \`string\`. | Changes from \`number\` to decimal \`string\`. | | \`sponsor.id\` | \`integration\_data.sponsor.id\` | \`USER\_ID\` of the integrating system. In the Dynamic QR Model API, it was an \`integer\`. In the Orders API, it is a \`string\`. | Moves to \`integration\_data.sponsor\`. Changes from \`integer\` to string. | The Orders API also introduces the following fields in the query response. | Field | Description | |---|---| | \`status\` | Current order status. The possible values are \`created\`, \`canceled\`, \`processed\`, \`refunded\`, \`expired\`. In the POST flow, state was obtained only via webhooks. | | \`status\_detail\` | Current order status detail. The possible values are \`created\`, \`canceled\`, \`accredited\`, \`refunded\`, \`expired\`. | | \`currency\` | Order currency. The possible values are \`ARS\`, \`BRL\`, \`CLP\`, \`UYU\`. | | \`created\_date\` / \`last\_updated\_date\` | Creation and last update dates. Format \`yyyy-MM-ddTHH:mm:ss.sssZ\`. | | \`config.qr.external\_pos\_id\` | External identifier of the checkout associated with the order. | | \`config.qr.mode\` | Configured QR mode. | | \`transactions.payments\[\].id\` | Payment transaction identifier. | | \`transactions.payments\[\].amount\` | Payment amount. | | \`transactions.payments\[\].refunded\_amount\` | Refunded amount. Present only when a refund has occurred. | | \`transactions.payments\[\].paid\_amount\` | Total amount paid. | | \`transactions.payments\[\].status\` | Current payment status. | | \`transactions.payments\[\].status\_detail\` | Detailed payment status. | | \`transactions.payments\[\].reference\_id\` | Identifier of the payment associated with the order. | | \`transactions.payments\[\].payment\_method.type\` | Selected payment method. | | \`transactions.payments\[\].payment\_method.installments\` | Number of installments. | | \`transactions.payments\[\].payment\_method.id\` | Payment method or card brand identifier. | | \`transactions.payments\[\].discounts.type\` | Payment method with which the discount was applied. Present only if a discount was applied. | | \`transactions.cash\_outs\[\].id\` | Cash withdrawal transaction identifier, generated by Mercado Pago. | | \`transactions.cash\_outs\[\].amount\` | Cash withdrawal amount. | | \`transactions.cash\_outs\[\].status\` | Cash withdrawal status. When created it is \`created\`. The possible values are \`created\`, \`canceled\`, \`processed\`, \`expired\`. | | \`transactions.cash\_outs\[\].status\_detail\` | Cash withdrawal status detail. When created it is \`ready\_to\_process\`. | | \`transactions.refunds\[\].id\` | Refund identifier. | | \`transactions.refunds\[\].transaction\_id\` | Identifier of the payment being refunded. | | \`transactions.refunds\[\].amount\` | Refund amount. | | \`transactions.refunds\[\].status\` | Refund status. | ::: :::AccordionComponent{title="Update error handling on queries" pill="2"} Query errors were grouped by type of change. See the tables below for details. > NOTE > > \*Legacy\* errors correspond to the \*\*PUT flow\*\* query endpoint only. ### Errors that disappear The following errors exist in the Dynamic QR Model API PUT flow but \*\*have no equivalent\*\* in the Orders API. | HTTP | Dynamic QR Model API | Note | |---|---|---| | \`400\` | \`invalid\_collectorId\` | Removed in the Orders API. The query operates on \`order\_id\`. | | \`400\` | \`invalid\_externalPosId\` | Removed. The query operates on \`order\_id\`, not on the checkout. | | \`400\` | \`pos\_obtainment\_by\_external\_id\_error\` | Removed. The concept of POS as a query parameter disappears. | | \`400\` | \`pos\_deleted\_error\` | Removed in the Orders API. | | \`403\` | \`forbidden\` | Removed in the Orders API. | ### Renamed errors The following errors were renamed in the Orders API. | HTTP | Dynamic QR Model API | Orders API | Note | |---|---|---|---| | \`404\` | \`point\_of\_sale\_in\_store\_order\_not\_found\` and \`in\_store\_order\_not\_found\` | \`order\_not\_found\` | Both cases are unified into \`order\_not\_found\`. | ### Errors that change behavior The following errors exist in both APIs but with different behavior in the Orders API. | HTTP | Dynamic QR Model API | Orders API | Note | |---|---|---|---| | \`400\` → \`401\` | authentication via middleware | \`unauthorized\` | In the Dynamic QR Model API, it was \`400\`. In the Orders API, it is an explicit \`401\`. | ### Errors introduced by the Orders API The following errors have no equivalent in the Dynamic QR Model API. | HTTP | Error | Note | |---|---|---| | \`400\` | \`invalid\_path\_param\` | The \`order\_id\` has an invalid format. It must start with \`ORD\` followed by 26 characters. | | \`401\` | \`unauthorized\` | Invalid or expired Access Token. | | \`404\` | \`order\_not\_found\` | The \`order\_id\` does not correspond to any created order. | | \`500\` | Generic | Internal error. Check the response and retry the request. | ::: ## Update order cancellations The :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/cancel" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/cancel-order/post"} cancellation endpoint is available for \*\*both modes\*\* in the Orders API. In the Dynamic QR Model API, cancellation was available only for the \*\*PUT flow\*\* and canceled the order associated with the checkout via \`DELETE\`. The POST flow had no cancellation endpoint. The HTTP method changes from \`DELETE\` to \`POST\`, \`{user\_id}\` and \`{external\_pos\_id}\` are removed from the path, and the operation now identifies the order by \`order\_id\`. The response changed from empty (HTTP \`204\`) to the full order object with \`status: "canceled"\` (HTTP \`200\`). :::AccordionComponent{title="Adapt cancellation headers" pill="1"} | Header | Requirement | Description | |---|---|---| | \`X-Idempotency-Key\` | Required | Unique key per request. | The response includes the following fields of the canceled order. | Field | Description | |---|---| | \`id\` | Identifier of the canceled order. | | \`user\_id\` | Identifier of the user who created the order. | | \`type\` | Order type. For QR: always \`qr\`. | | \`external\_reference\` | External order reference. | | \`description\` | Product or service description. | | \`expiration\_time\` | Expiration time in ISO 8601 format. | | \`processing\_mode\` | Processing mode. For QR: always \`automatic\`. | | \`total\_amount\` | Total order amount. | | \`country\_code\` | Site/country identifier. | | \`marketplace\_fee\` | Marketplace fee. Exclusive to OAuth integrations. | | \`integration\_data.application\_id\` | Mercado Pago application identifier. | | \`integration\_data.platform\_id\` | Platform identifier, assigned by Mercado Pago. | | \`integration\_data.integrator\_id\` | Integrator identifier, assigned by Mercado Pago. | | \`integration\_data.sponsor.id\` | \`USER\_ID\` of the integrating system. | | \`status\` | Current order status. When canceled it is \`canceled\`. | | \`status\_detail\` | Status detail. When canceled it is \`canceled\`. | | \`currency\` | Currency identifier. The possible values are \`ARS\`, \`BRL\`, \`CLP\`, \`UYU\`. | | \`created\_date\` | Creation date. Format \`yyyy-MM-ddTHH:mm:ss.sssZ\`. | | \`last\_updated\_date\` | Last update date. Format \`yyyy-MM-ddTHH:mm:ss.sssZ\`. | | \`config.qr.external\_pos\_id\` | External identifier of the checkout associated with the order. | | \`config.qr.mode\` | Configured QR mode. | | \`transactions.payments\[\].id\` | Payment transaction identifier. | | \`transactions.payments\[\].amount\` | Payment amount. | | \`transactions.payments\[\].status\` | Payment status. When canceled it is \`canceled\`. | | \`transactions.payments\[\].status\_detail\` | Payment status detail. When canceled it is \`canceled\_by\_api\`. | | \`transactions.cash\_outs\[\].id\` | Cash withdrawal transaction identifier, generated by Mercado Pago. | | \`transactions.cash\_outs\[\].amount\` | Cash withdrawal amount. | | \`transactions.cash\_outs\[\].status\` | Cash withdrawal status. When canceled it is \`canceled\`. The possible values are \`created\`, \`canceled\`, \`processed\`, \`expired\`. | | \`transactions.cash\_outs\[\].status\_detail\` | Cash withdrawal status detail. | | \`items\[\].title\` | Item name. | | \`items\[\].unit\_price\` | Item unit price. | | \`items\[\].quantity\` | Item quantity. | | \`items\[\].unit\_measure\` | Item unit of measure. | | \`items\[\].external\_code\` | External item code. | | \`items\[\].external\_categories\[\].id\` | Item category identifier in the external system. | | \`discounts.payment\_methods\[\].new\_total\_amount\` | New total amount when a discount is applied. | | \`discounts.payment\_methods\[\].type\` | Payment method to which the discount applies. | ::: :::AccordionComponent{title="Update error handling on cancellations" pill="2"} Cancellation errors were grouped by type of change. See the tables below for details. > NOTE > > \*Legacy\* errors correspond to the \*\*PUT flow\*\* cancellation endpoint only. ### Errors that disappear The following errors exist in the Dynamic QR Model API PUT flow but \*\*have no equivalent\*\* in the Orders API. | HTTP | Dynamic QR Model API | Note | |---|---|---| | \`400\` | \`invalid\_collectorId\` | Removed in the Orders API. | | \`400\` | \`invalid\_externalPosId\` | Removed. \`{external\_pos\_id}\` is no longer a path parameter. | | \`400\` | \`pos\_deleted\_error\` | Removed in the Orders API. | | \`403\` | \`forbidden\` | Removed. Identity is validated internally by the Access Token. | ### Renamed errors The following errors were renamed in the Orders API. | HTTP | Dynamic QR Model API | Orders API | Note | |---|---|---|---| | \`400\` → \`404\` | \`pos\_obtainment\_by\_external\_id\_error\` and \`in\_store\_order\_delete\_error\` (order not found) | \`order\_not\_found\` | In the Dynamic QR Model API, it was \`400\`. In the Orders API, it is \`404\`. Cancellation now operates on the \`order\_id\`. | | \`409\` | \`in\_store\_order\_delete\_error\` (Cannot delete a locked order) | \`instore\_order\_locked\_error\` | Same concept. The error code name changes. | ### Errors that change behavior The following errors exist in both APIs but with different behavior in the Orders API. | HTTP | Dynamic QR Model API | Orders API | Note | |---|---|---|---| | \`400\` → \`401\` | authentication via middleware | \`unauthorized\` | In the Dynamic QR Model API, it was \`400\`. In the Orders API, it is an explicit \`401\`. | ### Errors that remain unchanged The following error has the same behavior in both APIs. | HTTP | Error | Note | |---|---|---| | \`500\` | Generic | Internal error. Check the response and retry the request. | ### Errors introduced by the Orders API The following errors have no equivalent in the Dynamic QR Model API. | HTTP | Error | Note | |---|---|---| | \`400\` | \`empty\_required\_header\` | \`X-Idempotency-Key\` is missing. | | \`400\` | \`invalid\_path\_param\` | The \`order\_id\` has an invalid format. | | \`401\` | \`unauthorized\` | Invalid or expired Access Token. | | \`404\` | \`order\_not\_found\` | The \`order\_id\` does not correspond to any created order. | | \`409\` | \`idempotency\_key\_already\_used\` | The \`X-Idempotency-Key\` was already used with a different request. | | \`409\` | \`order\_already\_canceled\` | The order is already canceled. Orders can only be canceled with \`status: created\`. | ::: ## Implement refunds with the dedicated endpoint The Orders API introduces the dedicated :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/refund" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/refund-order/post"} endpoint for refunds, which did not exist in the Dynamic QR Model API in either flow. Previously, it was necessary to receive the payment webhook, obtain the \`payment\_id\`, and execute the refund through the Payments API separately. Now, the refund is performed directly on the order, for both modes. :::::AccordionComponent{title="Implement the Orders API refund endpoint" pill="1"} > WARNING > > The Payments API \*\*must not be used\*\* in integrations with the Orders API. All operations, including refunds, must be performed through the Orders API. To refund the full order amount, send the request to the :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/refund" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/refund-order/post"} endpoint without a body. \`\`\`curl curl -X POST \\ 'https://api.mercadopago.com/v1/orders/{{ORDER\_ID}}/refund' \\ -H 'Authorization: Bearer {{ACCESS\_TOKEN}}' \\ -H 'X-Idempotency-Key: {{IDEMPOTENCY\_KEY}}' \`\`\` Refunds are accepted up to 360 days from the payment date. For orders with cash withdrawal (\*cash-out\* or \*extracash\*), the maximum period is 72 hours. > SUCCESS\_MESSAGE > > See all possible error codes for this operation at \[Refund errors\](https://www.mercadopago.com.ar/developers/en/docs/qr-code-ca/resources/refund-errors). ::::: :::AccordionComponent{title="Adapt refund response fields" pill="2"} The refund response varies depending on the type of operation performed. | Field | Type | Description | |---|---|---| | \`id\` | \`string\` | Identifier of the refunded order. | | \`status\` | \`string\` | \`refunded\` for a full refund. | | \`status\_detail\` | \`string\` | \`refunded\` for a full refund. | | \`transactions.refunds\[\].id\` | \`string\` | Refund identifier, generated by Mercado Pago. | | \`transactions.refunds\[\].transaction\_id\` | \`string\` | Identifier of the payment transaction being refunded. | | \`transactions.refunds\[\].amount\` | \`string\` | Refund amount. If the payment was made with a discount, reflects that value. | | \`transactions.refunds\[\].status\` | \`string\` | \`processing\` when the refund was requested and is being processed. | | \`transactions.refunds\[\].reference\_id\` | \`string\` | Identifier that associates the payment with its corresponding refund. | ::: ## Validate the migration After applying the changes, verify that the integration operates correctly across all flows before going to production. :::CheckboxComponent{label="X-Idempotency-Key header configured" defaultChecked="false"} The \`X-Idempotency-Key\` \*header\* must be present in all creation, cancellation, and refund operations. ::: :::CheckboxComponent{label="Order creation (dynamic mode) validated" defaultChecked="false"} Orders successfully created via the :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"} endpoint with \`config.qr.mode: dynamic\` (equivalent to the POST flow of the legacy API). ::: :::CheckboxComponent{label="Order creation (hybrid mode) validated" defaultChecked="false"} Orders successfully created via the :TagComponent{tag="API" text="POST /v1/orders" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/create-order/post"} endpoint with \`config.qr.mode: hybrid\` (equivalent to the PUT flow of the legacy API). ::: :::CheckboxComponent{label="id field captured" defaultChecked="false"} The \`id\` field was captured from the creation \*response\* for use in subsequent queries, cancellations, and refunds. ::: :::CheckboxComponent{label="Order query validated" defaultChecked="false"} Orders successfully queried via the :TagComponent{tag="API" text="GET /v1/orders/{order\_id}" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/get-order/get"} endpoint in both modes. ::: :::CheckboxComponent{label="Status webhook configured" defaultChecked="false"} Order status monitored via webhook with the \*\*"Order (Mercado Pago)"\*\* topic and the new values: \`created\`, \`processed\`, \`canceled\`, \`refunded\`, and \`expired\`. ::: :::CheckboxComponent{label="Order cancellation validated" defaultChecked="false"} Orders successfully canceled via the :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/cancel" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/cancel-order/post"} endpoint in both modes. ::: :::CheckboxComponent{label="Refunds validated" defaultChecked="false"} Refunds executed via the dedicated :TagComponent{tag="API" text="POST /v1/orders/{order\_id}/refund" href="/developers/en/reference/in-person-payments/qr-code-ca/orders/refund-order/post"} endpoint. ::: See the documentation to learn how to \[test the integration\](https://www.mercadopago.com.ar/developers/en/docs/qr-code/test-integration).