# MD for: https://www.mercadopago.com.ar/developers/es/docs/vault-migration/enrollment-links.md \# Enrollment Links With Enrollment Links, sellers generate a secure link and share it with the buyer so the buyer can link their card to an automatic payments profile — without the seller handling sensitive card data at any point. Card capture and tokenization are managed entirely by Mercado Pago. When the buyer opens the enrollment link, they go through the following flow managed entirely by Mercado Pago: - \*\*Introduction:\*\* the buyer sees a welcome screen prompting them to register their card so the seller can make the charge. - \*\*Card data entry:\*\* the buyer fills in the secure Mercado Pago form with their card number, expiry date, security code, cardholder name, and ID. - \*\*Success:\*\* if the process completes successfully, the buyer sees a confirmation screen and the link transitions to \`vinculated\`. - \*\*Card error:\*\* if the card data is invalid or the card is rejected, the buyer can correct the data and retry. The link remains in \`created\` state. - \*\*Inactive link:\*\* if the link has expired, was already used, or was cancelled, the buyer sees an error message indicating the link is no longer valid. > NOTE > > Before generating an enrollment link, you must have a \`customer\_id\` and a \`profile\_id\` already created. Refer to the \[Customers documentation\](https://www.mercadopago.com.ar/developers/en/reference) and the \[Payment Profiles documentation\](https://www.mercadopago.com.ar/developers/en/docs/vault-migration/payment-profiles) to complete these prerequisite steps. :::AccordionComponent{title="Create an enrollment link" pill="1"} Send a request to :TagComponent{tag="POST" text="/v1/payment-method-enrollments" href="/developers/en/reference/online-payments/checkout-api-payments/vault-migration/enrollment-links/create-enrollment-link/post"} using your :toolTipComponent\[\`APP\_ACCESS\_TOKEN\`\]{content="Production Access Token of the integrator's application. Available under Your integrations > Production credentials." link="/developers/en/docs/your-integrations/credentials"} to generate the link you will share with the buyer. \*\*Body parameters\*\* | Parameter | Type | Required | Description | Example | |---|---|---|---|---| | \`profile\_id\` | String | Yes | ID of the automatic payments profile associated with the buyer. Alphanumeric characters only. Only one active link can exist per profile. | \`7036b192b541454fa9b9990660dfa1b5\` | | \`expiration\_time\` | String | No | Link validity period in ISO 8601 format. Minimum: \`P1D\`. Maximum: \`P90D\`. Default: \`P7D\`. | \`P7D\` | curl curl ``` curl -X POST \ 'https://api.mercadopago.com/v1/payment-method-enrollments' \ -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ -H 'X-Idempotency-Key: 0d5020ed-1af6-469c-ae06-c3bec19954bb' \ -H 'Content-Type: application/json' \ -d '{ "profile_id": "7036b192b541454fa9b9990660dfa1b5", "expiration_time": "P7D" }' ``` If the request is successful, you will receive a \`201 Created\` response with the following body: \`\`\`json { "id": "bea0ffb8-6c94-40a3-bc31-c2e7a0d46ed7", "profile\_id": "7036b192b541454fa9b9990660dfa1b5", "enrollment\_link": "https://mpago.la/1alzDGT", "state": "created", "expiration\_time": "P7D", "expired\_date": "2026-06-05T17:11:41.000-03:00", "created\_date": "2026-05-29T17:11:41.000-03:00", "last\_update\_date": "2026-05-29T17:11:41.000-03:00" } \`\`\` Save the \`id\` value to check the link status later. The \`enrollment\_link\` value is the URL you must share with the buyer. \*\*Possible errors\*\* | HTTP | Code | Cause | |---|---|---| | 400 | \`enrollment\_link.profile\_id\_invalid\_format\` | The \`profile\_id\` contains non-alphanumeric characters or is empty. | | 400 | \`enrollment\_link.expiration\_time\_invalid\_format\` | The \`expiration\_time\` does not follow ISO 8601 format. Correct example: \`P7D\`. | | 400 | \`enrollment\_link.expiration\_time\_out\_of\_range\` | The duration is less than 1 day or more than 90 days. | | 401 | \`enrollment\_link.unauthorized\` | Access Token missing or invalid. | | 404 | \`enrollment\_link.profile\_not\_found\` | The \`profile\_id\` does not exist in Automatic Payments. | | 404 | \`enrollment\_link.resource\_not\_found\` | The \`profile\_id\` exists but does not belong to the authenticated seller. | | 409 | \`enrollment\_link.conflict\` | An active enrollment link already exists for this \`profile\_id\`. Cancel the existing one before creating a new link. | | 500 | \`enrollment\_link.internal\_error\` | Internal error. Retry the request. | ::: :::AccordionComponent{title="Share the link with the buyer" pill="2"} Share the \`enrollment\_link\` URL with the buyer through any channel you prefer: WhatsApp, email, SMS, or another. The buyer opens the URL in their browser, fills in the Mercado Pago card form, and the card is linked to the profile. The seller does not participate in this step and never accesses the card data. The link has an expiration date defined by \`expiration\_time\`. If the buyer does not use it before \`expired\_date\`, the state automatically transitions to \`expired\`. ::: :::AccordionComponent{title="Configure webhook notifications" pill="3"} Instead of polling the link status, you can receive real-time notifications. When the buyer successfully links their card, Automatic Payments sends a webhook with the profile status changing to \`READY\`. To configure notifications: 1\. Go to \*\*Your integrations\*\* in the Developer Dashboard and select your application. 2\. Go to \*\*Webhooks\*\* and register the HTTPS URL of your receiver endpoint. The endpoint must respond with \`200\` within 22 seconds. 3\. Enable the \*\*Payment Profile\*\* event (\`payment\_profile\`). 4\. Copy the generated secret key to validate the \`x-signature\` header of each incoming notification. When the buyer successfully links their card, you will receive a webhook with the following payload: \`\`\`json { "id": "7036b192b541454fa9b9990660dfa1b5", "type": "payment\_profile", "action": "payment\_profile.updated", "version": 1, "live\_mode": true, "data": { "status": "ready", "payment\_methods": \[{ "unique\_id": "pm\_001", "type": "credit\_card", "status": "ready", "card\_id": 9453094596 }\] } } \`\`\` The \`version\` field is an incremental counter per profile. If notifications arrive out of order, always process the one with the highest \`version\` and discard earlier ones. > NOTE > > The webhook only includes fields that changed. To retrieve the full profile state, call \`GET /v1/customers/{customerId}/payment-profiles/{id}\` after receiving the notification. To learn more about notification format and signature validation, refer to the \[Webhooks documentation\](https://www.mercadopago.com.ar/developers/en/docs/your-integrations/notifications/webhooks). ::: :::AccordionComponent{title="Check link status" pill="4"} Send a request to :TagComponent{tag="GET" text="/v1/payment-method-enrollments/{id}" href="/developers/en/reference/online-payments/checkout-api-payments/vault-migration/enrollment-links/get-enrollment-link/get"} including the link \`id\` in the path to verify whether the buyer completed the linking process. curl curl ``` curl -X GET \ 'https://api.mercadopago.com/v1/payment-method-enrollments/bea0ffb8-6c94-40a3-bc31-c2e7a0d46ed7' \ -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' ``` If the buyer completed the linking process, you will receive a \`200 OK\` response with the following body: \`\`\`json { "id": "bea0ffb8-6c94-40a3-bc31-c2e7a0d46ed7", "profile\_id": "7036b192b541454fa9b9990660dfa1b5", "enrollment\_link": "https://mpago.la/1alzDGT", "state": "vinculated", "expiration\_time": "P7D", "expired\_date": "2026-06-05T17:11:41.000-03:00", "created\_date": "2026-05-29T17:11:41.000-03:00", "last\_update\_date": "2026-05-29T18:02:14.000-03:00" } \`\`\` When \`state\` is \`vinculated\`, the card is linked to the profile and ready to use for automatic charges. \*\*Possible link states\*\* | State | Description | |---|---| | \`created\` | Link generated and active. The buyer has not completed the form yet. | | \`vinculated\` | The buyer completed the process. The card is linked to the profile. | | \`cancelled\` | The seller cancelled the link before the buyer used it. | | \`expired\` | The link expired before the buyer completed the process. | | \`error\` | An error occurred during the linking process. | The states \`vinculated\`, \`cancelled\`, \`expired\`, and \`error\` are terminal: once reached, the link cannot transition to any other state. \*\*GET scenarios\*\* | Scenario | Link state | Expected result | |---|---|---| | Newly created link | \`created\` | 200 — \`state: created\` | | Buyer completed the form | \`vinculated\` | 200 — \`state: vinculated\` | | Link cancelled by seller | \`cancelled\` | 200 — \`state: cancelled\` | | Expired link | \`expired\` | 200 — \`state: expired\` | \*\*Possible errors\*\* | HTTP | Code | Cause | |---|---|---| | 400 | \`enrollment\_link.bad\_request\` | The \`id\` is not a valid UUID format. | | 401 | \`enrollment\_link.unauthorized\` | Access Token missing or invalid. | | 404 | \`enrollment\_link.resource\_not\_found\` | The link does not exist or does not belong to the authenticated seller. | | 500 | \`enrollment\_link.internal\_error\` | Internal error. | ::: :::AccordionComponent{title="Cancel a link" pill="5"} Send a request to :TagComponent{tag="DELETE" text="/v1/payment-method-enrollments/{id}" href="/developers/en/reference/online-payments/checkout-api-payments/vault-migration/enrollment-links/disable-enrollment-link/delete"} including the link \`id\` in the path to deactivate it before the buyer uses it. Only links in \`created\` state can be cancelled. curl curl ``` curl -X DELETE \ 'https://api.mercadopago.com/v1/payment-method-enrollments/bea0ffb8-6c94-40a3-bc31-c2e7a0d46ed7' \ -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' ``` A \`204 No Content\` response confirms the link was cancelled. Verify the result with a GET request: \`state\` will be \`cancelled\`. \*\*Possible errors\*\* | HTTP | Code | Cause | |---|---|---| | 401 | \`enrollment\_link.unauthorized\` | Access Token missing or invalid. | | 404 | \`enrollment\_link.resource\_not\_found\` | The link does not exist or does not belong to the authenticated seller. | | 409 | \`enrollment\_link.invalid\_state\_transition\` | The link is in \`vinculated\`, \`expired\`, or \`error\` state and cannot be cancelled. | | 500 | \`enrollment\_link.internal\_error\` | Internal error. | ::: :::AccordionComponent{title="Test the integration" pill="6"} Use the :toolTipComponent\[test credentials\]{content="Test credentials available in the Developer Dashboard under Your integrations > Test credentials." link="/developers/en/docs/your-integrations/credentials"} from your Mercado Pago account to validate the full flow without processing real transactions. ### Prerequisites - Retrieve your test \`APP\_ACCESS\_TOKEN\` from \*\*Your integrations > Test credentials\*\*. - Get test cards to simulate approval and rejection scenarios from the \[test cards documentation\](https://www.mercadopago.com.ar/developers/en/docs/your-integrations/test/cards). - Create a test customer with an email in the format \`xxxx@testuser.com\`. - Create a Payment Profile from that test customer's \`customer\_id\`. ### Scenarios — POST /v1/payment-method-enrollments | Scenario | Request | Expected result | |---|---|---| | Create link with default validity (7 days) | \`{"profile\_id": "{PROFILE\_ID}"}\` | 201 — \`state: created\`, \`enrollment\_link\` starts with \`https://mpago.la/\` | | Create link with explicit \`expiration\_time\` | \`{"profile\_id": "{PROFILE\_ID}", "expiration\_time": "P7D"}\` | 201 — \`expired\_date\` is 7 days from now | | Minimum valid \`expiration\_time\` | \`"expiration\_time": "P1D"\` | 201 | | Maximum valid \`expiration\_time\` | \`"expiration\_time": "P90D"\` | 201 | | \`profile\_id\` with special characters | \`"profile\_id": "abc!@#"\` | 400 — \`enrollment\_link.profile\_id\_invalid\_format\` | | Empty \`profile\_id\` | \`"profile\_id": ""\` | 400 — \`enrollment\_link.profile\_id\_invalid\_format\` | | Invalid ISO 8601 format | \`"expiration\_time": "7D"\` | 400 — \`enrollment\_link.expiration\_time\_invalid\_format\` | | Duration less than 1 day | \`"expiration\_time": "PT23H59M59S"\` | 400 — \`enrollment\_link.expiration\_time\_out\_of\_range\` | | Duration more than 90 days | \`"expiration\_time": "P100D"\` | 400 — \`enrollment\_link.expiration\_time\_out\_of\_range\` | | Active link already exists for same profile | Second POST with same \`profile\_id\` | 409 — \`enrollment\_link.conflict\` | | Non-existent \`profile\_id\` | \`profile\_id\` not registered in AP | 404 — \`enrollment\_link.profile\_not\_found\` | | \`profile\_id\` belonging to another seller | \`profile\_id\` of a different seller | 404 — \`enrollment\_link.resource\_not\_found\` | | Missing authentication | No \`Authorization\` header | 401 — \`enrollment\_link.unauthorized\` | ### Scenarios — GET /v1/payment-method-enrollments/{id} | Scenario | Link state | Expected result | |---|---|---| | Newly created link | \`created\` | 200 — \`state: created\` | | Buyer completed the form | \`vinculated\` | 200 — \`state: vinculated\` | | Link cancelled by seller | \`cancelled\` | 200 — \`state: cancelled\` | | Expired link | \`expired\` | 200 — \`state: expired\` | | Error during linking process | \`error\` | 200 — \`state: error\` | | Invalid ID format (not UUID) | — | 400 — \`enrollment\_link.bad\_request\` | | Link not found or from another seller | — | 404 — \`enrollment\_link.resource\_not\_found\` | ### Scenarios — DELETE /v1/payment-method-enrollments/{id} | Scenario | Link state | Expected result | |---|---|---| | Cancel active link | \`created\` | 204 — subsequent GET returns \`state: cancelled\` | | Cancel already linked | \`vinculated\` | 409 — \`enrollment\_link.invalid\_state\_transition\` | | Cancel expired link | \`expired\` | 409 — \`enrollment\_link.invalid\_state\_transition\` | | Cancel link with error | \`error\` | 409 — \`enrollment\_link.invalid\_state\_transition\` | | Link not found | — | 404 — \`enrollment\_link.resource\_not\_found\` | :::