How to migrate from the Instore Orders V2 API to the Orders API
The Orders API unifies payment processing with QR Code on Mercado Pago, offering standardized endpoints, a consolidated status model, and new native features that did not exist in the Instore Orders V2 API. In addition, all new Mercado Pago features will be developed on the Orders API.
Migrating from the Instore Orders V2 API to the Orders API involves updating endpoints and request fields, consolidating the notifications model, and taking advantage of new native features, such as dedicated cancel and refund endpoints. The main change in creation is that the Orders API returns 201 Created with the complete order object, instead of 204 No Content without a body. The migration does not involve changes to the business flow: the customer continues scanning the QR Code with the Mercado Pago app and confirming the payment.
See below how to carry out this migration in full.
Map endpoint changes
Before starting the migration steps, refer to the table below for an overview of all endpoint changes. In the Instore Orders V2 API, each operation used a URL with the user, store, and POS identifier in the path. The Orders API consolidates these operations into standardized endpoints identified by the order_id.
| Operation | Instore Orders V2 API | Orders API |
| Create order | PUT /instore/qr/seller/collectors/{user_id}/stores/{external_store_id}/pos/{external_pos_id}/ordersAPI | POST /v1/ordersAPI |
| Get order | GET /instore/qr/seller/collectors/{user_id}/pos/{external_pos_id}/ordersAPI | GET /v1/orders/{order_id}API |
| Cancel order | DELETE /instore/qr/seller/collectors/{user_id}/pos/{external_pos_id}/ordersAPI | POST /v1/orders/{order_id}/cancelAPI |
| Refund order | Via Payments API (no dedicated endpoint in the Instore Orders V2 API) | POST /v1/orders/{order_id}/refundAPI |
Update the status and notifications model
One of the most significant changes between the Instore Orders V2 API and the Orders API is the status model. In the Instore Orders V2 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 in 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 Instore Orders V2 API, the merchant_order is created when the order is generated, so notifications from the merchant_orders topic are available from the beginning of the flow. The payment object, on the other hand, is only created when there is a payment attempt, whether approved or rejected. Anyone monitoring only the payments topic received no notifications 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 with rejected payment) | created | In the Instore Orders V2 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 V2 API, a rejected payment left the merchant_order in opened awaiting 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". |
refunded (partial, status_detail: "partially_refunded") | closed + payment with status_detail: "partially_refunded" | refunded | Partial refund. In the Instore Orders V2 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 V2 API. |
| — | — | expired | New status with no equivalent in the Instore Orders V2 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, subscribing to the "Order (Mercado Pago)" (orders) topic, before going to production. 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. Sending it as a query parameter is not allowed in the Orders API. Apply the change below 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 unique random string per request. GET operations do not require this header.
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 endpoint changes from PUT /instore/qr/seller/collectors/{user_id}/stores/{external_store_id}/pos/{external_pos_id}/ordersAPI to POST /v1/ordersAPI. In addition to the method change from PUT to POST and the URL change, the response changes from HTTP 204 No Content without a body to 201 Created with the complete order object. The {user_id} and the store and POS path parameters are removed. Identity comes from the Access Token and the POS is identified by the config.qr.external_pos_id field in the body. Follow the steps below to adapt the request, the response, and error handling.
204 No Content without a body, making it impossible to obtain the order id for subsequent operations. The Orders API returns 201 Created with the complete order object. The id field from the response is required for subsequent queries, cancellations, and refunds.In the Orders API, the user identity is obtained directly from the Access Token. The {external_store_id} and {user_id} are no longer path parameters. The POS 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 changed during migration.
| Instore Orders V2 API | Orders API | Description | Change |
{user_id} (path param) | Does not exist | Mercado Pago user identifier. In the Orders API, identity is obtained directly from the Access Token. | No longer a path parameter. |
{external_store_id} (path param) | Does not exist | External store identifier. | No longer a path parameter. The store is identified indirectly through the POS. |
{external_pos_id} (path param) | config.qr.external_pos_id | External POS identifier. In the Instore Orders V2 API, it was sent in the path. In the Orders API, it is sent in the body inside config.qr. | No longer a path parameter; moves to the body. |
external_reference | external_reference | Reference that can sync 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. |
title | Does not exist | Purchase title. In the Orders API, description is sufficient. | Removed. |
description | description | Purchase description. In the Instore Orders V2 API, it existed at both the root level and inside items[]. In the Orders API, it only exists at the root level. Max. 150 characters. Must not contain PII data. | Remains at root level. The version inside items[] was removed. |
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 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. |
expiration_date | expiration_time | Order validity. In the Instore Orders V2 API, it was an absolute date (e.g., 2020-08-22T16:34:56.559-04:00). In the Orders API, it is a relative duration in ISO 8601 format (e.g., PT5M). The minimum value is PT30S and the maximum value is PT3H. In all modes (Dynamic QR, Static QR, and Hybrid QR), the default value is 15 minutes and the value sent is always respected. | Changes from absolute date to relative duration in ISO 8601 format. |
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. Has no equivalent in the Orders API. |
items[].title | items[].title | Item name. In the Orders API, max. 150 characters. | Becomes required. |
items[].unit_price | items[].unit_price | Item unit price. In the Instore Orders V2 API, it was a number. In the Orders API, it is a decimal string. Does not accept decimal places (e.g., "15"). | Changes from number to decimal string and becomes required. |
items[].quantity | items[].quantity | Item quantity. In the Instore Orders V2 API, it was a number. In the Orders API, it is an integer. | Changes from number to integer and becomes required. |
items[].unit_measure | items[].unit_measure | Item unit of measure. Max. 10 characters. | Becomes required. |
items[].total_amount | Does not exist | Total items amount (unit price × quantity). | Removed. Has no equivalent in the Orders API. |
items[].external_categories[].id | items[].external_categories[].id | Item category identifier in the external system. Enables category-based discounts. Cannot be used together with discounts. | No change. |
marketplace_fee | marketplace_fee | Marketplace fee amount. In the Instore Orders V2 API, it was a number. In the Orders API, it is a decimal string. Does not accept decimal places (e.g., "15"). Exclusive to OAuth integrations. | Changes from number to decimal string. |
total_amount | transactions.payments[].amount | Total transaction amount. In the Instore Orders V2 API, it was the total_amount field at root level. In the Orders API, the payment amount is sent inside transactions.payments[]. Does not accept decimal places (e.g., "15"). | Moves to the transactions.payments node. Changes from number to decimal string. |
The Orders API also introduces the following fields with no equivalent in the Instore Orders V2 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 all transactions. Does not accept decimal places (e.g., "15"). |
config.qr.mode | QR Code mode. Values: static (static QR Code associated with the POS, equivalent to the Instore Orders V2 API behavior), dynamic (unique QR Code per transaction, returned in type_response.qr_data), hybrid (both modes in parallel). Default value: static. |
items[].external_code | Code that identifies the item in the external system (e.g., EAN). Max. 30 characters. |
discounts.payment_methods[].new_total_amount | New total order amount when a discount is applied. Does not accept decimal places (e.g., "15"). 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 Instore Orders V2 API returned HTTP 204 No Content without a body on creation. The Orders API returns 201 Created with the complete order object. The table below shows the fields of the new API's response.
| Field | Description |
id | Identifier of the created order (e.g., ORD00001111222233334444555566). In the Instore Orders V2 API, there was no body in the creation response. Required for subsequent queries, cancellations, and refunds. |
user_id | Identifier of the Mercado Pago user who created the order. Renamed from collector_id (integer) to user_id (string). |
total_amount | Total order amount. Changes from number to decimal string. |
external_reference | External reference of the order. |
description | Product or service description. Remains at root level. The version inside items[] was removed. |
marketplace_fee | Marketplace fee. Changes from number to decimal string. |
integration_data.sponsor.id | USER_ID of the integrating system. Moved from sponsor_id (integer) to integration_data.sponsor (string). |
country_code | Site/country identifier (e.g., "AR"). Renamed from site_id. Format changes from lowercase to uppercase. |
type | Order type. For QR Code: always qr. |
expiration_time | Expiration duration in ISO 8601 format. Replaces expiration_date (absolute date) with a relative duration. |
processing_mode | Processing mode. For QR Code: always automatic. Replaces the processing_modes array. |
status | Current order status. On creation: created. In the Instore Orders V2 API, status was only available via webhooks. |
status_detail | Current order status detail. The possible values are created, canceled, accredited, refunded, expired. |
currency | Currency identifier. Values: ARS, BRL, CLP, UYU. In the Instore Orders V2 API, currency came from items[].currency_id. |
created_date | Creation date. Format yyyy-MM-ddTHH:mm:ss.sssZ. |
last_updated_date | Last updated date. Format yyyy-MM-ddTHH:mm:ss.sssZ. |
config.qr.external_pos_id | External identifier of the POS associated with the order. In the Instore Orders V2 API, it was an input path parameter and was not returned. |
config.qr.mode | Configured QR Code mode. Values: static, dynamic, hybrid. |
transactions.payments[].id | Payment transaction identifier. In the Instore Orders V2 API, the payment ID only arrived via webhook from the payments topic. |
transactions.payments[].amount | Payment amount. In the Instore Orders V2 API, the amount was in total_amount at the order level. |
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 string for conversion into a QR Code. Present only when config.qr.mode is dynamic or hybrid. |
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. |
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). |
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. In addition, response messages are more descriptive, making it easier to identify and resolve issues. See the tables below for more details.
Errors that change behavior
The following errors exist in both APIs but behave differently in the Orders API.
| HTTP | Instore Orders V2 API | Orders API | Note |
400 | Multiple: invalid_collectorId, invalid_externalStoreId, invalid_externalPosId, invalid_total_amount, invalid_items.* and others | property_value | property_value consolidates field-level validation errors. The field with the error is indicated in the response detail. |
400 | error_creating_seller_qr_order (malformed JSON) | property_type | The Orders API distinguishes malformed JSON with its own code instead of the generic error_creating_seller_qr_order. |
400 | error_creating_seller_qr_order (general errors) | bad_request | The generic error_creating_seller_qr_order code covered multiple errors. The Orders API uses bad_request for cases not covered by other codes. |
400 | error_invalid_sponsor_id | sponsor_id_not_valid | The field migrates to integration_data.sponsor.id. |
400 → 401 | authentication via middleware | unauthorized | In the Instore Orders V2 API, failed authentication returned 400. In the Orders API, it is an explicit 401. |
404 | pos_obtainment_error | pos_not_found | The external_pos_id is no longer a path parameter and moves to the body. |
Errors that remain unchanged
The following error has the same behavior in both APIs.
| HTTP | Error | Note |
500 | Generic | Internal error. Verify the response and retry the request. |
Errors introduced by the Orders API
The following errors have no equivalent in the Instore Orders V2 API.
| HTTP | Error | Note |
400 | marketplace_not_valid | The Access Token was not obtained via OAuth and it is not possible to identify a valid marketplace. |
400 | empty_required_header | X-Idempotency-Key is missing. |
400 | unsupported_site | Attempt to create an order in an unsupported country. |
400 | unsupported_properties | Unsupported property in the body. The field with the error is indicated in the response detail. |
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 has already been used with a different request in the last 24 hours. |
Update order queries
The query endpoint changes from GET /instore/qr/seller/collectors/{user_id}/pos/{external_pos_id}/ordersAPI to GET /v1/orders/{order_id}API. In the Instore Orders V2 API, queries were made by POS (external_pos_id), returning the active order at that POS without exposing the transaction state. The Orders API replaces the POS-based query endpoint with one that operates directly by order_id and returns the complete transaction state: status, payments, refunds, and payment method data. In the Instore Orders V2 API, this information only arrived via notification.
curl
curl -X GET \ 'https://api.mercadopago.com/v1/orders/{{ORDER_ID}}' \ -H 'Authorization: Bearer {{ACCESS_TOKEN}}'
The table below shows the query response fields that existed in both APIs and changed during migration.
| Instore Orders V2 API | Orders API | Description | Change |
| Does not exist | id | Order identifier. In the Instore Orders V2 API, the query endpoint did not return the order ID. | New field. |
| Does not exist | user_id | Identifier of the user who created the order. In the Instore Orders V2 API, it was a path parameter but was not returned in the response. | New field. |
| Does not exist | type | Order type. For QR Code: always qr. | New field. |
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. Configure webhook notifications in your application. For more information, see the notifications documentation. |
expiration_date | expiration_time | Order validity. In the Instore Orders V2 API, it was an absolute date. In the Orders API, it is a relative duration in ISO 8601 format. | Changes from absolute date to relative duration. |
| Does not exist | processing_mode | Processing mode. For QR Code: always automatic. | New field. |
total_amount | total_amount | Total order amount. In the Instore Orders V2 API, it was a number. In the Orders API, it is a decimal string. | Changes from number to decimal string. |
| Does not exist | country_code | Site/country identifier. | New field. |
marketplace_fee | marketplace_fee | Marketplace fee. In the Instore Orders V2 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 V2 API, it was an integer. In the Orders API, it is a string. | Moves to integration_data.sponsor. Changes from integer to string. |
items[].title | items[].title | Item name. | No change. |
items[].unit_price | items[].unit_price | Item unit price. In the Instore Orders V2 API, it was a number. In the Orders API, it is a decimal string. | Changes from number to decimal string. |
items[].quantity | items[].quantity | Item quantity. | No change. |
items[].unit_measure | items[].unit_measure | Item unit of measure. | No change. |
items[].external_categories[].id | items[].external_categories[].id | Item category identifier in the external system. | No change. |
When migrating to the Orders API, the following items[] fields were removed and have no direct equivalent.
| Field | Note |
items[].description | Removed. Description is managed only at the order root level. |
items[].sku_number | items[].external_code |
items[].category | Removed. Has no equivalent in the Orders API. |
items[].currency_id | Removed. Currency is exposed at the order level in the currency field. |
items[].total_amount | Removed. Has no equivalent in the Orders API. |
items[].discount | Removed. The discount structure moved to discounts.payment_methods[]. |
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 Instore Orders V2 API, status was only visible via payments and merchant_orders notifications. |
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 | Creation date. Format yyyy-MM-ddTHH:mm:ss.sssZ. |
last_updated_date | Last updated date. Format yyyy-MM-ddTHH:mm:ss.sssZ. |
config.qr.external_pos_id | External identifier of the POS associated with the order. |
config.qr.mode | Configured QR Code mode. |
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. |
transactions.payments[].id | Payment transaction identifier. In the Instore Orders V2 API, the payment ID only arrived via webhook from the payments topic. |
transactions.payments[].amount | Payment amount. |
transactions.payments[].refunded_amount | Refunded amount. Present only when a refund occurred. |
transactions.payments[].paid_amount | Total paid amount. |
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.refunds[].id | Refund identifier. |
transactions.refunds[].transaction_id | Identifier of the refunded payment transaction. |
transactions.refunds[].reference_id | Identifier that associates the transaction with the refund. |
transactions.refunds[].amount | Refund amount. |
transactions.refunds[].status | Refund status. |
items[].external_code | External item code (e.g., EAN). |
In queries, errors were grouped by type of change. See the tables below for more details.
Errors that disappear
The following errors exist in the Instore Orders V2 API but have no equivalent in the Orders API.
| HTTP | Instore Orders V2 API | Note |
400 | invalid_collectorId | Removed in the Orders API. Queries operate on the order_id. |
400 | invalid_externalPosId | Removed. Queries operate on order_id, not on the POS. |
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 | Instore Orders V2 API | Orders API | Note |
404 | point_of_sale_in_store_order_not_found and in_store_order_not_found | order_not_found | Both not found 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 | Instore Orders V2 API | Orders API | Note |
400 → 401 | authentication via middleware | unauthorized | In the Instore Orders V2 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 Instore Orders V2 API.
| HTTP | Error | Note |
400 | invalid_path_param | The order_id has an invalid format. 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. Verify the response and retry the request. |
Update order cancellation
The cancellation endpoint changes from DELETE /instore/qr/seller/collectors/{user_id}/pos/{external_pos_id}/ordersAPI to POST /v1/orders/{order_id}/cancelAPI. The HTTP method changes from DELETE to POST, the {user_id} and {external_id} are removed from the path, and cancellation now operates on the order_id. The response changed from empty (HTTP 204) to the complete order object with status: "canceled" (HTTP 200).
In the Instore Orders V2 API, cancellation deleted the active order associated with the POS, an operation on the POS rather than the individual transaction. In the Orders API, cancellation operates directly on the order via order_id, without depending on the POS in the path.
The response returns the complete order object with status: "canceled". 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 V2 API, cancellation returned HTTP 204 without a body. The table below lists the fields returned in the cancellation response.
| Field | Description |
id | Identifier of the cancelled 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 reference of the order, assigned at creation. |
description | Product or service description, the reason for the order. |
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 | Site/country identifier of the Mercado Pago application. |
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. |
currency | Currency identifier used. Values: ARS, BRL, CLP, UYU. |
created_date | Order creation date. Format yyyy-MM-ddTHH:mm:ss.sssZ. |
last_updated_date | Order last updated date. Format yyyy-MM-ddTHH:mm:ss.sssZ. |
config.qr.external_pos_id | External identifier of the POS 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. |
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). |
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. |
In cancellation, errors were grouped by type of change. See the tables below for more details.
Errors that disappear
The following errors exist in the Instore Orders V2 API but have no equivalent in the Orders API.
| HTTP | Instore Orders V2 API | Note |
400 | invalid_collector_id | Removed in the Orders API. |
400 | pos_deleted_error | 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. Identity is validated internally by the Access Token. |
Renamed errors
The following errors were renamed in the Orders API.
| HTTP | Instore Orders V2 API | Orders API | Note |
400 → 404 | pos_obtainment_by_external_id_error and in_store_order_delete_error | order_not_found | In the Instore Orders V2 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 | Instore Orders V2 API | Orders API | Note |
400 → 401 | invalid_access_token | unauthorized | In the Instore Orders V2 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. Verify the response and retry the request. |
Errors introduced by the Orders API
The following errors have no equivalent in the Instore Orders V2 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 has already been used with a different request. |
409 | order_already_canceled | The order is already cancelled. Orders can only be cancelled with status: created. |
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 V2 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.
Choose between a full or partial refund according to the amount to be returned.
To refund the full order amount, send the request to the POST /v1/orders/{order_id}/refundAPI 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.
The refund response varies according to the type of operation performed.
| Field | Type | Description |
id | string | Identifier of the refunded order. |
status | string | refunded for a full refund. processed for a partial refund (balance is still available). |
status_detail | string | refunded for a full refund. partially_refunded for a partial 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, this reflects that amount. |
transactions.refunds[].status | string | processing when the full refund has been requested and is being processed. processed when the partial refund was successfully completed. |
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.
The X-Idempotency-Key header must be present in all creation, cancellation, and refund operations.
Orders successfully created with the new payload at the POST /v1/ordersAPI endpoint and the id field captured from the response.
Orders successfully queried via the GET /v1/orders/{order_id}API endpoint.
Order status monitored via webhook with the "Order (Mercado Pago)" topic and the new values: created, processed, canceled, refunded, and expired.
Orders successfully canceled via the POST /v1/orders/{order_id}/cancelAPI endpoint.
Refunds executed via the dedicated POST /v1/orders/{order_id}/refundAPI endpoint.
See the documentation to learn how to test the integration.