Skip to main content
Once a customer has saved their card and the payment method status is ACTIVE, you can charge it at any time by calling the charge endpoint. No customer interaction is required. The charge is processed entirely server-side.

Overview

Charging a saved card allows you to:
  • Bill customers for subscriptions, memberships, or recurring services
  • Charge after service delivery (on-demand services, freight, freelance work)
  • Process payments when orders ship or milestones are reached

Prerequisites

Before charging a saved card:
  • The payment method must have a status of ACTIVE
  • You must have the payment method id (returned when creating the payment method)
  • The card must not be expired or revoked

Creating a Charge

These examples call the production host. While you build, replace https://api-harmony.compago.com with https://demo-api-harmony.compago.com and use a key from the Demo dashboard. See Environments.

Endpoint

API reference: POST /payment-method/{id}/payment

Authentication

Include your API key in the request headers:

Path Parameters

string
required
The unique identifier of the payment method to charge (UUID).

Request Body

Field Descriptions

Charging a card is unchanged. The contract you already use (amount, currency, description) is untouched, and holdFunds is optional and defaults to false, so a request that omits it charges the card immediately, exactly as it always has. Send holdFunds: true only when you want to hold funds instead of charging them.
holdFunds is the only name for this flag, and an unrecognized field is ignored rather than rejected. If you built against an earlier preview of the hold feature, check the name you send: there is no alias, so a request carrying any other spelling of this flag is treated as a request that set nothing, and the card is charged immediately instead of held. Confirm the response status is HELD before you treat a hold as placed.

Response

The response also carries the hold fields (heldAmount, heldAt, capturedAt, holdReleasedAt, holdExpiresAt, holdExpired, holdDaysRemaining). On a regular charge they are null, with holdExpired set to false. They are only populated on holds, and are documented in Hold Funds.

Code Examples

Checking Payment Status

After creating a charge, you can check its status at any time:
API reference: GET /payment-method/{id}/payment/{paymentId}

Idempotency (externalId)

POST /api/payment-method/{id}/payment accepts an optional externalId: your own key for this operation, up to 255 characters, unique across all payments in your organization. It is the supported way to make this endpoint safe to retry, and we recommend sending one on every charge. Without it, a retry after a timeout charges the card a second time. A network error tells you nothing about whether the bank approved the first attempt, so a blind retry of a 500MXNchargecantake500 MXN charge can take 1,000 MXN. With an externalId, the retry returns the payment you already made.
This is not the payment method externalId. They are two different fields, one screen apart in these docs:
  • The payment method externalId, sent to POST /api/payment-method and documented in Manage Payment Methods, is your reference for the saved card or customer.
  • The payment externalId, sent to POST /api/payment-method/{id}/payment and documented here, is your idempotency key for one charge or hold.
They live in separate namespaces, so the same string may safely be used for both. They are never compared with each other.

Replay Behaviour

A declined attempt spends the externalId. This surprises people, so it is worth stating plainly: if the bank declines the charge, replaying the same externalId returns that decline again, it does not try the card a second time. To retry a declined charge you must send a new externalId, for example invoice-2025-01-8842-2.This is deliberate. An idempotency key names one attempt, not one intention. The endpoint cannot tell “my HTTP client retried” from “my operator pressed the button again”, and freeing the key on a decline would let a duplicate retry fire a second authorization at the issuer, which feeds their fraud heuristics and can produce a genuine double charge when the bank actually approved a request we recorded as declined.
Charges and holds share one externalId namespace, because both are created by this same endpoint. order-1 cannot exist as a charge and also as a hold. Replaying a key with a different holdFunds value than the original returns the original payment rather than an error, so keep the flag stable across your retries.
The key is echoed back as externalId on every payment response for a saved card (the charge itself, capture, release, refund, list and detail), so you can reconcile our payment id against your own order reference without keeping a mapping table.

Important Business Rules

Payment method must be ACTIVE. You can only charge payment methods with an ACTIVE status. Attempting to charge a PENDING, EXPIRED, or REVOKED payment method will result in a 400 error.
Card expiration. If the customer’s card has expired since it was saved, the charge will fail. In this case, you’ll need to create a new payment method and have the customer save an updated card.
Currency restriction. Only MXN (Mexican Peso) is currently supported. Charges in other currencies will be rejected.

Error Handling

Next Steps

Hold Funds

Hold funds on a card with holdFunds, then capture them (in full or in part) or release them within 7 natural days.

Process Refunds

Issue full refunds for charges made against saved cards.

Manage Payment Methods

List, retrieve, and revoke payment methods and view payment history.