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 POST /v1/ordersAPI, 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.
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 POST /v1/ordersAPI and extends query and cancellation resources to both modes.
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.
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".
closed + payment with status_detail: "partially_refunded"
refunded
Partial refund. In the Dynamic QR Model API, it was necessary to inspect the status_detail of the payment within the merchant_order. In the Orders API, the status_detail indicates partially_refunded.
—
—
canceled
New status with no equivalent in the Dynamic QR Model API.
—
—
expired
New status with no equivalent in the Dynamic QR Model API.
When migrating to the Orders API, configure webhook notifications in your application by selecting the "Order (Mercado Pago)" topic. Do not use the payments or merchant_orders topics to monitor the state of orders created by the new API, as they are not compatible with the unified status model. For more information, see the notifications documentation.
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.
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.
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 POST /v1/ordersAPI, 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.
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.
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.
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. Accepts two decimal places (e.g., "15.00") or none.
Changes from number to decimal string.
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.
discount.total_amount
discounts.payment_methods[].new_total_amount
New total amount when a discount is applied. Accepts two decimal places (e.g., "15.00") or none. Cannot be used together with items[].external_categories.
Restructured within discounts.payment_methods[]. Changes from number to decimal string.
discount.details[]
discounts.payment_methods[].type
Payment method to which the discount applies. Values: debit_card, credit_card, account_money, prepaid_card. Up to 4 methods.
Restructured. The details array now indicates the payment method type.
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.
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.
integration_data.platform_id
Platform identifier, assigned by Mercado Pago.
integration_data.integrator_id
Integrator identifier, assigned by Mercado Pago. Must have the dev_ prefix.
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. On creation: created.
status_detail
Current order status detail. The possible values are created, canceled, accredited, refunded, expired.
currency
Order currency. Values: 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. On creation: created.
transactions.payments[].status_detail
Payment status detail. On creation: 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.
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.
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
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.
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 GET /v1/orders/{order_id}API 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}}'
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. Values: 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. Values: 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.
Payment method with which the discount was applied. Present only if a discount was applied.
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.
Query errors were grouped by type of change. See the tables below for details.
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 POST /v1/orders/{order_id}/cancelAPI 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).
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. On cancellation: canceled.
status_detail
Status detail. On cancellation: canceled.
currency
Currency identifier. Values: 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. On cancellation: canceled.
transactions.payments[].status_detail
Payment status detail. On cancellation: canceled_by_api.
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.
Cancellation errors were grouped by type of change. See the tables below for details.
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 POST /v1/orders/{order_id}/refundAPI 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.
The Payments API must not be used in integrations with the Orders API. All operations, including refunds, must be performed through the Orders API.
Choose between a full or partial refund based on the amount to be returned.