# Amend outbound instructions When an exchange cannot deliver a payment due to incorrect or missing `beneficiary_info` details, On-Demand Liquidity allows you to amend the `outbound_instructions` object using the RippleNet [Add payment sub state](/products/payments-odl/api-docs/ripplenet/reference/openapi/non-orchestration-payments/addpaymentsubstate) operation. The reasons for a non-deliverable payment may be due to a missing field required by the payout outlet for [KYC](/products/payments-odl/glossary#know-your-customer-kyc) or compliance checks, a misspelled beneficiary's first or last name, or an address that doesn't match the address on the ID of the beneficiary picking up the payment. The following diagram shows the flow for situations where an exchange cannot deliver a payment, and how On-Demand Liquidity handles the payment. On-Demand Liquidity provides functionality for a sender to amend the `outbound_instructions` for a payment. ![Amend Outbound Instructions Flow](/assets/bene-info-amendment-flow-odl.f8fe1af2cb2eb3db3f7c376a4b5e5cf2fa01f4340e81ca497eafeab0834d5e00.0d998ffb.png) ## Labels for outbound instructions The following labels are used by On-Demand Liquidity as a `RECEIVING` node to inform the `SENDING` node that a payment cannot be delivered by a payout outlet. Depending on the label added to the RippleNet Payment Object (RPO), you can either amend the `outbound_instructions` for the payment and try to complete the payment, or you can recover the funds manually from the destination exchange. | Label | Description | | --- | --- | | `OUTBOUND_TRANSFER_FAILED_RECOVERABLY` | Payments with this label are in an `EXECUTED` payment state. When present, the `SENDING` node can update the `outbound_instructions` object with the "Add Payment Sub-State" operation, adding an `AMEND` sub-state to the payment. The exchange will reattempt a payout to the beneficiary. If the amended `outbound_instructions` result in a successful payout to the beneficiary, On-Demand Liquidity transitions the payment state to `COMPLETED`. | | `OUTBOUND_TRANSFER_FAILED_IRRECOVERABLY` | When set, the exchange could not make the payment to the beneficiary, and transitioned the payment to a `FAILED` payment state. On-Demand Liquidity adds this label when the number of `AMEND` retries has been exhausted. In this case, the `SENDING` node must recover the funds manually from the destination exchange. | ## Amend a payment's outbound instructions To amend outbound instructions, configure your middleware as follows: ### Step 1 Check for payments that require corrections to the `outbound_instructions` using the [Get Payments](/products/payments-odl/api-docs/ripplenet/reference/openapi/non-orchestration-payments/getpayments) operation. Pass in query parameters to return payments with an `OUTBOUND_TRANSFER_FAILED_RECOVERABLY` label. Make a note of any RippleNet `payment_id`s that match the query. **Example:** `GET https://{{target.example_url_string}}/v4/payments?with_labels=OUTBOUND_TRANSFER_FAILED_RECOVERABLY` Note: You should also check for payments that failed and the exchange has determined the issue cannot be fixed by amending the `outbound_instructions`. Use the "Get Payments" operation, passing in query parameters to return payments with an `OUTBOUND_TRANSFER_FAILED_IRRECOVERABLY` label. br Here's an example of a payment with the label `OUTBOUND_TRANSFER_FAILED_RECOVERABLY` set by On-Demand Liquidity: br **Get payments (HTTP request)** ```http GET /v4/payments?with_labels=OUTBOUND_TRANSFER_FAILED_RECOVERABLY HTTP/1.1 Host: https://{{target.example_url_string}} Content-Type: application/json Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ``` br **Get Payments (cURL request)** ```js curl -X GET \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' \ -H 'Content-Type: application/json' https://{{target.example_url_string}}/v4/payments?with_labels=OUTBOUND_TRANSFER_FAILED_RECOVERABLY ``` br **Response (success)** Response has been shortened to show pertinent information. ```json { "payment_id": "98d08b9e-4885-48e4-9e09-8f457859e142", "contract_hash": "abc65fad5e00aac237a44f22e7919046bc2ac2df3b955", "payment_state": "EXECUTED", "internal_info": { "internal_id": "6b0a4263-f9b9-48f3-910d-005b6873cb5e", "connector_role": "RECEIVING", "labels": [ { "label": "OUTBOUND_TRANSFER_FAILED_RECOVERABLY" } ] }, "modified_at": "2020-01-08T00:58:49.129Z", ... ``` br ### Step 2 Make a call to the "Add Payment Sub-State" operation. Provide the corrected `outbound_instructions` object and add an `AMEND` `sub-state` in the request. This call updates beneficiary information for the exchange to reattempt making the payout. You can provide a `memo` in the request body of your API call. Note: You must include the complete `outbound_instructions` object in your request body for this call. br **Add payment sub-state (HTTP request)** ```http POST /v4/payments/98d08b9e-4885-48e4-9e09-8f457859e142/sub_state HTTP/1.1 Host: https://{{target.example_url_string}} Content-Type: application/json Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 { "sub_state": "AMEND", "memo": "updates recipient given names value", "info": { "outbound_instructions": { "outlet_id": "spei", "beneficiary_info": [ { "field_name": "recipient_family_names", "field_value": "Minuarez" }, { "field_name": "recipient_given_names", "field_value": "Jorge" }, { "field_name": "clabe", "field_value": "014027000005555558" } ] } } } ``` br **Add payment sub-state (cURL request)** ```js curl -X POST \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' \ -H 'Content-Type: application/json' \ https://{{target.example_url_string}}/v4/payments/98d08b9e-4885-48e4-9e09-8f457859e142/sub_state -d '{ "sub_state": "AMEND", "memo": "updates recipient given names value", "info": { "outbound_instructions": { "outlet_id": "spei", "beneficiary_info": [ { "field_name": "recipient_family_names", "field_value": "Minuarez" }, { "field_name": "recipient_given_names", "field_value": "Jorge" }, { "field_name": "clabe", "field_value": "014027000005555558" } ] } } } ``` br ### Step 3 Use the [Delete payment labels](/products/payments-odl/api-docs/ripplenet/reference/openapi/non-orchestration-payments/deletepaymentlabels) operation to delete the `OUTBOUND_TRANSFER_FAILED_RECOVERABLY` label from the RippleNet payment you just amended. It's good practice to remove the label to ensure the amended payment won't appear in a subsequent search for payments that require amendments. On-Demand Liquidity may add this label again if there's still an issue delivering this payment, and you haven't reached the retry limit. **Format:** `DELETE {% title="{ripplenet_base_url}/v4/payments/{payment_id}/labels?label={label_name}`" %} **Delete payment label (HTTP request)** ```http DELETE /v4/payments/98d08b9e-4885-48e4-9e09-8f457859e142/labels?label=OUTBOUND_TRANSFER_FAILED_RECOVERABLY HTTP/1.1 Host: https://{{target.example_url_string}} Content-Type: application/json Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ``` br **Delete payment label (cURL Request)** ```js curl -X DELETE \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' \ -H 'Content-Type: application/json' \ https://{{target.example_url_string}}/v4/payments/98d08b9e-4885-48e4-9e09-8f457859e142/labels?label=OUTBOUND_TRANSFER_FAILED_RECOVERABLY ``` br ### Step 4 If the update to the `outbound_instructions` object in your RippleNet Payment Object results in a successful payout to the beneficiary, On-Demand Liquidity transitions the payment to `COMPLETED`. Check the status of the payment by calling the [Get payment by payment ID](/products/payments-odl/api-docs/ripplenet/reference/openapi/non-orchestration-payments/getpaymentbypaymentid) operation using the `payment_id`. **Get payment by ID (HTTP request)** ```http GET {{target.example_url_string}}/v4/payments/98d08b9e-4885-48e4-9e09-8f457859e142 HTTP/1.1 Host: https://{{target.example_url_string}} Content-Type: application/json Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ``` br **Get payment by ID (cURL request)** ```js curl -X GET \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' \ -H 'Content-Type: application/json' \ https://{{target.example_url_string}}/v4/payments/98d08b9e-4885-48e4-9e09-8f457859e142 ``` br - If the RippleNet Payment Object contains `OUTBOUND_TRANSFER_FAILED_RECOVERABLY` label, retry. If the payout attempt is unsuccessful and you can still amend the outbound instructions, On-Demand Liquidity adds an `OUTBOUND_TRANSFER_FAILED_RECOVERABLY` label to the RippleNet payment object and the payment remains in the `EXECUTED` state. Repeat steps one through four until successful or until you have reached the retry limit. ```json { "payment_id": "98d08b9e-4885-48e4-9e09-8f457859e142", "contract_hash": "abc65fad5e00aac237a44f22e7919046bc2ac2df3b955", "payment_state": "EXECUTED", "internal_info": { "internal_id": "6b0a4263-f9b9-48f3-910d-005b6873cb5e", "connector_role": "RECEIVING", "labels": [ { "label": "AMEND" }, { "label": "OUTBOUND_TRANSFER_FAILED_RECOVERABLY" } ] }, "modified_at": "2020-01-08T00:59:23.129Z", ... ``` - If the RippleNet Payment Object contains `OUTBOUND_TRANSFER_FAILED_IRRECOVERABLY` label, recover funds. If the payment is not successful, you have reached the amend retry limit, and the exchange determines the payment cannot be made to the beneficiary, On-Demand Liquidity adds an `OUTBOUND_TRANSFER_FAILED_IRRECOVERABLY` label to the payment object, and transitions the payment state to `FAILED`. Manually recover the funds from the destination exchange, in this case. ```undefined json { "payment_id": "98d08b9e-4885-48e4-9e09-8f457859e142", "contract_hash": "abc65fad5e00aac237a44f22e7919046bc2ac2df3b955", "payment_state": "FAILED", "internal_info": { "internal_id": "6b0a4263-f9b9-48f3-910d-005b6873cb5e", "connector_role": "RECEIVING", "labels": [ { "label": "OUTBOUND_TRANSFER_FAILED_IRRECOVERABLY" }, { "label": "AMEND" } ] }, "modified_at": "2020-01-08T00:59:23.129Z", ... ```