Skip to main content
Some operations in this guide have Discovery handlers but no public gateway route yet: POST /payments/{id}/support-documents. Check the availability notice on each API reference page before using them.
Start with shared fields, then the Mexico or Colombia guide. The CFDI, RFC, SAT catalog, and payment-complement examples below describe Mexico; they are not universal country requirements.
Process, track, and manage payments with automatic invoice generation and Mexican tax compliance. The Payments API handles payment registration, processing, refunds, and automated CFDI creation.

Overview

The Payments API provides comprehensive payment management with flexible automation options. Register payments manually, create payment requests, process refunds, and automatically generate compliant invoices.

Key Features

  • Payment Registration - Record payments with optional invoice automation
  • Payment Requests - Create shareable payment links
  • Client Search - Find and update existing clients to prevent duplicates
  • Payment Splitting - Split payments between master and connect teams (marketplace)
  • Refund Processing - Full or partial refund support
  • Invoice Automation - Automatic PUE/PPD invoice creation
  • PPD Complement Linking - Link payments to existing PPD invoices for automatic complement generation
  • Multiple Processors - Support for various payment gateways
  • Status Tracking - Real-time payment status updates
  • Idempotency - Prevent duplicate payment processing

Endpoints

List Payments

Retrieve a paginated list of payments with powerful filtering capabilities. Query Parameters: Metadata Filtering: You can filter payments by any metadata key using dot notation or underscore notation:
Example Request:
Example with Filters:
Example Response:

Search Payments

Full-text search across payments using Typesense. This endpoint provides fast, typo-tolerant search capabilities. Query Parameters: Example Request:
Example Response:

Register Payment

Register a payment that has already been received. This endpoint marks the payment as ‘succeeded’ immediately and is used for recording payments that have already been completed through external means (bank transfers, cash, etc.). Key Features:
  • Payment is marked as ‘succeeded’ immediately
  • Requires payment_form field (Mexican SAT payment form code)
  • Optional invoice automation
  • Client search to prevent duplicate client records
  • Used for payments already received
Automation Types:
  • pue_invoice - Create PUE (Pago en Una sola Exhibición) invoice immediately
  • none - No automation, register payment only
Request Body:
Optional Fields:
  • idempotency_key (string) - Stable key for one business payment. A repeated registration returns 400 resource_conflict with the existing payment ID in the error details; retrieve and reconcile that payment. Keep the same key on retries. See the tested payment recipe.
  • date (number) - Unix timestamp (in milliseconds) for when the payment was received. Must be in the past. Defaults to current time if not provided.
  • exchange_rate (number) - Exchange rate for currency conversion. If not provided, the rate from the payment date (or current date if no date specified) will be fetched automatically from our rates collection.
  • ppd_invoice_id (string) - UUID of an existing PPD invoice to link this payment to. When provided, a payment complement (complemento de pago) will be automatically generated and linked to the PPD invoice. The referenced invoice must have payment_method='PPD', status='valid' and invoice_type='I' — a payment complement only ever settles an income CFDI, never an egress one.
Payment Form Codes:
  • 01 - Cash
  • 02 - Check
  • 03 - Electronic transfer
  • 04 - Credit card
  • 05 - Electronic money
  • 06 - Digital money
  • 99 - To be defined
Client Search Feature: The search parameter enables upsert-like behavior to find existing clients before creating payments, helping to avoid duplicate client records:
  • on_key (string, required) - Field to search on (e.g., ‘tax_id’, ‘email’, ‘name’)
  • on_value (string, required) - Value to match against the specified field
  • update (boolean, optional) - If true and a match is found, update the existing client with the provided data. If false, return the existing client without modifications. Default: false
Search Behavior:
  • If a single client matches the search criteria, that client is used for the payment
  • If update: true, the matched client is updated with any new data provided in the request
  • If multiple clients match the search criteria, a 409 Conflict error is returned to prevent ambiguity
  • If no client matches and client data is provided, a new client is created automatically
Example with Client Search:

Request Payment

Create a payment request that generates a shareable payment link. This endpoint creates a payment in ‘requires_payment_method’ status and provides customers with various payment options to complete the transaction. Key Features:
  • Payment is created with ‘requires_payment_method’ status
  • Generates shareable payment link
  • Supports multiple payment methods
  • Optional email notifications
  • Used for requesting future payments
  • Optionally returns the payer to your site after paying (success_url)
Payment Methods:
  • card - Credit/debit card payments (requires Stripe integration)
  • bank - Mexican bank transfer (SPEI)
  • oxxo - OXXO convenience store payments (requires Stripe integration)
  • stripe-spei - Customer balance payments (requires Stripe integration)
The selected processor determines which methods are accepted. The default processor is stripe; other supported processors have their own integrations and method lists. Use the request-payment contract for the current processor/method combinations. In particular, a payment method being valid in the shared enum does not make it valid for every processor. Request Body:

Returning the payer to your site (success_url)

By default the hosted payment page is where the journey ends. For a shop checkout that is a dead end: the customer pays and is left on a page with no order reference and no way back, which reads as a failed purchase and leads to duplicate payments. Set success_url to your order confirmation page and gigstack sends them back:
The payer sees the destination host and is redirected a few seconds after the payment is confirmed, and can also return immediately with a button. For asynchronous methods (SPEI, OXXO) confirmation may arrive after the payer has closed the page, so treat your webhook — not this redirect — as the source of truth for whether a payment succeeded. The value is validated on write. A 400 is returned unless the URL:
  • uses https
  • carries no credentials (https://user:pass@host)
  • contains no whitespace or control characters
  • resolves to a fully qualified, publicly reachable host — loopback, private and link-local addresses are rejected
  • is at most 2048 characters
It is returned as success_url on payment reads, and is accepted only on POST /payments/request. /payments/register records payments that already settled, where there is no payer to redirect. Example Request:
Response with Payment Link:

Get Payment

Retrieve a specific payment by ID. Example Request:

Mark Payment as Paid

Mark a pending payment as paid. Example Request:

Cancel Payment

Cancel a payment request. Example Request:

Refund Payment

Choose whether you are recording money already returned outside gigstack or asking an eligible Stripe payment to be refunded. The default only records the refund. The endpoint does not directly cancel an invoice or create a credit note. Recording a refund can trigger published refund Journeys and the team’s automatic refund handling, which may act on associated fiscal documents. Check those settings before deciding whether a separate fiscal action is needed. Do not send items: it is not an accepted input. The payment must be succeeded. The cumulative refunds cannot exceed the payment amount. Use a test key and a payment created in that mode for a test run; substituting a live payment ID is not a test. The original payment’s automation_type: "none" does not disable refund Journeys or team automatic refund handling. Test mode can still run published test Journeys and deliver processor webhooks or team notifications. Before testing, check the account’s refund settings, webhook destinations and recipients; see the refund walkthrough.

1. Read the payment and its prior refunds

Use GIGSTACK_BASE_URL and GIGSTACK_API_KEY from the quickstart, and set PAYMENT_ID to the intended payment. The examples require Bash, curl, and jq.
Check the units: data.total is in currency units, but the current data.total_refunded is in minor units (100 minor units per currency unit in this handler). Each refunds[].total is in currency units. For a payment of 1160 with total_refunded: 50000, the already-refunded amount is 500 and the remaining amount is 660. Do not subtract total_refunded directly from total.

2. Record an external refund

Run this only after your bank transfer or other external refund has completed. It records MXN 100 returned against a Mexican-peso example payment; it moves no money:
Expected contract: HTTP 200, a new data.refund.id, data.refund.total: 100, and data.refund.external_processor_refund: false. The nested data.payment.amount and data.payment.total_refunded in this response are both in minor units. An additional staging check exercised this record-only path with a refund of MXN 116 against a fresh MXN 1,160 payment and verified the returned refund and cumulative amount by reading the payment again. The MXN 100 variation shown here is source-checked; the refund walkthrough contains the exercised amount and test evidence.

3. Request a Stripe refund instead

This is an alternative to step 2 for a payment with a linked Stripe payment intent. Sending it after step 2 would create another refund. Choose the amount still owed:
A manually registered cash or bank payment has no Stripe charge to refund and is rejected when this flag is true. Do not infer support for other payment processors from their availability for checkout. A successful response records the refund after the Stripe call returns; verify the processor’s refund status before promising the customer that the money has reached their account.

4. Reconcile the result

Read GET /payments/{id} again and find the returned refund ID in data.refunds. Compare the increment in total_refunded with the requested amount multiplied by 100. For an external-processor refund, also reconcile the underlying Stripe payment. A later Stripe webhook can set partial_refunded or refunded and append a processor refund entry alongside the API-created entry. Do not count those entries as separate refunds or sum them to infer money returned; compare the cumulative amount with Stripe. There is no refund idempotency key in this API. Do not blindly repeat the POST following a timeout or 500: Stripe may have accepted the request before the local record was saved. Compare the before/after refund list and the processor record; if the outcome remains unknown, ask support to reconcile it before sending another refund. Serialise refund requests for the same payment in your application.

Support Documents

Attach and list evidence for a payment (proof of payment, payment confirmation…). Same fields, limits and response as invoice support documents.

Payment Structure

Payment Status

Payment Processors

  • stripe - Stripe payment gateway
  • mercado_pago - MercadoPago
  • paypal - PayPal
  • manual - Manual/bank transfer

Complex Payment Examples

Register Payment with Client Search and Update

This example demonstrates how to search for an existing client by tax ID and update their information if found, or create a new client if not found:
Key Points:
  • With update: true, if a client with tax_id “PEGJ800101ABC” exists, their email, phone, and address will be updated
  • If no client is found, a new client will be created with all the provided information
  • The search ensures you don’t create duplicate clients when processing recurring payments

Register Payment with Multiple Items

Register Payment with Withholding Taxes

Payment Request with Custom Invoice Config

Register USD Payment with Exchange Rate

Payment Splitting (Marketplace)

Split payments between a master team (platform) and a connect team (merchant) in marketplace scenarios. This is only available for master teams with marketplace-enabled billing accounts. Important: When using transfer_data, the team and livemode fields are automatically extracted from the authentication token. Developers do not need to send these fields in the request body.
Transfer Data Configuration:
  • master (number, 0-100) - Percentage of the payment for the master team
  • connect (string) - Tax ID (RFC) or Team ID of the connect team. If not found, a new team will be created
  • master_to (string: ‘client’ | ‘connect’) - Client assignment for master payment:
    • client: Use the original client from the request
    • connect: Create the connect team as a client for the master payment
  • connect_to (string: ‘client’ | ‘master’) - Client assignment for connect payment:
    • client: Use the original client from the request
    • master: Create the master team as a client for the connect payment
  • connect_custom_config (object, optional) - Customize items in the connect payment:
    • product_key (string) - SAT product key for connect payment items
    • unit_key (string) - SAT unit key for connect payment items
    • custom_description (string) - Custom description for connect payment items
    • custom_price (number) - Fixed amount for connect payment (overrides percentage calculation)
    • taxes (array) - Custom tax configuration for connect payment items
Split Payment Response:
When a new team is created: If the connect team doesn’t exist, the response includes an onboarding_url that can be sent to the merchant to complete their team setup.

Advanced: Split Payment with Custom Configuration

Fixed Commission Fee (Custom Price)

Instead of percentage-based splitting, you can charge a fixed commission fee using custom_price. This is useful when you want to charge a flat platform fee regardless of the transaction amount.
Key Points:
  • When custom_price is set, it overrides the percentage calculation
  • The connect team gets the exact custom_price amount (e.g., $50)
  • The master team gets the remainder (e.g., 1110ona1110 on a 1160 total)
  • The master percentage field is ignored when using custom_price
  • Useful for fixed platform fees, minimum commissions, or tiered pricing
Response with Custom Price:

Common Scenarios

1. Register Completed Payment (Bank Transfer)

3. Register Cash Payment

4. Register Payment with PPD Invoice Complement

Register a payment linked to an existing PPD invoice. This automatically generates a payment complement (complemento de pago) CFDI.
Key Points:
  • The ppd_invoice_id must reference a valid PPD income invoice (payment_method='PPD', status='valid', invoice_type='I')
  • The payment complement is generated automatically by proserver after the payment is created
  • You can use automation_type: "none" since the complement is handled via ppd_invoice_id
  • The PPD invoice’s payments array is updated to link back to this payment

5. Partial Refund

This example records a refund already completed outside gigstack; it does not return money through Stripe. Read Refund Payment for the alternative processor request, amount units and timeout recovery.

6. Marketplace Payment Split (Platform Fee)

Response includes split payment details with master (10%) and connect (90%) payment IDs.

7. Marketplace Payment with Fixed Commission

Connect (marketplace) receives fixed 25commission,master(vendor)receivestheremainder(25 commission, master (vendor) receives the remainder (1135 from $1160 total).

Payment Workflows

Immediate Payment (PUE)

Deferred Payment (PPD)

Payment Request Flow

Best Practices

  1. Use idempotency keys - Use one stable idempotency_key per business payment on creation; reuse it for retry attempts. Refunds do not accept this key.
  2. Use client search - Leverage the search parameter to avoid duplicate client records when processing recurring payments.
  3. Set automation_type correctly - Choose the appropriate automation type based on your invoicing workflow (PUE for immediate, PPD for deferred).
  4. Include all items - Ensure complete and accurate payment information for proper invoice generation.
  5. Validate clients first - Verify fiscal data (RFC, tax system) before processing payments to avoid stamp failures.
  6. Use metadata - Track internal references, order IDs, and other business-specific data for reconciliation.
  7. Handle webhooks - Process payment status updates to keep your systems synchronized.
  8. Test in staging - Validate all workflows in the staging environment before deploying to production.

Error Handling

Payment handler errors generally use this envelope; authentication and gateway failures can have a different body. Check the HTTP status before assuming error.code exists:

At a glance

Payments themselves do not stamp CFDIs synchronously. When a payment carries an automation_type that produces an invoice, the stamping happens downstream and asynchronously — a 2xx from POST /payments/register means the payment was recorded, not that a CFDI exists. Subscribe to the invoice.created / invoice.failed webhook events for the stamping outcome, and expect the CFDI-specific statuses (412 SAT not connected, 503 PAC unavailable, 429 credit limit) to surface on the invoice endpoints rather than here.

409 — Multiple clients match search criteria

details lists the ids that matched, so you can resolve the ambiguity without a second query. Not retryable — the same request will conflict again. Do: pass the exact client id, or search on a more selective key (tax_id over name), or deduplicate the clients.

409 — Concurrent resource creation

A different in-flight request is auto-creating the same client (or Service creation in progress … for an item). gigstack refused to race it rather than create a duplicate. Do: this one is retryable — wait ~1s and send the same request again; the resource will exist. If you are firing many payments for a new client in parallel, create the client once up front and reference it by id.

400 — Invalid state transition

The lifecycle endpoints reject operations that do not apply to the payment’s current status: All terminal. Read the payment first (GET /payments/{id}) and branch on status rather than probing.

400 — Refund exceeds the payment

The check is against the cumulative total, not this one refund — prior partial refunds count. Read the payment first: the current API returns total in currency units and total_refunded in minor units. Subtract (total_refunded ?? 0) / 100 from total; see Refund Payment.

400 — External processor refund not allowed

You set external_processor_refund: true on a payment that has no paymentIntent — there is no upstream charge for gigstack to refund. Manually registered payments (cash, transfer) are in this category. Do: omit external_processor_refund to record the refund in gigstack only, and move the money back yourself.

Failed Client Fiscal Information (400)

When a client’s fiscal information fails validation (invalid RFC or EFOS blacklist), the payment is rejected. Update the client (PUT /v2/clients/{id}) and retry.
For additional assistance, contact support@gigstack.io