curl --request POST \
--url https://demo-api-harmony.compago.com/api/payment-method/{id}/payment \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"amount": 500,
"currency": "MXN",
"description": "Monthly subscription - January 2025",
"holdFunds": false,
"externalId": "invoice-2025-01-8842"
}
'import requests
url = "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment"
payload = {
"amount": 500,
"currency": "MXN",
"description": "Monthly subscription - January 2025",
"holdFunds": False,
"externalId": "invoice-2025-01-8842"
}
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: 500,
currency: 'MXN',
description: 'Monthly subscription - January 2025',
holdFunds: false,
externalId: 'invoice-2025-01-8842'
})
};
fetch('https://demo-api-harmony.compago.com/api/payment-method/{id}/payment', 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",
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' => 500,
'currency' => 'MXN',
'description' => 'Monthly subscription - January 2025',
'holdFunds' => false,
'externalId' => 'invoice-2025-01-8842'
]),
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"
payload := strings.NewReader("{\n \"amount\": 500,\n \"currency\": \"MXN\",\n \"description\": \"Monthly subscription - January 2025\",\n \"holdFunds\": false,\n \"externalId\": \"invoice-2025-01-8842\"\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")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 500,\n \"currency\": \"MXN\",\n \"description\": \"Monthly subscription - January 2025\",\n \"holdFunds\": false,\n \"externalId\": \"invoice-2025-01-8842\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://demo-api-harmony.compago.com/api/payment-method/{id}/payment")
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\": 500,\n \"currency\": \"MXN\",\n \"description\": \"Monthly subscription - January 2025\",\n \"holdFunds\": false,\n \"externalId\": \"invoice-2025-01-8842\"\n}"
response = http.request(request)
puts response.read_body{
"id": "11111111-2222-3333-4444-555555555555",
"status": "CONFIRMED",
"amount": 123,
"currency": "MXN",
"heldAmount": null,
"externalId": "invoice-2025-01-8842",
"heldAt": "2026-08-12T10:00:00.000Z",
"capturedAt": "2023-11-07T05:31:56Z",
"holdReleasedAt": "2023-11-07T05:31:56Z",
"holdExpiresAt": "2026-08-19T05:00:00.000Z",
"holdExpired": true,
"holdDaysRemaining": 7
}Charge Method
curl --request POST \
--url https://demo-api-harmony.compago.com/api/payment-method/{id}/payment \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"amount": 500,
"currency": "MXN",
"description": "Monthly subscription - January 2025",
"holdFunds": false,
"externalId": "invoice-2025-01-8842"
}
'import requests
url = "https://demo-api-harmony.compago.com/api/payment-method/{id}/payment"
payload = {
"amount": 500,
"currency": "MXN",
"description": "Monthly subscription - January 2025",
"holdFunds": False,
"externalId": "invoice-2025-01-8842"
}
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: 500,
currency: 'MXN',
description: 'Monthly subscription - January 2025',
holdFunds: false,
externalId: 'invoice-2025-01-8842'
})
};
fetch('https://demo-api-harmony.compago.com/api/payment-method/{id}/payment', 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",
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' => 500,
'currency' => 'MXN',
'description' => 'Monthly subscription - January 2025',
'holdFunds' => false,
'externalId' => 'invoice-2025-01-8842'
]),
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"
payload := strings.NewReader("{\n \"amount\": 500,\n \"currency\": \"MXN\",\n \"description\": \"Monthly subscription - January 2025\",\n \"holdFunds\": false,\n \"externalId\": \"invoice-2025-01-8842\"\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")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 500,\n \"currency\": \"MXN\",\n \"description\": \"Monthly subscription - January 2025\",\n \"holdFunds\": false,\n \"externalId\": \"invoice-2025-01-8842\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://demo-api-harmony.compago.com/api/payment-method/{id}/payment")
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\": 500,\n \"currency\": \"MXN\",\n \"description\": \"Monthly subscription - January 2025\",\n \"holdFunds\": false,\n \"externalId\": \"invoice-2025-01-8842\"\n}"
response = http.request(request)
puts response.read_body{
"id": "11111111-2222-3333-4444-555555555555",
"status": "CONFIRMED",
"amount": 123,
"currency": "MXN",
"heldAmount": null,
"externalId": "invoice-2025-01-8842",
"heldAt": "2026-08-12T10:00:00.000Z",
"capturedAt": "2023-11-07T05:31:56Z",
"holdReleasedAt": "2023-11-07T05:31:56Z",
"holdExpiresAt": "2026-08-19T05:00:00.000Z",
"holdExpired": true,
"holdDaysRemaining": 7
}Authorizations
Path Parameters
The unique identifier of the payment method to charge
Body
Request body for charging a saved payment method
The amount to charge in MXN. Must be a positive number.
x >= 0.01500
Currency code. Defaults to MXN.
MXN "MXN"
A description of the charge.
"Monthly subscription - January 2025"
When true the funds are only authorized and held, not captured. The payment comes back as HELD, with heldAmount set to the authorized amount, and the money reaches you only when you call the capture endpoint, which can take the full held amount or part of it. Call the release endpoint to give the funds back, otherwise the hold is released automatically at the end of the 7th natural day. 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.
false
Optional idempotency key for this charge or hold. Your own reference, up to 255 characters, unique across all payments in your organization. Repeating a request with the same value returns the ORIGINAL payment, with the HTTP status the original attempt produced, instead of charging or holding a second time. A DECLINED attempt SPENDS the key: replaying it returns the decline again, so retrying a declined charge or hold requires a NEW externalId. Reusing a key with a different payment method, amount or currency answers 409 EXTERNAL_ID_MISMATCH. An empty string is treated as not supplied. This is NOT the payment method externalId, which identifies the saved card: they are separate namespaces and are never compared with each other.
255"invoice-2025-01-8842"
Response
Payment created successfully. When holdFunds is true the payment comes back with status HELD, heldAmount set to the authorized amount, and the funds held rather than captured. When the request carried an externalId that was already used, this is the ORIGINAL payment replayed rather than a new one.
Response after charging a payment method, or after placing a hold when holdFunds was true
Unique identifier of the payment
"11111111-2222-3333-4444-555555555555"
Current status of the payment. A successful hold returns HELD.
NOT_INITIALIZED, PENDING, CONFIRMED, CANCELLED, REFUNDED, HELD, HOLD_RELEASED, HOLD_EXPIRED "CONFIRMED"
The money that moved, or that will move. On a hold it equals heldAmount until the hold is captured; after a partial capture it is the amount actually captured.
"MXN"
The amount the bank authorized when the hold was placed. Stamped once and never rewritten, so it stays the record of what was blocked on the card even after a partial capture. Null on any payment that was never held.
null
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. Null on payments that were never held.
"2026-08-12T10:00:00.000Z"
When the hold was captured. This, and not createdAt, is the settlement date of a captured hold. Null until the hold is captured.
When the hold was released, either by the release endpoint or by the automatic expiry. Null otherwise.
When the hold expires: 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 once the hold has ended, because it is a fact about the payment. Null on payments that were never held.
"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. False on payments that were never held.
Natural days on which the hold can still be captured, including today when today's 23:00 cutoff has not passed yet: 7 for a hold placed before 23:00 on its placement day, 6 for one placed at or after 23:00 (its placement day is already spent), 1 through the last day, and 0 from 23:00 on the last day. Null unless the payment is currently HELD.
7