curl --request POST \
--url https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/capture \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '{
"amount": 200
}'import requests
url = "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/capture"
payload = { "amount": 200 }
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({amount: 200})
};
fetch('https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/capture', 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}/capture",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount' => 200
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"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"
"strings"
"net/http"
"io"
)
func main() {
url := "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/capture"
payload := strings.NewReader("{\n \"amount\": 200\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
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}/capture")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 200\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/capture")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 200\n}"
response = http.request(request)
puts response.read_body{
"id": "11111111-2222-3333-4444-555555555555",
"status": "CONFIRMED",
"amount": 123,
"currency": "MXN",
"heldAmount": 1200,
"externalId": "invoice-2025-01-8842",
"heldAt": "2026-08-12T10:00:00.000Z",
"capturedAt": "2026-08-14T09:30:00.000Z",
"holdReleasedAt": "2023-11-07T05:31:56Z",
"holdExpiresAt": "2026-08-19T05:00:00.000Z",
"holdExpired": true,
"holdDaysRemaining": 123
}Capture Hold
Captures a payment that is currently HELD, moving the held funds to the merchant. The request body is OPTIONAL: send no body to capture the full held amount, or send { "amount": 200 } to capture only part of it. The bounds are 0 < amount <= heldAmount; the 1 MXN minimum that applies to a charge does not apply to a capture. A successful capture leaves the payment CONFIRMED with capturedAt set, amount rewritten to the amount actually captured, and heldAmount still carrying the amount that was authorized. TWO RULES THAT SURPRISE INTEGRATORS: (1) there is exactly ONE capture per hold, so you cannot capture 200 now and the remaining 1000 later, and a second capture answers 409 PAYMENT_NOT_HELD; (2) Compago issues NO release for the difference on a partial capture, the cardholder’s issuing bank frees it on its own schedule, usually within a few days. Capture is time-gated: it is refused from 23:00 America/Mexico_City on the last natural day of the hold window. 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}/capture \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '{
"amount": 200
}'import requests
url = "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/capture"
payload = { "amount": 200 }
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({amount: 200})
};
fetch('https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/capture', 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}/capture",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount' => 200
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"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"
"strings"
"net/http"
"io"
)
func main() {
url := "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/capture"
payload := strings.NewReader("{\n \"amount\": 200\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
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}/capture")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 200\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://demo-api-harmony.compago.com/api/payment-method/{id}/payment/{paymentId}/capture")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 200\n}"
response = http.request(request)
puts response.read_body{
"id": "11111111-2222-3333-4444-555555555555",
"status": "CONFIRMED",
"amount": 123,
"currency": "MXN",
"heldAmount": 1200,
"externalId": "invoice-2025-01-8842",
"heldAt": "2026-08-12T10:00:00.000Z",
"capturedAt": "2026-08-14T09:30:00.000Z",
"holdReleasedAt": "2023-11-07T05:31:56Z",
"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 capture
Body
Optional. Omit the body entirely to capture the full held amount.
Optional body for capturing a hold. Omit the body, or omit amount, to capture the full held amount. Sending amount equal to heldAmount is the same as sending no body.
How much of the hold to capture, in MXN. Must be greater than 0 and no greater than the payment's heldAmount; 400 INVALID_CAPTURE_AMOUNT and 400 CAPTURE_AMOUNT_EXCEEDS_HOLD respectively. The 1 MXN minimum that applies to a charge does NOT apply here, so small captures are accepted. Remember that there is only one capture per hold and that Compago issues no release for the difference.
200
Response
Hold captured successfully. On a partial capture amount is what was captured and heldAmount is what had been authorized.
Response after capturing a held payment, in full or in part. amount is what was captured; heldAmount is what had been authorized.
Unique identifier of the payment
"11111111-2222-3333-4444-555555555555"
Payment status after a successful capture
CONFIRMED "CONFIRMED"
The amount actually captured. It is the full held amount unless you sent a smaller amount in the request body, in which case it is that amount.
"MXN"
What the bank authorized when the hold was placed. It is never rewritten, so on a partial capture amount is what was captured and this is what had been held: capturing 200 of a 1200 hold reports amount 200 and heldAmount 1200. Compago issues no release for the 1000 difference; the cardholder's issuing bank frees it on its own schedule.
1200
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"
When the hold was captured. This, and not createdAt, is the settlement date of a captured hold.
"2026-08-14T09:30:00.000Z"
Null on a captured hold: captured funds are never released.
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 capture, 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 payment is captured: it is only reported while the payment is HELD.