How to migrate from the Instore Orders API to the Orders API
The Orders API unifies QR Code payment processing on Mercado Pago, providing standardized endpoints, a consolidated status model, and new native capabilities that did not exist in the Instore Orders API. In addition, all new Mercado Pago features will be developed on the Orders API.
Migrating from the Instore Orders API to the Orders API involves updating endpoints and request fields, consolidating the notifications model, and leveraging new native resources that did not exist before: dedicated query endpoints by order_id, cancellation, and refund. The migration does not change the business flow: the customer continues scanning the QR Code with the Mercado Pago app and confirming the payment.
Read on to learn how to complete this migration.
Map the endpoint changes
Before starting the migration steps, see the table below for an overview of all endpoint changes. In the Instore Orders API, each operation used a URL with the user and point-of-sale identifier in the path. The Orders API consolidates these operations into standardized endpoints and introduces resources that did not exist before.
In the Instore Orders API, there were two valid endpoints for creation and cancellation: /mpmobile/instore/qr/{user_id}/{external_id}, using the external point-of-sale identifier, and /mpmobile/instore/qr/{pos_id}, using the internal identifier. Both migrate to the same endpoint in the Orders API: POST /v1/ordersAPI, where the point of sale is identified by the config.qr.external_pos_id field in the body.
Update the status and notifications model
One of the most significant changes between the Instore Orders API and the Orders API is the status model. In the Instore Orders API, the state of a transaction was determined by monitoring notifications from two independent topics , payments and merchant_orders, with distinct fields and values for each. The Orders API unifies this behavior into a single status field in 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 Instore Orders API, the merchant_order is created when the order is generated, so merchant_orders topic notifications are available from the start of the flow. The payment object, on the other hand, is only created when a payment attempt exists, whether approved or rejected. Integrations monitoring only the payments topic received no notification until the user started 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 Instore Orders API, opened indicated that the order did not yet have an approved payment. 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 Instore Orders API, a rejected payment left the merchant_order in opened awaiting a new attempt. In the Orders API, rejected payments are not exposed and 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 Instore Orders API, it was necessary to inspect the status_detail of the payment inside the merchant_order. In the Orders API, the state is the same as a full refund, but the status_detail indicates partially_refunded.
—
—
canceled
New status with no equivalent in the Instore Orders API.
—
—
expired
New status with no equivalent in the Instore Orders 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 status 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 the headers
The Orders API introduces two mandatory header changes that affect all endpoints. The Access Token must be sent via the Authorization header. Sending it as a query parameter is not allowed in the Orders API. Apply the following changes before testing any other resource.
The X-Ttl-Store-Preference header was used in the Instore Orders API to define the order validity period in seconds. In the Orders API, this functionality was replaced by the expiration_time field in the request body, in ISO 8601 format, for example PT5M for 5 minutes. The default value is PT10M. Values greater than 10 minutes are ignored and the order expires in 10 minutes. Remove this header from all requests and migrate the validity control to the expiration_time field.
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 unique UUID v4 or 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 creation endpoints POST /mpmobile/instore/qr/{user_id}/{external_id}API and POST /mpmobile/instore/qr/{pos_id} migrate to POST /v1/ordersAPI. Beyond the URL change, the request structure was significantly reorganized: the user and point-of-sale identifiers move from the path to the body, new required fields were introduced, and the Orders API offers three QR Code modes configurable via config.qr.mode. Follow the steps below to adapt the request, response, and error handling.
In the Orders API, the user identity is obtained directly from the Access Token. The {user_id} is no longer a path parameter. The point of sale is now identified by the config.qr.external_pos_id field in the body. The type field (with value "qr") and external_reference become required. The table below shows the fields that existed in both APIs and were updated during the migration.
Instore Orders API
Orders API
Description
Change
{user_id} (path param)
Does not exist
Mercado Pago user identifier. In the Instore Orders API, it was sent in the path. In the Orders API, identity is obtained directly from the Access Token.
No longer a path parameter.
{external_id} (path param)
config.qr.external_pos_id
External point-of-sale identifier, defined by the integrator. In the Instore Orders API, it was sent in the path. In the Orders API, it is sent in the body inside config.qr.
Moves from path parameter to body.
external_reference
external_reference
Reference that can synchronize with the integrator's sales system. In the Orders API, it is required, with a maximum of 64 characters, only letters, numbers, - and _. Cannot contain PII data.
Becomes required.
notification_url
Does not exist
URL to receive payment or merchant_order notifications. In the Orders API, notifications are configured as a webhook in your application within Your integrations by selecting the "Order (Mercado Pago)" topic. For more information, see the notifications documentation.
Removed from the body.
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[].id
items[].external_code
Item identifier in the external system.
Renamed to items[].external_code.
items[].title
items[].title
Item name. In the Orders API, max. 150 characters.
Becomes required.
items[].currency_id
Does not exist
Item currency identifier in ISO 4217 format. In the Orders API, the currency is determined from the seller's account.
Removed.
items[].unit_price
items[].unit_price
Item unit price. In the Instore Orders 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 and becomes required.
items[].quantity
items[].quantity
Item quantity. In the Instore Orders API, it was a number. In the Orders API, it is an integer.
Changes from number to integer and becomes required.
items[].description
description
Item description or order reason. In the Orders API, the description exists only at the root level of the order, not inside items. Max. 150 characters. Must not contain PII data.
Moves from item level to order root level.
items[].picture_url
Does not exist
Item image URL.
Removed. Has no equivalent in the Orders API.
marketplace_fee
marketplace_fee
Marketplace fee amount. In the Instore Orders API, it was a number. In the Orders API, it is a decimal string. Accepts two decimal places (e.g.: "15.00") or none. Exclusive to OAuth integrations.
Changes from number to decimal string.
X-Ttl-Store-Preference (header)
expiration_time
Order validity period. In the Instore Orders API, it was defined in seconds via header. In the Orders API, it is defined in ISO 8601 format (e.g.: PT5M for 5 minutes) in the body. Minimum: 30 seconds. Maximum: 3600 hours. For static mode, the default value is PT10M. Values greater than 10 minutes are ignored and the order expires in 10 minutes.
Changes from a header in seconds to a body field in ISO 8601 format.
The Orders API also introduces the following fields with no equivalent in the Instore Orders API.
Field
Description
type
Order type. For QR Code payments, the only possible value is "qr". Required.
total_amount
Total order amount. Represents the sum of transactions. Accepts two decimal places (e.g.: "15.00") or none.
config.qr.mode
QR Code mode. Values: static (static QR Code associated with the point of sale, equivalent to the behavior of the Instore Orders API), dynamic (unique QR Code per transaction, returned in type_response.qr_data), hybrid (both modes in parallel). Default value: static.
transactions.payments[].amount
Payment amount inside the transactions node. Accepts two decimal places (e.g.: "15.00") or none. Only 1 payment transaction per order.
items[].unit_measure
Item unit of measure. Max. 10 characters. Required.
items[].external_categories[].id
Item category identifier in the external system. Enables category-based discounts. Cannot be used together with discounts.
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. Values: debit_card, credit_card, account_money, prepaid_card. Up to 4 methods can be defined.
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 creation response fields that existed in both APIs and were updated during the migration.
Instore Orders API
Orders API
Description
Change
id
id
Identifier of the created order. In the Instore Orders API, the format was {collector_id}-{uuid}. In the Orders API, it is a proprietary alphanumeric ID (e.g.: ORD00001111222233334444555566).
ID format changes from {collector_id}-{uuid} to alphanumeric.
collector_id
user_id
Seller identifier. In the Instore Orders API, it was an integer. In the Orders API, it is a string.
Renamed from collector_id to user_id. Changes from integer to string.
collector
Does not exist
Seller data.
Removed. Seller data is not returned in the order.
total_amount
total_amount
Total order amount. In the Instore Orders API, it was a number. In the Orders API, it is a decimal string.
Changes from number to decimal string.
amount
Does not exist
Order amount. Was always equal to total_amount.
Removed. In the Orders API, the payment amount is in transactions.payments[].amount.
external_reference
external_reference
External order reference.
No changes.
notification_url
Does not exist
URL configured to receive notifications.
Removed from the response. Configure webhook notifications in your application within Your integrations. For more information, see the notifications documentation.
expiration_date_to
Does not exist
Order expiration date and time in absolute ISO 8601 format.
Removed. In the Orders API, expiration is configured as a relative duration in expiration_time.
expires
Does not exist
Indicates whether the order has an expiration date.
Removed. In the Orders API, the order always expires.
marketplace_fee
marketplace_fee
Marketplace fee. In the Instore Orders 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 Instore Orders API, it was an integer. In the Orders API, it is a string.
Moves to integration_data.sponsor. Changes from integer to string.
site_id
country_code
Site/country identifier. In the Instore Orders API, the format was lowercase (e.g.: "mla"). In the Orders API, it is the country code in uppercase (e.g.: "AR").
Renamed from site_id to country_code. Format changes from lowercase to uppercase.
items[].description
description
Item description.
Moves from item level to order root level.
items[].id
items[].external_code
Item identifier in the external system.
Renamed to items[].external_code.
In the migration to the Orders API, the following fields were removed and have no direct equivalent.
Field
Note
operation_type
Removed. Has no equivalent in the Orders API.
payment_methods
Removed. Payment method exclusions are not returned at the order level.
payment_methods.included_payment_types
Removed along with the payment_methods node.
marketplace
Removed. Has no equivalent in the Orders API.
back_urls
Removed. Has no equivalent in the Orders API.
payer
Removed. Payer data is not returned in the creation response.
client_id
Removed. Redundant with user_id.
processing_modes
Removed. Replaced by the processing_mode field (string) in the Orders API.
internal_metadata
Removed. Internal-use field with no equivalent in the Orders API.
items[].currency_id
Removed. The currency is exposed at the order level in the currency field.
items[].picture_url
Removed. Has no equivalent in the Orders API.
The Orders API also introduces the following fields in the creation response.
Field
Description
type
Order type. For QR Code: always qr.
description
Product or service description, order reason (in Legacy it was items[].description).
expiration_time
Order expiration time in ISO 8601 format.
processing_mode
Processing mode. For QR Code: always automatic.
status
Current order status. On creation: created.
status_detail
Current order status detail. The possible values are created, canceled, accredited, refunded, expired.
currency
Currency identifier. Values: ARS, BRL, CLP, UYU.
created_date
Order creation date. Format yyyy-MM-ddTHH:mm:ss.sssZ.
last_updated_date
Order last update date. Format yyyy-MM-ddTHH:mm:ss.sssZ.
config.qr.external_pos_id
External point-of-sale identifier associated with the order.
config.qr.mode
Configured QR Code mode. Values: static, dynamic, 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.
type_response.qr_data
QR Code data string that must be converted into a QR Code for the payment to be made. Present only when config.qr.mode is dynamic or hybrid.
integration_data.application_id
Mercado Pago application identifier.
items[].title
Item name.
items[].unit_price
Item unit price.
items[].quantity
Item quantity.
items[].unit_measure
Item unit of measure.
items[].external_categories[].id
Item category identifier in the external system.
discounts.payment_methods[].new_total_amount
New total order amount when a discount is applied.
discounts.payment_methods[].type
Payment method to which the discount applies.
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. Response messages are also more descriptive, making it easier to identify and resolve issues. See the tables below for more details.
Errors that disappear
The following errors exist in the Instore Orders API but have no equivalent in the Orders API.
HTTP
Instore Orders API
Note
400
invalid_collector_id
Removed. Identity is obtained directly from the Access Token.
Renamed errors
The following errors were renamed in the Orders API.
HTTP
Instore Orders API
Orders API
Note
400
invalid_sponsor_id
sponsor_id_not_valid
The field migrates to integration_data.sponsor.id.
404
pos_obtainment_error
pos_not_found
The external_pos_id moves from path parameter to body.
Errors that change behavior
The following errors exist in both APIs but with different behavior in the Orders API.
HTTP
Instore Orders API
Orders API
Note
400
invalid_items
property_value
property_value consolidates field-level validation errors. The field with the error is indicated in the response detail.
400
json_syntax_error
property_type
Closest equivalent. In the Instore Orders API, it also covered malformed JSON in general.
400
invalid_access_token
unauthorized (HTTP 401)
In the Instore Orders API, it was 400. In the Orders API, it is 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 Instore Orders API.
HTTP
Error
Note
400
empty_required_header
X-Idempotency-Key missing.
400
unsupported_site
Attempt to create the order in an unsupported country.
400
unsupported_properties
Unsupported property in the body. The field with the error is indicated in the response detail.
400
bad_request
Unsupported or invalid fields in general.
400
marketplace_not_valid
The Access Token was not obtained via OAuth and a valid marketplace cannot be identified.
401
unauthorized
Invalid or expired token. The equivalent in the Instore Orders API was invalid_access_token with code 400.
404
marketplace_fee_not_allowed
Sending marketplace_fee is not allowed because 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.
Implement order queries
The query endpoint GET /v1/orders/{order_id}API is a capability exclusive to the Orders API. The Instore Orders API did not have a dedicated endpoint to query the status of a transaction. Previously, the status was obtained exclusively via webhook notifications from the payments and merchant_orders topics. The Orders API allows querying the complete order status at any time, 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 GET response includes all fields from the creation response, plus the following additional fields available after payment processing.
Field
Description
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.
transactions.payments[].refunded_amount
Refunded amount. Present only when a refund occurred.
transactions.payments[].paid_amount
Total amount paid. If the payment was made with a discount, reflects that amount.
transactions.payments[].status
Current payment status. Values: created, canceled, processed, refunded, expired.
Since this is a new endpoint in the Orders API, there is no equivalent in the Instore Orders API. The errors documented below are exclusive to the new API.
HTTP
Error
Note
400
invalid_path_param
The order_id sent to query the order has an invalid format. It must start with the ORD prefix 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.
In the Instore Orders API, cancellation deleted the active order associated with the point of sale, an operation on the point of sale rather than on the individual transaction. In the Orders API, cancellation operates directly on the order via order_id, without depending on the point of sale in the path. The response changed from empty (HTTP 204) to the complete order object with status: "canceled" (HTTP 200).
The header required for this operation is:
Header
Required
Description
X-Idempotency-Key
Required
Unique key per request.
The response returns the complete order object with status: "canceled". In the Instore Orders API, cancellation returned HTTP 204 with no body. The table below lists the fields returned in the cancellation response.
Field
Description
id
Identifier of the canceled order, generated by Mercado Pago.
user_id
Identifier of the Mercado Pago user who created the order.
type
Order type. For QR Code: always qr.
external_reference
External order reference, assigned at creation.
description
Product or service description, order reason.
expiration_time
Order expiration time in ISO 8601 format.
processing_mode
Processing mode. For QR Code: always automatic.
total_amount
Total order amount.
country_code
Mercado Pago application site/country identifier.
marketplace_fee
Marketplace fee. Exclusive to OAuth integrations.
integration_data.application_id
Identifier of the Mercado Pago application that created the order.
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
Current order status detail. On cancellation: canceled.
Order creation date. Format yyyy-MM-ddTHH:mm:ss.sssZ.
last_updated_date
Order last update date. Format yyyy-MM-ddTHH:mm:ss.sssZ.
config.qr.external_pos_id
External point-of-sale identifier associated with the order.
config.qr.mode
Configured QR Code mode. Values: static, dynamic, hybrid.
transactions.payments[].id
Payment transaction identifier, generated by Mercado Pago.
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.
transactions.payments[].reference_id
Payment identifier associated with the order.
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 (e.g.: EAN).
In cancellation, errors are grouped by type of change. See the tables below for more details.
Errors that disappear
The following errors exist in the Instore Orders API but have no equivalent in the Orders API.
HTTP
Instore Orders API
Note
400
invalid_collector_id
Removed in the Orders API.
401
unauthorized (authorization value not present)
Removed. Occurred when sending an alphanumeric {user_id}. Identity is obtained directly from the Access Token.
403
forbidden
Removed. Occurred when the {user_id} did not belong to the Access Token owner.
Renamed errors
The following errors were renamed in the Orders API.
HTTP
Instore Orders API
Orders API
Note
400 → 404
pos_obtainment_by_external_id_error
order_not_found
In the Instore Orders API, it was 400. In the Orders API, it is 404. Cancellation now operates on the order_id.
Errors that change behavior
The following errors exist in both APIs but with different behavior in the Orders API.
HTTP
Instore Orders API
Orders API
Note
400 → 401
invalid_access_token
unauthorized
In the Instore Orders API, it was 400. In the Orders API, it is 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 Instore Orders API.
HTTP
Error
Note
400
empty_required_header
X-Idempotency-Key missing.
400
invalid_path_param
The order_id has an invalid format. It must start with ORD followed by 26 characters.
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.
409
instore_order_locked_error
The order cannot be canceled while the payment is in progress.
Implement refunds with the dedicated endpoint
The Orders API introduces the dedicated endpoint POST /v1/orders/{order_id}/refundAPI for refunds, which did not exist in the Instore Orders API. 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.
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 return.