How to migrate from the Payment Intent API to the Orders API
The Orders API unifies in-person payment processing for Mercado Pago Point, providing standardized endpoints, a more complete status model, and new native capabilities that did not exist in the Payment Intent API. In addition, all new Mercado Pago features will be developed on the Orders API.
Migrating your Mercado Pago Point integration from the Payment Intent API to the Orders API involves updating endpoints and request fields, adapting the status model, and leveraging new native resources, such as the dedicated refund endpoint. The migration does not change the business flow: the terminal continues receiving orders created by the backend and processing payments autonomously.
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 Payment Intent API, each operation had its own URL structure with the device identifier in the path. The Orders API consolidates these operations into standardized endpoints and removes the {deviceid} from the path in all operations.
The Orders API introduces two mandatory header changes that affect all endpoints: the sandbox mechanism was removed and a new idempotency control was introduced. Apply the following changes before testing any other resource.
The x-test-scope: sandbox header was used in the Payment Intent API to identify test requests. In the Orders API, the sandbox mechanism was restructured and this header no longer exists. Remove it from all integration requests. To operate in a test environment with the Orders API, use test users. See the integration test documentation for more information.
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.
Update terminal listing and configuration
The terminal endpoints changed URL: listing moves from GET /point/integration-api/devices to GET /terminals/v1/list and mode update moves from PATCH /point/integration-api/devices/{device_id} to PATCH /terminals/v1/setup. In the mode update, the terminal identifier also moves from the path to the body, allowing multiple terminals to be updated in a single call.
In the Payment Intent API, devices were returned in the devices array. In the Orders API, they move to data.terminals. The query params store_id, pos_id, limit, and offset remain the same. The table below describes the response changes.
Payment Intent API
Orders API
Description
Change
devices[]
data.terminals[]
List of terminals associated with the account. In the Payment Intent API, it was returned as a direct array in devices. In the Orders API, it is moved inside the data object and renamed to terminals.
Renamed and moved inside the data object.
devices[].id
data.terminals[].id
Unique terminal identifier. The last characters match the serial number on the back label of the terminal.
Same concept. The ID format may vary depending on the terminal model.
devices[].pos_id
data.terminals[].pos_id
Identifier of the point of sale associated with the terminal.
No changes.
devices[].store_id (integer)
data.terminals[].store_id (string)
Identifier of the store associated with the terminal. In the Payment Intent API, it was returned as integer. In the Orders API, it is returned as string.
Changes from integer to string.
devices[].external_pos_id
data.terminals[].external_pos_id
External point of sale identifier, defined by the integrator.
No changes.
devices[].operating_mode
data.terminals[].operating_mode
Terminal operating mode. In the Payment Intent API, the possible values are PDV and STANDALONE. In the Orders API, the UNDEFINED value is added for unrecognized configurations.
Adds the UNDEFINED value for unrecognized configurations.
paging.total / paging.offset / paging.limit
paging.total / paging.offset / paging.limit
Listing pagination data: total records, start point, and limit.
No changes.
In the terminal listing, errors are grouped by type of change. See the tables below for more details.
Errors not documented in the Orders API
The following errors exist in the Payment Intent API but are not documented in the Orders API.
HTTP
Payment Intent API
Note
400
bad_request
Required parameter missing or with incorrect format.
400
bad_request
Invalid request format.
403
forbidden
Present in the Payment Intent API.
Errors that remain unchanged
The following errors have the same behavior in both APIs.
HTTP
Error
Note
401
unauthorized
Invalid or expired token.
500
internal_error
Internal error. Check the response and retry.
In the Payment Intent API, only one terminal could be updated per call and its identifier was sent in the path. In the Orders API, the identifier moves to the body inside the terminals[] array, allowing multiple terminals to be updated in a single call.
Below is a comparative example of an operating mode update.
Operating mode update via the Payment Intent API. The terminal identifier is sent as a path param.
In the Payment Intent API, the response returns only the operating_mode field. In the Orders API, the response returns the terminals[] array with id and operating_mode, identifying each updated terminal. See the table below for the complete mapping.
Payment Intent API
Orders API
Description
Change
operating_mode
terminals[].operating_mode
Operating mode defined for the terminal. In the Payment Intent API, it was returned as a single field. In the Orders API, it is returned inside the terminals[] array. The possible values are PDV and STANDALONE.
Moves inside the terminals[] array.
The Orders API also introduces the following field with no equivalent in the Payment Intent API.
Field
Description
terminals[].id
Unique identifier of the updated terminal. It is returned only in the Orders API.
In the operating mode update, errors are grouped by type of change. See the tables below for more details.
Errors that disappear
The following error exists in the Payment Intent API but was removed in the Orders API.
HTTP
Payment Intent API
Note
424
failed_dependency
Code removed in the Orders API.
Renamed errors
The following error was renamed in the Orders API.
HTTP
Payment Intent API
Orders API
Note
403
forbidden
store_pos_not_found
Terminal has no associated store or point of sale.
Errors that change behavior
The following error exists in both APIs but with a different meaning in the Orders API.
HTTP
Payment Intent API
Orders API
Note
400
bad_request
property_value
Invalid device_id or operating_mode value.
Errors that remain unchanged
The following errors have the same behavior in both APIs.
HTTP
Error
Note
401
unauthorized
Invalid or expired token.
500
internal_error
Internal error. Retry the request.
Errors introduced by the Orders API
The following errors have no equivalent in the Payment Intent API.
HTTP
Orders API error
Note
400
unsupported_site
Invalid site.
400
required_properties
Required property missing.
400
unsupported_properties
Unsupported field sent.
400
invalid_payload
Invalid payload. Check the submitted fields.
403
terminal_not_allowed_action
Action not allowed for this terminal model.
404
not_found
Resource not found or invalid ID.
412
(no string code)
Operation not allowed: a terminal is already associated with the point of sale in PDV mode. Only one terminal per point of sale is allowed in PDV mode.
Migrate payment intent creation to order creation
The creation endpoint changes from POST /point/integration-api/devices/{deviceid}/payment-intents to POST /v1/orders. Beyond the URL change, the request structure was significantly reorganized: the terminal identifier moves from the path to the body, new required fields were introduced, and the Orders API has a distinct structure. Follow the steps below to adapt the request, response, and error handling.
The Orders API has a different structure from the Payment Intent API: the terminal identifier must be sent in the body, payment and printing configuration fields are placed in new nodes, and identification headers become part of the body. The new required fields are type (with value "point") and external_reference. See the table below for the complete field mapping.
Below is a comparative example between creating a payment intent and creating an order, followed by the complete field mapping table.
Request to create a payment intent. The terminal identifier is sent as a path param and amount is an integer value.
Identifies the terminal that will receive the order. In the Payment Intent API, it is sent as a path parameter. In the Orders API, it is sent in the body in the format {terminal_type}__{terminal_serial}.
Moves from path param to body field.
amount (integer)
transactions.payments[].amount (string)
Amount to charge. In the Payment Intent API, it is represented as an integer with implicit two decimals (e.g.: 1500 for $15.00). In the Orders API, it is represented as a string inside the transactions.payments array. Accepts two decimal places (e.g.: "15.00") or none. Only 1 transaction per order is allowed when type is point.
Changes from integer to string and enters the transactions.payments array.
additional_info.external_reference
external_reference
Alphanumeric identifier for the transaction in the integrator's system, returned in webhook notifications. In the Orders API, it is required, with a maximum of 64 characters, only letters, numbers, - and _. Must be unique per order and cannot contain PII data.
Moves to root level and becomes required.
additional_info.print_on_terminal (boolean)
config.point.print_on_terminal (string)
Controls ticket printing on the terminal. In the Payment Intent API, it accepts the boolean values true and false. In the Orders API, it accepts the values seller_ticket (prints) and no_ticket (does not print). Default value: seller_ticket.
Changes from boolean to enum string.
payment.type
config.payment_method.default_type
Defines the payment method accepted by the terminal. In the Payment Intent API, the possible values are credit_card, debit_card, and voucher_card. In the Orders API, the possible values are credit_card, debit_card, and qr. If not sent, the terminal accepts all payment methods.
Changes node and now also accepts qr.
payment.instalments_cost
config.payment_method.default_installments_cost
Defines who bears the financial cost of installments. seller: the seller bears it. buyer: the buyer bears it. Available only on Point Pro 2 and Point Pro 3 terminals.
Changes node.
description
description
Describes the product, service, or payment reason. In the Orders API, it accepts up to 150 characters.
No changes.
X-platform-id (header)
integration_data.platform_id
Identifies the integration platform, assigned by Mercado Pago. In the Payment Intent API, it was sent as a header. In the Orders API, it is sent in the body inside integration_data.
Moves from header to body.
X-integrator-id (header)
integration_data.integrator_id
Identifies the integrator, assigned by Mercado Pago. In the Payment Intent API, it was sent as a header. In the Orders API, it is sent in the body inside integration_data.
Moves from header to body.
payment.instalments
config.payment_method.default_installments
Defines the pre-selected number of installments for credit card payments. It is only allowed when default_type = credit_card. If the card does not support the specified quantity, the terminal displays the selection screen.
Changes node.
In the migration to the Orders API, the following field was removed and has no direct equivalent.
Payment Intent API field
Note
payment.voucher_type
Removed. Absorbed by config.payment_method.default_type.
The Orders API also introduces the following fields with no equivalent in the Payment Intent API.
Field
Description
integration_data.sponsor.id
USER_ID of the Mercado Pago account of the integrating system.
type
Order type. For Point, the only accepted value is "point". Required.
expiration_time
Defines the order validity period from creation, in ISO 8601 format. The minimum value is PT30S (30 seconds), the maximum value is PT3H (3 hours) and the default value is PT15M (15 minutes). Examples: PT30S for 30 seconds, PT10M for 10 minutes, PT1H15M for 1 hour and 15 minutes. If the order expires without being processed, it is automatically canceled. Optional.
The order ID format changes from UUID (e.g.: 7f25f9aa-eea6-...) to alphanumeric (e.g.: ORD00001111222233334444555566). Update any storage or query logic that depends on the ID format before proceeding.
The table below shows the creation response fields that existed in both APIs and were updated during the migration.
Payment Intent API
Orders API
Description
Change
id (UUID)
id (alphanumeric)
Order identifier, generated by Mercado Pago. In the Payment Intent API, it is returned in UUID format. In the Orders API, it is returned in alphanumeric format with ORD prefix.
ID format changes to ORD....
device_id
config.point.terminal_id
Identifier of the terminal that received the order.
Moves to config.point.
amount (integer)
transactions.payments[].amount (string)
Payment amount. In the Payment Intent API, it is returned as an integer. In the Orders API, it is returned as a decimal string inside the payments array.
Moves to the payments array.
additional_info.external_reference
external_reference
External reference of the order in the integrator's system.
Moves to root level.
additional_info.print_on_terminal (boolean)
config.point.print_on_terminal (string)
Indicates whether the terminal printed the ticket. In the Payment Intent API, it is returned as a boolean. In the Orders API, it is returned as an enum string: seller_ticket or no_ticket.
Changes from boolean to enum string.
additional_info.ticket_number
config.point.ticket_number
Alphanumeric value to identify the invoice or ticket number, printed on the terminal when printing is enabled.
Moves to config.point.
The Orders API also introduces the following fields with no equivalent in the Payment Intent API.
Field
Description
status
Current order status. On creation: created.
status_detail
Order status detail. On creation: created.
type
Order type. For Point: always point.
expiration_time
Order validity period in ISO 8601 format. If the order expires without being processed, it is automatically canceled.
created_date / last_updated_date
Records the order creation and last update dates in yyyy-MM-ddTHH:mm:ss.sssZ format.
transactions.payments[].id
Payment transaction identifier, generated by Mercado Pago. Required for partial refunds.
transactions.payments[].status
Payment transaction status. On creation: created.
user_id
Identifier of the Mercado Pago user who created the order.
processing_mode
Order processing mode. For Point: always automatic.
country_code
Application site/country identifier.
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.
In creation, errors are grouped by type of change. See the tables below for more details.
Errors that change behavior
In the Payment Intent API, the following errors returned the generic bad_request code. In the Orders API, they are replaced by specific codes.
HTTP
Payment Intent API
Orders API
Note
400
bad_request
required_properties
Required property missing.
400
bad_request
property_value
Invalid value in a property.
400
bad_request
property_type
Incorrect type. For example: integer instead of string.
400
bad_request
json_syntax_error
Invalid JSON.
400
bad_request
unsupported_properties
Unsupported property sent.
Renamed errors
The following errors were renamed in the Orders API.
HTTP
Payment Intent API
Orders API
Note
403
forbidden
forbidden_checking_terminal_owner
Terminal not linked to the account.
409
conflict_error
already_queued_order_for_terminal
An order is already queued for this terminal.
Errors that disappear
The following error exists in the Payment Intent API but was removed in the Orders API.
HTTP
Payment Intent API
Note
424
failed_dependency
Code removed in the Orders API.
Errors that remain unchanged
The following error has the same behavior in both APIs.
HTTP
Error
Note
401
unauthorized
Invalid or expired token.
500
internal_error
Internal error. Check the response and retry the request.
Errors introduced by the Orders API
The following errors have no equivalent in the Payment Intent API.
HTTP
Orders API error
Note
400
empty_required_header
X-Idempotency-Key missing.
400
minimum_properties
Minimum number of required properties not sent.
400
minimum_items / maximum_items
Invalid number of items in the transactions.payments array.
409
idempotency_key_already_used
Idempotency key reused with a different body.
500
idempotency_validation_failed
Idempotency validation failure. Retry the request.
Update the query mechanism from Payment Intent to Orders
In addition to the fields documented in the creation response, the GET response includes additional fields relevant to the migration. See the table below for details.
Payment Intent API
Orders API
Description
Change
state
status
Order status. In the Payment Intent API, state field with uppercase values. In the Orders API, status field with lowercase values and an expanded set of statuses.
Identifier of the processed payment, available when the flow completes successfully. In the Payment Intent API, it was returned as an integer in payment.id. In the Orders API, it is returned as a string in transactions.payments[].reference_id.
Processed payment identifier. Changes from integer to string.
The Orders API also introduces the following fields with no equivalent in the Payment Intent API.
Field
Description
status_detail
Order status detail. Possible values: accredited, canceled_by_api, bad_filled_card_data, insufficient_amount, among others. See the API Reference for the complete list.
transactions.payments[].paid_amount
Amount effectively paid in the transaction.
transactions.payments[].refunded_amount
Amount refunded in the transaction.
transactions.payments[].status
Payment transaction status.
transactions.payments[].status_detail
Transaction status detail. Same possible values as status_detail of the order.
transactions.payments[].payment_method.type
Payment method used in the transaction (e.g.: credit_card, debit_card).
First and last digits of the card used in the transaction, available when payment was made by card.
transactions.refunds[]
Array with the refund data for the order (id, transaction_id, reference_id, amount, status).
In the query, errors are grouped by type of change. See the tables below for more details.
Errors that disappear
The following errors exist in the Payment Intent API but have no equivalent in the Orders API.
HTTP
Payment Intent API
Note
401
unauthorized (Unauthorized use of live credentials)
The x-test-scope header was removed in the Orders API.
403
forbidden
Terminal ownership validation occurs only during creation.
429
too_many_requests
Present in the Payment Intent API. Not documented in the Orders API.
Renamed errors
The following error was renamed in the Orders API.
HTTP
Payment Intent API
Orders API
Note
404
not_found
order_not_found
Order not found. Verify that the submitted ID is correct.
Errors that change behavior
The following error exists in both APIs but with a different meaning in the Orders API.
HTTP
Payment Intent API
Orders API
Note
400
bad_request
bad_request
Invalid order_id format. The ID changes from UUID to alphanumeric.
Errors that remain unchanged
The following errors have the same behavior in both APIs.
HTTP
Error
Note
401
unauthorized
Invalid or expired token.
500
internal_error
Internal error. Retry the request.
Update status handling
One of the most significant changes between the Payment Intent API and the Orders API is the state field, which is renamed to status in the new API and receives new values. In the Payment Intent API, the FINISHED status required additional calls to determine the actual payment result. In the Orders API, the processed and failed statuses are self-contained and eliminate that need. Update all status checks in your integration according to the table below.
Payment Intent API (state)
Orders API (status)
Note
OPEN
created
Renamed
ON_TERMINAL
at_terminal
Renamed
PROCESSING
Absorbed
No intermediate status in the Orders API
PROCESSED
Absorbed
No intermediate status in the Orders API
FINISHED (approved payment)
processed
The approved payment result is contained in the order itself, without requiring additional queries.
FINISHED (rejected payment)
failed
The rejected payment result is contained in the order itself, without requiring additional queries.
CONFIRMATION_REQUIRED
action_required
Renamed
ERROR
failed
Renamed
ABANDONED
expired
Renamed (timeout-based)
CANCELED
canceled
Same meaning. Now lowercase
Does not exist
refunded
New status. Must be handled explicitly in all flows
In the Payment Intent API, confirming the result of a payment with state: FINISHED required inspecting payment.state in the webhook, calling GET /v1/payments/{id}, or searching by external_reference. In the Orders API, the processed and failed statuses are self-contained: the payment result is available directly in the order, without additional queries.
Update order cancellation
The new endpoint to cancel an order is POST /v1/orders/{orderid}/cancel, replacing the DELETE from the Payment Intent API. The {deviceid} is removed from the path and the cancellation is now identified solely by the {orderid}.
In the Payment Intent API, it was only possible to cancel payment intents that had not yet reached the terminal. In the Orders API, it is possible to cancel an order even after it has reached the terminal, using a new specific header.
The response now returns the complete order object with status: "canceled", instead of just the id. The headers required for this operation are:
Header
Required
Description
X-Idempotency-Key
Required
Unique key per request
x-allow-cancelable-status
Conditional
Required to cancel orders in at_terminal. Value: "at_terminal"
To cancel orders with at_terminal status, send the x-allow-cancelable-status: at_terminal header. Without this header, only orders with status: created will be canceled.
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 Payment Intent API but have no equivalent in the Orders API.
HTTP
Payment Intent API
Note
400
bad_request (deviceID format)
deviceid was removed from the path in the Orders API.
429
too_many_requests
Present in the Payment Intent API. Not documented in the Orders API.
Renamed errors
The following error was renamed in the Orders API.
HTTP
Payment Intent API
Orders API
Note
409
conflict_error
cannot_cancel_order
Order status does not allow cancellation. For at_terminal, send x-allow-cancelable-status: at_terminal.
Errors that change behavior
The following errors exist in both APIs but with different behavior in the Orders API.
HTTP
Payment Intent API
Orders API
Note
400
bad_request
bad_request
Invalid order_id format. The ID changes from UUID to alphanumeric.
404
101
order_not_found
Order not found. In the Payment Intent API, the error field uses the numeric code "101".
409
101
order_already_canceled
In the Payment Intent API, non-existent orders and already-canceled orders returned the same "101" error. In the Orders API, they are differentiated into two distinct errors: 404 for order not found and 409 for order already canceled.
500
internal_error
idempotency_validation_failed
Idempotency validation failure. Retry the request.
Errors that remain unchanged
The following error has the same behavior in both APIs.
HTTP
Error
Note
401
unauthorized
Invalid or expired token.
Errors introduced by the Orders API
The following errors have no equivalent in the Payment Intent API.
HTTP
Orders API error
Note
400
empty_required_header
X-Idempotency-Key missing.
409
idempotency_key_already_used
Idempotency key reused.
Implement refunds with the dedicated endpoint
The Orders API introduces the dedicated endpoint POST /v1/orders/{orderid}/refund for refunds, which did not exist in the Payment Intent 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 based on the amount to return.