Get started

Create your first charge

Create a charge with the API, send your customer to checkout and confirm the payment by webhook. In five minutes.

1Get an API key

Create a Secret, Live key with the orders:write scope (and orders:read if you'll retrieve charges). The steps are in Authentication.

Terminal
Shell
# Keep it in a server environment variableexport TUCAPI_KEY=tcb_sk_live_…

2Create the charge

From your server, call POST /v1/charges. Only amount is required.

  • amountstringRequired
    Amount in USD. Greater than 0, up to 2 decimals and up to 1,000,000. Send it as a string: "125.00".
  • descriptionstringOptional
    What your customer sees at checkout. Up to 200 characters.
  • referencestringOptional
    Your order or invoice number. Up to 120 characters. Unique per merchant.
  • metadataobjectOptional
    Your own data. Up to 20 keys and 4 KB. Returned as is.
  • allowedMethodsstring[]Optional
    paypal, zelle, card. If you leave it out, every method you have enabled is offered.
  • expiresAtstring (date-time)Optional
    Up to one year. After that date, checkout stops accepting payments.

Note

Always send the Idempotency-Key header. If the connection drops and you retry, no second charge is created. More in Idempotency.
curl -X POST https://api.business.tucapi.com/v1/charges \  -H "Authorization: Bearer $TUCAPI_KEY" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: ORD-1042" \  -d '{    "amount": "125.00",    "description": "Pedido #1042",    "reference": "ORD-1042",    "metadata": {      "customerId": "c_88"    },    "allowedMethods": [      "paypal",      "card"    ],    "expiresAt": "2026-09-16T12:00:00Z"  }'
Response
{  "object": "charge",  "id": "9b1e2d3c-4f5a-4b6c-8d7e-0f1a2b3c4d5e",  "status": "open",  "amount": "125.00",  "currency": "USD",  "description": "Pedido #1042",  "reference": "ORD-1042",  "metadata": {    "customerId": "c_88"  },  "allowedMethods": [    "paypal",    "card"  ],  "checkoutUrl": "https://pay.tucapi.com/k7x2m9qz",  "embedUrl": "https://pay.tucapi.com/embed/k7x2m9qz",  "expiresAt": "2026-09-16T12:00:00Z",  "paidAt": null,  "createdAt": "2026-09-15T18:42:07Z",  "orders": []}

3Send your customer to checkout

Redirect your customer to checkoutUrl. You can also send them the link by WhatsApp or email.

There they pick a payment method from allowedMethods.

  • A charge can be paid once: it has at most one paid order.
  • Want the payment to happen inside your site? Use embedUrl with the embedded checkout.
// Expressapp.post("/checkout", async (req, res) => {  const charge = await createCharge(req.body); // POST /v1/charges  res.redirect(303, charge.checkoutUrl);});

4Confirm the payment by webhook

In the dashboard, go to Webhooks and create an endpoint subscribed to order.paid.

When the payment is confirmed, you get a signed POST. Your endpoint should:

  1. 1Verify the signature in the TuCapi-Signature header.
  2. 2Skip the event if you've already processed that id.
  3. 3Answer 2xx within 10 seconds.

order.paid doesn't include the charge id

It includes orderId. Call GET /v1/orders/{orderId}: the order has chargeId. The order.created event does include the charge, as paymentLinkId.

Details in Receive webhooks.

// Express: use the RAW body, not parsed JSONapp.post(  "/webhooks/tucapi",  express.raw({ type: "application/json" }),  (req, res) => {    try {      verifyTuCapi(req.body, req.get("TuCapi-Signature"), process.env.TUCAPI_WEBHOOK_SECRET);    } catch {      return res.sendStatus(400);    }    const event = JSON.parse(req.body);    if (alreadyProcessed(event.id)) return res.sendStatus(200); // dedupe​    if (event.type === "order.paid") {      // order.paid has no charge id; the order does (chargeId).      // Queue it and call GET /v1/orders/{orderId} later.      queuePayment(event.data.orderId);    }    res.sendStatus(200); // answer fast; process later  },);

5Retrieve the charge

Can't receive webhooks? Retrieve the charge. Its status changes to paid when one of its orders is confirmed.

Note

Don't poll nonstop: the limit is 300 reads per minute per key.
curl "https://api.business.tucapi.com/v1/charges/9b1e2d3c-4f5a-4b6c-8d7e-0f1a2b3c4d5e" \  -H "Authorization: Bearer $TUCAPI_KEY"
Response
{  "object": "charge",  "id": "9b1e2d3c-4f5a-4b6c-8d7e-0f1a2b3c4d5e",  "status": "paid",  "amount": "125.00",  "currency": "USD",  "description": "Pedido #1042",  "reference": "ORD-1042",  "metadata": {    "customerId": "c_88"  },  "allowedMethods": [    "paypal",    "card"  ],  "checkoutUrl": "https://pay.tucapi.com/k7x2m9qz",  "embedUrl": "https://pay.tucapi.com/embed/k7x2m9qz",  "expiresAt": "2026-09-16T12:00:00Z",  "paidAt": "2026-09-15T18:50:31Z",  "createdAt": "2026-09-15T18:42:07Z",  "orders": [    {      "object": "order",      "id": "c9227f9b-13f4-4e9c-8fe0-4644e62b83cd",      "chargeId": "9b1e2d3c-4f5a-4b6c-8d7e-0f1a2b3c4d5e",      "merchantOrderRef": "MO-k7x2m9qz-1a2b3c4d",      "status": "paid",      "paymentMethod": "paypal",      "amount": "125.00",      "currency": "USD",      "fees": {        "rate": "0.035",        "fixed": "0.30"      },      "netAmountUsdt": "120.33",      "externalRef": "5O190127TN364715T",      "payer": {        "email": "ana@example.com",        "name": null      },      "expiresAt": "2026-09-15T19:12:07Z",      "paidAt": "2026-09-15T18:50:31Z",      "createdAt": "2026-09-15T18:42:07Z"    }  ]}

Before going live

  1. 1A live secret key with just the scopes you need, stored on the server.
  2. 2POST /v1/charges with Idempotency-Key and reference set to your order number.
  3. 3Redirect to checkoutUrl.
  4. 4A webhook endpoint that verifies the signature, dedupes by id and answers 2xx fast.
  5. 5On order.paid, mark the order as paid (find the charge through the order).
  6. 6Daily reconciliation with GET /v1/orders?status=paid.