curl --request POST \
--url https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release \
--header 'x-api-key: <api-key>'import requests
url = "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release"
headers = {"x-api-key": "<api-key>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {'x-api-key': '<api-key>'}};
fetch('https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"id": "11111111-2222-3333-4444-555555555555",
"status": "HOLD_RELEASED",
"amount": 123,
"currency": "MXN",
"heldAmount": 3500,
"externalId": "invoice-2025-01-8842",
"heldAt": "2026-08-12T10:00:00.000Z",
"capturedAt": "2023-11-07T05:31:56Z",
"holdReleasedAt": "2026-08-14T09:30:00.000Z",
"holdExpiresAt": "2026-08-19T05:00:00.000Z",
"holdExpired": true,
"holdDaysRemaining": 123
}Release Hold
Releases a payment that is currently HELD, returning the held funds to the cardholder. The endpoint takes no request body. A successful release leaves the payment HOLD_RELEASED with holdReleasedAt set. A hold that is never captured or released is released automatically by a sweep that runs twice each night, at 23:00 and again at 23:15 America/Mexico_City, at the end of the 7th natural day counting from the day it was placed, and ends up as HOLD_EXPIRED. The 23:15 run is a catch-up in case the 23:00 run is slow or fails, it lands before the bank closes its day at 23:30, and it is a no-op when the first run already succeeded. It does not extend the capture window: capture is still refused from 23:00 on the last day. Unlike capture, release has NO time cutoff: releasing a hold after 23:00 on its last day, or after its window closed, still works. The hold window is counted in NATURAL DAYS, not in elapsed hours. The day the hold is placed counts as day 1 and the time of day it was placed is irrelevant: a hold placed Monday 10:00 and one placed Monday 23:29 both expire at the same instant, at the end of the following Sunday. The bank closes its day at 23:30 America/Mexico_City and Compago closes the hold at 23:00, 30 minutes earlier, so the last minute to capture is 22:59 on the last day.
curl --request POST \
--url https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release \
--header 'x-api-key: <api-key>'import requests
url = "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release"
headers = {"x-api-key": "<api-key>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {'x-api-key': '<api-key>'}};
fetch('https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/release")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"id": "11111111-2222-3333-4444-555555555555",
"status": "HOLD_RELEASED",
"amount": 123,
"currency": "MXN",
"heldAmount": 3500,
"externalId": "invoice-2025-01-8842",
"heldAt": "2026-08-12T10:00:00.000Z",
"capturedAt": "2023-11-07T05:31:56Z",
"holdReleasedAt": "2026-08-14T09:30:00.000Z",
"holdExpiresAt": "2026-08-19T05:00:00.000Z",
"holdExpired": true,
"holdDaysRemaining": 123
}Authorizations
Path Parameters
The unique identifier of the payment method
The unique identifier of the held payment to release
Response
Hold released successfully
Response after releasing a held payment
Unique identifier of the payment
"11111111-2222-3333-4444-555555555555"
Payment status after a successful release. A hold released by the automatic nightly expiry sweep (23:00 and again at 23:15 America/Mexico_City) instead of by this endpoint ends up as HOLD_EXPIRED.
HOLD_RELEASED "HOLD_RELEASED"
The released amount
"MXN"
What the bank authorized when the hold was placed. On a released hold nothing was ever captured, so it equals amount.
3500
The idempotency key supplied when the payment was created, echoed back so you can map this payment id to your own order reference. Null when none was supplied. Not to be confused with the payment method externalId, which identifies the saved card.
"invoice-2025-01-8842"
When the bank approved the hold
"2026-08-12T10:00:00.000Z"
Null on a released hold: nothing was ever captured.
When the hold was released
"2026-08-14T09:30:00.000Z"
When the hold would have expired: 23:00 America/Mexico_City on the 7th natural day, counting the day of heldAt as day 1. The hold window is counted in NATURAL DAYS, not in elapsed hours. The day the hold is placed counts as day 1 and the time of day it was placed is irrelevant: a hold placed Monday 10:00 and one placed Monday 23:29 both expire at the same instant, at the end of the following Sunday. The bank closes its day at 23:30 America/Mexico_City and Compago closes the hold at 23:00, 30 minutes earlier, so the last minute to capture is 22:59 on the last day. It keeps being reported after the release, because it is a fact about the payment.
"2026-08-19T05:00:00.000Z"
Whether the hold window has closed. It flips at 23:00 America/Mexico_City on the last natural day, not 7 times 24 hours after heldAt.
Null once the hold is released: it is only reported while the payment is HELD.