Skip to content
ESENGo to the Portal

Create charges

How to create charges one by one or in bulk, with due dates, surcharge, states, payer notifications and safe retries.

This page covers how to create, track and cancel charges, one at a time or up to 100 per call. You need a connected merchant account and its id_token in Authorization: Bearer: see Connect merchant accounts.

How a payment request moves from one state to another.

  • draft becomes pending when you set the amount with PATCH.
  • pending becomes total_payment with a full payment, partial_payment with a partial payment, and overdue if it expires unpaid.
  • partial_payment becomes total_payment when the rest is paid, and partial_overdue if it expires first.
  • draft and pending become canceled when you cancel them, if they received no payment.

1Create a charge

You identify each charge with your own referenceId, which comes back in every response and event.

From your backend, create the charge in Sandbox:

cURL

curl -X POST https://integration-api.test.chytapay.com.ar/payment-request \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "referenceId": "invoice-1001",
    "amount": 25000,
    "description": "October installment",
    "dueDates": ["2026-10-15", "2026-10-25"],
    "surcharge": { "type": "percentage", "value": 10 },
    "conciliationType": "cuil",
    "sendWhatsappNotification": false,
    "sendEmailNotification": true,
    "customer": {
      "name": "Jane Doe",
      "email": "[email protected]",
      "taxDocument": "20000000001"
    },
    "metadata": { "order": "1001" }
  }'

Node

const BASE = 'https://integration-api.test.chytapay.com.ar';

const res = await fetch(`${BASE}/payment-request`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${idToken}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    referenceId: 'invoice-1001',
    amount: 25000,
    description: 'October installment',
    dueDates: ['2026-10-15', '2026-10-25'],
    surcharge: { type: 'percentage', value: 10 },
    conciliationType: 'cuil',
    sendWhatsappNotification: false,
    sendEmailNotification: true,
    customer: {
      name: 'Jane Doe',
      email: '[email protected]',
      taxDocument: '20000000001',
    },
    metadata: { order: '1001' },
  }),
});
const charge = await res.json(); // 201

Python

import requests

BASE = "https://integration-api.test.chytapay.com.ar"

res = requests.post(
    f"{BASE}/payment-request",
    headers={"Authorization": f"Bearer {id_token}"},
    json={
        "referenceId": "invoice-1001",
        "amount": 25000,
        "description": "October installment",
        "dueDates": ["2026-10-15", "2026-10-25"],
        "surcharge": {"type": "percentage", "value": 10},
        "conciliationType": "cuil",
        "sendWhatsappNotification": False,
        "sendEmailNotification": True,
        "customer": {
            "name": "Jane Doe",
            "email": "[email protected]",
            "taxDocument": "20000000001",
        },
        "metadata": {"order": "1001"},
    },
)
charge = res.json()  # 201

Response

{
  "type": "standard",
  "paymentRequestId": "<charge uuid>",
  "referenceId": "invoice-1001",
  "amount": 25000,
  "description": "October installment",
  "dueDates": ["2026-10-15", "2026-10-25"],
  "state": "pending",
  "surcharge": { "type": "percentage", "value": 10 },
  "calculatedAmounts": [
    { "dueDate": "2026-10-15", "amount": 25000, "isOriginalAmount": true },
    { "dueDate": "2026-10-25", "amount": 27500, "surchargeApplied": 2500 }
  ],
  "sendWhatsappNotification": false,
  "sendEmailNotification": true,
  "metadata": { "order": "1001" },
  "customer": {
    "name": "Jane Doe",
    "email": "[email protected]",
    "taxDocument": "20000000001"
  },
  "CVU": "<22-digit CVU>",
  "bankAccountInfo": {
    "accountHolderName": "<merchant account holder>",
    "accountHolderTaxId": "<holder's CUIT>",
    "bankName": "<bank>"
  }
}

Use dates within the next 35 days. The example reconciles by CUIL, the recommended mode: first, validate the document.

What it returns

201 with paymentRequestId, state (pending, or draft without an amount), CVU and bankAccountInfo (where the payer transfers) and, with an amount and 2 dates, calculatedAmounts (each due date with surcharge). It also returns what you sent, except customer.taxDocument in CVU mode.

2Retrieve a charge

Request it with GET /payment-request/{referenceId}:

cURL
curl https://integration-api.test.chytapay.com.ar/payment-request/invoice-1001 \
  -H "Authorization: Bearer $ID_TOKEN"

Same shape as the create response, with an updated state. If it doesn't exist for the merchant account, 404. To list the charges created in a range, use GET /payment-request?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD, both dates optional.

3Duplicates and retries

There's no idempotency key: the referenceId is unique per integration and merchant account, canceled charges included. Repeating it returns 409 without the existing charge:

JSON
{
  "error": "ReferenceIdAlreadyExists",
  "message": "The provided reference ID already exists for this client",
  "referenceId": "invoice-1001"
}

Use your entity's id (invoice, order, installment), not one per attempt. If a create times out:

  1. Call GET /payment-request/{referenceId}. If it returns 200, the charge was created: use that response.
  2. If it returns 404, retry the POST with the same body and referenceId.
  3. If the POST returns 409, another attempt created it: go back to step 1.

If the charge from the GET doesn't match what you meant to create, you reused an old referenceId by mistake.

4Cancel a charge

Call POST /payment-request/{referenceId}/cancel with no body:

cURL

curl -X POST https://integration-api.test.chytapay.com.ar/payment-request/invoice-1001/cancel \
  -H "Authorization: Bearer $ID_TOKEN"

Response

{ "referenceId": "invoice-1001", "state": "canceled" }

Only charges in draft or pending with no payments can be canceled: otherwise, 409 PaymentRequestCannotBeCanceled. It generates no event: the 200 response is the confirmation. If a transfer comes in later, you get payment.unmatched with reason: "canceled", which is opt-in.

Fields

The fields of a standard charge. Omit the optional fields you don't use; don't send null.

Field Required Rules
referenceId Yes 8 to 100 characters: letters, numbers, - and _. See duplicates.
description Yes What the payer sees. 3 to 500 characters.
dueDates Yes 1 or 2 dates: see due dates.
customer Yes The payer (table below).
amount No Pesos, greater than 0, up to 2 decimals, cap of 200,000,000. Without it, a draft.
surcharge With amount and 2 dates { "type", "value" } for the second due date. Not allowed without amount.
sendWhatsappNotification With amount WhatsApp notifications. Without an amount, only false.
sendEmailNotification With amount Email notifications. Without an amount, only false.
sendWhatsappPaymentConfirmation No See payment confirmation.
reminderNotificationDate No YYYY-MM-DD. Replaces the advance reminder. Only with an amount.
deferredInitialNotificationDate No YYYY-MM-DD. Defers the initial notification. Only with an amount.
conciliationType No cvu (default) or cuil, the recommended one.
webhookUrl No HTTPS, up to 2048 characters. Overrides the webhook URL for this charge.
metadata No Strings: up to 50 keys of 40 characters, values of 500, 8 KB. Returned in the GET and in events.
customer field Required Rules
name Yes 2 to 150 characters.
taxDocument With conciliationType: "cuil" 11-digit CUIL or 7- or 8-digit DNI. Ignored in CVU mode.
email With sendEmailNotification: true Valid, up to 320 characters.
phoneNumber With sendWhatsappNotification: true { "countryCode": "+54", "number": "..." }, no spaces, 6 to 15 digits.

Outside these rules, 400 with the details: see the errors.

Due dates and surcharge

dueDates takes 1 or 2 dates, after today and no more than 35 days out:

Format Example Due
YYYY-MM-DD "2026-10-15" At the end of that day, Argentina time.
YYYY-MM-DDTHH:MM[:SS] "2026-10-15T18:30" At that minute. Without a zone it's -03:00; accepts Z or an offset.

With a time, the charge is short-lived. With 2 dates, the second element of dueDates carries surcharge, required with an amount (Chytapay doesn't reorder them). The payer owes amount up to the first due date and amount plus the surcharge from the day after.

type is percentage (0 to 100, on the amount), fixed (pesos, up to 200,000,000) or none (value: 0). With amount: 25000 and { "type": "percentage", "value": 10 }, the second due date is 27,500, in calculatedAmounts.

Short-lived charges

For reservations, tickets or any charge that has to close at a set time, send the due date with a time: "2026-10-15T18:30". It changes several things compared to a date alone:

Date alone With a time
Expires At the end of that day, Argentina time At that minute. Without a zone it's -03:00
Due dates 1 or 2, with a surcharge on the second A single one, no surcharge
Event on expiry None payment_request.expired
Payer notifications Reminder one day before With less than 24 hours there's no advance reminder: the initial and due-date ones remain
In bulk Yes No, it returns 400

If the charge is paid in the widget, the checkout charge expires between 3 and 60 minutes.

Draft without an amount

Without amount, the charge starts in draft, with one due date, no surcharge and no notifications (flags only false, notification dates not allowed). Complete it with PATCH /payment-request/{referenceId}:

cURL

curl -X PATCH https://integration-api.test.chytapay.com.ar/payment-request/invoice-1002 \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 18000 }'

Response

{
  "referenceId": "invoice-1002",
  "previousAmount": null,
  "amount": 18000,
  "description": "Enrollment",
  "state": "pending",
  "dueDates": ["2026-10-20"]
}
  • Only in draft with no payments; otherwise, 409.
  • It moves to pending. A draft never sends notifications, not even after the PATCH.
  • If you don't complete it, Chytapay discards it after a few hours.

States

Create, GET, cancel, PATCH and bulk return state, and payment_request.* events return stateType. There's no PAID: it's total_payment.

State Meaning
draft Draft without an amount.
pending Waiting for payment.
partial_payment Partially paid, balance left.
total_payment Paid.
overdue Expired unpaid.
partial_overdue Expired partially paid.
canceled Canceled.

With 2 dates, the charge stays open until the second due date. A day-only date expires at 00:00 Argentina time, with no event; one with a time expires at that minute with payment_request.expired. See the events in Webhooks.

Payer notifications

A payer notification is a WhatsApp or email from Chytapay to the payer (not an event to your backend). Channels are chosen in the fields, only with an amount. After a partial payment, reminders show the balance; paid, past due or canceled, they stop.

When each notification goes out

Reminders go out at 10 a.m. Argentina time.

See the notification schedule
Notification When Amount
Initial On creation, or on deferredInitialNotificationDate First due date, no surcharge
Advance reminder The day before the first due date, or on reminderNotificationDate First due date, no surcharge
Due date That day First due date, no surcharge
First due date passed (2 dates) The day after Second due date, with surcharge
Second due date (2 dates) That day Second due date, with surcharge

Both dates must be after today and before the first due date, and reminderNotificationDate also after deferredInitialNotificationDate.

Payment confirmation

The WhatsApp payment received notification is controlled by sendWhatsappPaymentConfirmation, not sendWhatsappNotification:

Value CVU mode CUIL mode
Omitted Sent, on full and partial payments Not sent
true Sent, on full and partial payments Only on full payment
false Not sent Not sent

In CUIL mode the payer can request it with "Recibir confirmación de pago" (Receive payment confirmation), and false doesn't turn that off.

Create charges in bulk

POST /payment-request/bulk creates 1 to 100 charges, all or nothing. Each item is a single charge, except that:

  • It always reconciles by CUIL: customer.taxDocument is required.
  • The same document twice in a batch returns 422.
  • A date with a time returns 400.
  • conciliationType, type, expiresInMinutes and webhookUrl aren't allowed: events go to the integration's URL.

From your backend, send the batch:

cURL

curl -X POST https://integration-api.test.chytapay.com.ar/payment-request/bulk \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentRequests": [
      {
        "referenceId": "installment-2026-10-001",
        "amount": 12000,
        "description": "October installment",
        "dueDates": ["2026-10-15"],
        "sendWhatsappNotification": false,
        "sendEmailNotification": false,
        "customer": { "name": "Jane Doe", "taxDocument": "20000000001" }
      },
      {
        "referenceId": "installment-2026-10-002",
        "amount": 12000,
        "description": "October installment",
        "dueDates": ["2026-10-15"],
        "sendWhatsappNotification": false,
        "sendEmailNotification": false,
        "customer": { "name": "John Smith", "taxDocument": "30405060" }
      }
    ]
  }'

Response

{
  "batchId": "<batch uuid>",
  "total": 2,
  "created": 2,
  "sharedCvu": "<22-digit CVU>",
  "accountInfo": {
    "accountHolderName": "<merchant account holder>",
    "accountHolderTaxId": "<holder's CUIT>",
    "bankName": "<bank>"
  },
  "paymentRequests": [
    { "referenceId": "installment-2026-10-001", "state": "pending" },
    { "referenceId": "installment-2026-10-002", "state": "pending" }
  ]
}

paymentRequests follows the request order; retrieve details with GET.

See the bulk responses
Code When
201 All created: store batchId.
400 Empty array, more than 100 items or a field outside the contract.
422 errors: [{ index, referenceId, reason }] with every failed item: fix them and resend the batch.
429 The batch doesn't fit entirely under the WhatsApp message cap: nothing is created. See the limits.

With 400, 422 or 429 nothing is created. Some reason values: Duplicate referenceId within the batch, The referenceId already exists for this integration, The taxDocument does not resolve to a valid CUIL and Duplicate payer within the batch: ....

sendWhatsappPaymentConfirmation applies to the whole batch and, if items differ, false wins. Notification channels work the other way: if any item turns on WhatsApp or email, it's on for the batch.

Checkout charge

A type: "checkout" charge is paid from the widget: see the checkout charge.

Next steps