> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gigstack.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Clients API Guide

> Integration guide for Clients

<Warning>Some operations in this guide have Discovery handlers but no public gateway route yet: `POST /clients/{id}/support-documents`. Check the availability notice on each API reference page before using them.</Warning>

<Note>Start with [shared fields](/concepts/shared-fields), then the [Mexico](/countries/mexico) or [Colombia](/countries/colombia) guide. The CFDI, RFC, SAT catalog, and payment-complement examples below describe Mexico; they are not universal country requirements.</Note>

Manage clients with fiscal information for Mexican tax compliance. The Clients API handles customer data, RFC validation, EFOS checking, and SAT compliance requirements.

## Overview

Clients are the foundation of your invoicing system. Each client contains fiscal information required for Mexican tax compliance, including RFC (tax ID), tax system, and address information.

## Key Features

* **RFC Validation** - Automatic tax ID validation against SAT
* **EFOS Checking** - Blacklist validation for compliance
* **Address Management** - Mexican address structure support
* **Metadata Support** - Custom fields for additional data
* **Duplicate Prevention** - Search for existing clients before creating (upsert-like behavior)
* **Auto-creation** - Create clients during invoice/payment flow

## Endpoints

### List Clients

```http theme={null}
GET /clients
```

Retrieve a paginated list of clients with filtering and search capabilities.

**Query Parameters:**

| Parameter | Type | Description |
| - | - | - |
| `limit` | integer (1-100) | Number of results per page (default: 10) |
| `next` | string | Pagination cursor for next page |
| `team` | string | gigstack Connect: Target team ID |
| `order_by` | string | Field to sort by (e.g., `created_at`) |
| `sort` | string | Sort direction (`asc`, `desc`) |
| `created_gte` | integer | Filter by creation date (greater than or equal to timestamp) |
| `created_lte` | integer | Filter by creation date (less than or equal to timestamp) |
| `tax_id` | string | Find a client by tax ID / RFC (e.g., `tax_id=PEGJ800101ABC`) |
| `metadata.{key}` | string | Filter by metadata field using dot notation (e.g., `metadata.external_id=EXT-123`) |
| `metadata_{key}` | string | Filter by metadata field using underscore notation (e.g., `metadata_external_id=EXT-123`) |
| `page` | integer | Page number for pagination when using metadata filters (default: 1) |

**Metadata Filtering:**

You can filter clients by any metadata key using either dot notation or underscore notation. Both formats are equivalent and supported:

```bash theme={null}
# Dot notation
GET /clients?metadata.external_id=EXT-123

# Underscore notation (alternative)
GET /clients?metadata_external_id=EXT-123
```

The metadata filtering uses Typesense search for efficient querying without requiring Firestore indexes. When using metadata filters, pagination is controlled via the `page` parameter instead of the `next` cursor.

**Example Requests:**

```bash theme={null}
# Basic listing
curl -X GET "https://api.gigstack.io/v2/clients?limit=20" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Filter by creation date
curl -X GET "https://api.gigstack.io/v2/clients?created_gte=1700000000000&created_lte=1710000000000" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Filter by metadata (dot notation)
curl -X GET "https://api.gigstack.io/v2/clients?metadata.external_id=EXT-123" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Filter by metadata (underscore notation)
curl -X GET "https://api.gigstack.io/v2/clients?metadata_customer_tier=premium" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Multiple metadata filters with pagination
curl -X GET "https://api.gigstack.io/v2/clients?metadata.region=north&metadata.status=active&page=2&limit=20" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Example Response:**

```json theme={null}
{
    "message": "Clients retrieved successfully",
    "data": [
        {
            "id": "client_1234567890",
            "name": "Juan Pérez García",
            "company": "Empresa SA de CV",
            "email": "juan.perez@ejemplo.com",
            "tax_id": "PEGJ800101ABC",
            "tax_system": "601",
            "livemode": true,
            "created_at": 1677651234,
            "efos": {
                "is_valid": true
            }
        }
    ],
    "has_more": false,
    "total_results": 1
}
```

### Create Client

```http theme={null}
POST /clients
```

Create a new client with fiscal information.

**Duplicate Prevention (Upsert):** Use the `search` parameter to find existing clients before creating:

* If a match is found and `search.update` is `false` (default): Returns the existing client without modifications (200).
* If a match is found and `search.update` is `true`: Updates the existing client with the provided data and returns it (200).
* If no match is found: Creates a new client (201).
* If multiple matches are found: Returns a 409 Conflict error with the list of matching client IDs.

**Request Body:**

```json theme={null}
{
    "name": "Juan Pérez García",
    "company": "Empresa SA de CV",
    "email": "juan.perez@ejemplo.com",
    "phone": "+52 55 1234 5678",
    "tax_id": "PEGJ800101ABC",
    "tax_system": "601",
    "use": "P01",
    "legal_name": "Juan Pérez García",
    "address": {
        "country": "MEX",
        "street": "Av. Insurgentes Sur",
        "exterior": "123",
        "interior": "4B",
        "neighborhood": "Del Valle",
        "municipality": "Benito Juárez",
        "city": "Ciudad de México",
        "state": "CDMX",
        "zip": "03100"
    },
    "metadata": {
        "custom_field": "value"
    },
    "search": {
        "on_key": "tax_id",
        "on_value": "PEGJ800101ABC",
        "update": false
    }
}
```

**Search Parameters:**

| Parameter | Type | Description |
| - | - | - |
| `on_key` | string | The field to search on (e.g., `tax_id`, `email`, `name`) |
| `on_value` | string | The value to match against the specified field |
| `update` | boolean | If `true` and a match is found, update the existing client with the provided data. Default: `false` |

**Example Request (Simple):**

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/clients \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Juan Pérez García",
    "email": "juan.perez@ejemplo.com",
    "tax_id": "PEGJ800101ABC",
    "tax_system": "601",
    "use": "P01"
  }'
```

**Example Request (With Duplicate Prevention):**

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/clients \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Juan Pérez García",
    "email": "juan.perez@ejemplo.com",
    "tax_id": "PEGJ800101ABC",
    "tax_system": "601",
    "use": "P01",
    "search": {
      "on_key": "tax_id",
      "on_value": "PEGJ800101ABC",
      "update": false
    }
  }'
```

**Response Codes:**

| Code | Description |
| - | - |
| `200` | Existing client found (when using `search`) |
| `201` | Client created successfully |
| `409` | Multiple clients match the search criteria |

### Get Client

```http theme={null}
GET /clients/{id}
```

Retrieve a specific client by ID.

**Example Request:**

```bash theme={null}
curl -X GET https://api.gigstack.io/v2/clients/client_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN"
```

### Update Client

```http theme={null}
PUT /clients/{id}
```

Update an existing client's information.

**Body Parameters:**

| Parameter | Type | Description |
| - | - | - |
| `check_pending_receipts` | boolean | When `true` (default), after the update succeeds and the client has valid fiscal information, automatically invoice all of the client's pending receipts using the new client data. Set to `false` to skip this behavior. |

The response includes a `pending_receipts` summary (`attempted`, `succeeded`, `failed`, `skipped`, `failures`) when the check is performed.

**Example Request:**

```bash theme={null}
curl -X PUT https://api.gigstack.io/v2/clients/client_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+52 55 9876 5432",
    "address": {
      "zip": "03200"
    }
  }'
```

**Skip the pending-receipts invoicing:**

```bash theme={null}
curl -X PUT https://api.gigstack.io/v2/clients/client_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+52 55 9876 5432",
    "check_pending_receipts": false
  }'
```

### Delete Client

```http theme={null}
DELETE /clients/{id}
```

Delete a specific client.

**Example Request:**

```bash theme={null}
curl -X DELETE https://api.gigstack.io/v2/clients/client_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN"
```

### Validate Client

```http theme={null}
POST /clients/validate/{id}
```

Validate client's fiscal information against SAT.

**Example Request:**

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/clients/validate/client_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN"
```

### Stamp Pending Receipts

```http theme={null}
POST /clients/{id}/stamp-pending-receipts
```

Stamps every pending receipt that belongs to this client, **to the client's own fiscal identity**.

> **This issues real CFDIs.** Each stamped receipt becomes a live invoice with the SAT, using the client's `rfc`, `legal_name`, `address.zip` and `tax_system`. There is no dry-run mode. Cancel through `DELETE /invoices/{id}` if you stamp by mistake.

**Preconditions** — the client document must already carry the fiscal data required by the CFDI:

| Field | Notes |
| - | - |
| `rfc` | Falls back to `tax_id` if `rfc` is absent |
| `legal_name` | Falls back to `name` if `legal_name` is absent |
| `address.zip` | Required |
| `tax_system` | Required (SAT régimen fiscal code) |

If any of these is missing the call returns `400` with code `client_fiscal_data_incomplete` and a `details` string naming the missing field(s). Nothing is stamped in that case.

**Scope** — only receipts that match *all* of the following are considered:

* `team` equals the effective team of the API key (or the `?team=` Connect target)
* `livemode` equals the mode of the API key (a test key never touches live receipts)
* `status` is `pending`
* `client.id` equals `{id}`

**Batching** — a maximum of **100 receipts are stamped per call**. The response's `remaining` field reports how many pending receipts are still left for this client (including any that failed in this batch, since a failed receipt stays `pending`). Keep calling the endpoint until `remaining` is `0` to drain a backlog.

**Example Request:**

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/clients/client_1234567890/stamp-pending-receipts \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Example Response:**

```json theme={null}
{
    "success": true,
    "message": "Stamped 98 receipt(s), 2 failed",
    "data": {
        "stamped": 98,
        "failed": 2,
        "remaining": 27,
        "results": [
            { "id": "receipt_1234567890", "status": "stamped" },
            { "id": "receipt_0987654321", "status": "failed", "error": "..." }
        ]
    },
    "timestamp": 1730000000000
}
```

When there is nothing to do the endpoint returns `200` with `{ "stamped": 0, "failed": 0, "remaining": 0, "results": [] }`.

**Drain loop:**

```bash theme={null}
while true; do
  remaining=$(curl -s -X POST \
    https://api.gigstack.io/v2/clients/client_1234567890/stamp-pending-receipts \
    -H "Authorization: Bearer YOUR_TOKEN" | jq '.data.remaining')
  [ "$remaining" -eq 0 ] && break
done
```

### Customer Portal Access

```http theme={null}
POST /clients/customerportal
```

Creates a single-use customer-portal session for a client and returns the URL to send them.

The client is identified **in the request body**, not in the path — pass either `id` or `email` (if both are present, `id` wins). Omitting both returns `400 "Client ID or email is required"`.

The team must have a customer portal configured (`customerPortalId`); otherwise the call returns `400 "Team customer portal is not configured"`.

**Request Body:**

| Field | Type | Required | Description |
| - | - | - | - |
| `id` | string | one of `id`/`email` | Client ID |
| `email` | string | one of `id`/`email` | Client email, used when `id` is not supplied |

**Example Request:**

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/clients/customerportal \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "id": "client_1234567890" }'
```

```bash theme={null}
# By email instead
curl -X POST https://api.gigstack.io/v2/clients/customerportal \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "email": "juan.perez@ejemplo.com" }'
```

**Example Response:**

```json theme={null}
{
    "success": true,
    "message": "Customer portal retrieved successfully",
    "data": {
        "url": "https://portal.gigstack.pro/{customerPortalId}?sessionId=...&c=...",
        "expires_at": 1730432000000,
        "session_id": "otpcustomerportal..."
    },
    "timestamp": 1730000000000
}
```

The link is valid for **5 days** (`expires_at`, epoch ms). Treat the URL as a credential — anyone holding it can see that client's documents.

### Upload CSF (Constancia de Situación Fiscal)

```http theme={null}
POST /clients/csf
```

Upload the SAT's CSF PDF to create a client from it, or to update an existing client. gigstack reads the RFC and CIF from the PDF, validates them against the SAT, and fills in the legal name, RFC, tax regime, fiscal type and status, and the full fiscal address.

Send the PDF as `multipart/form-data` in a `file` field. Add `?client_id=` to update that client; omit it to create a new one.

```bash theme={null}
# Create a client from a CSF
curl -X POST https://api.gigstack.io/v2/clients/csf \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "file=@constancia.pdf"

# Update an existing client
curl -X POST "https://api.gigstack.io/v2/clients/csf?client_id=client_1234567890" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "file=@constancia.pdf"
```

Returns `201` (`message: "Client created from CSF"`) or `200` (`message: "Client updated with CSF data"`) with the client in `data`, in the standardized envelope. An unreadable PDF, missing fiscal data or an unknown `client_id` returns `400`.

### Support Documents

```http theme={null}
POST /clients/{id}/support-documents
GET  /clients/{id}/support-documents
```

Attach and list supporting documents (contracts, communications…) for a client. Same fields, limits and response as [invoice support documents](/guides/invoices#support-documents): `multipart/form-data` with `file` (PDF, PNG, JPG or WEBP, up to 10 MB) and `documentType`; answers `201`.

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/clients/client_1234567890/support-documents \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "file=@contrato.pdf" \
  -F "documentType=contract"
```

## Client Structure

### Required Fields

* **None** - All fields are optional for maximum flexibility

### Important Fields

* `tax_id` (string) - RFC for Mexican tax compliance
* `tax_system` (string) - SAT tax system code (e.g., "601", "612")
* `use` (string) - Default CFDI use code (e.g., "P01", "G03")
* `email` (string) - Email for invoice delivery

### Tax System Codes

Common SAT tax system codes:

* `601` - General de Ley Personas Morales
* `612` - Persona Física con Actividades Empresariales
* `621` - Incorporación Fiscal
* `622` - Actividades Agrícolas, Ganaderas, Silvícolas y Pesqueras
* `623` - Opcional para Grupos de Sociedades
* `624` - Coordinados
* `625` - Régimen de las Actividades Empresariales con ingresos a través de Plataformas Tecnológicas
* `626` - Régimen Simplificado de Confianza

### CFDI Use Codes

Common CFDI use codes:

* `P01` - Por definir
* `G01` - Adquisición de mercancías
* `G02` - Devoluciones, descuentos o bonificaciones
* `G03` - Gastos en general
* `I01` - Construcciones
* `I02` - Mobiliario y equipo de oficina por inversiones
* `I03` - Equipo de transporte
* `I04` - Equipo de computo y accesorios
* `I05` - Dados, troqueles, moldes, matrices y herramental
* `I06` - Comunicaciones telefónicas
* `I07` - Comunicaciones satelitales
* `I08` - Otra maquinaria y equipo

## Search and Duplicate Prevention

The `search` parameter enables upsert-like behavior when creating clients. This is useful for integrations that may send the same client multiple times.

### Direct Client Creation (POST /clients)

```json theme={null}
{
    "name": "Juan Pérez García",
    "email": "juan.perez@ejemplo.com",
    "tax_id": "PEGJ800101ABC",
    "tax_system": "601",
    "search": {
        "on_key": "tax_id",
        "on_value": "PEGJ800101ABC",
        "update": false
    }
}
```

### Within Invoices or Payments

When creating invoices or payments, you can search for existing clients inline:

```json theme={null}
{
    "client": {
        "search": {
            "on_key": "tax_id",
            "on_value": "PEGJ800101ABC",
            "update": true
        },
        "name": "Juan Pérez García",
        "email": "juan.perez@ejemplo.com",
        "tax_id": "PEGJ800101ABC",
        "tax_system": "601"
    }
}
```

### Search Behavior

| Scenario | Result |
| - | - |
| Single match found, `update: false` | Returns existing client unchanged |
| Single match found, `update: true` | Updates client with provided data, returns updated client |
| No match found | Creates new client automatically |
| Multiple matches found | Returns 409 Conflict with list of matching client IDs |

### Example: Update Existing Client on Match

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/clients \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Juan Pérez García",
    "email": "nuevo.email@ejemplo.com",
    "tax_id": "PEGJ800101ABC",
    "tax_system": "601",
    "search": {
      "on_key": "tax_id",
      "on_value": "PEGJ800101ABC",
      "update": true
    }
  }'
```

This will find the client with `tax_id: PEGJ800101ABC` and update their email to `nuevo.email@ejemplo.com`.

## Validation and Compliance

### RFC Validation

* Automatic format validation
* SAT registry verification

### EFOS Checking

* Blacklist validation against SAT's EFOS list
* Automatic status updates
* Compliance reporting

### Address Validation

* Mexican postal code verification
* Address completeness checking

## Best Practices

1. **Always include tax\_id** for Mexican clients
2. **Set appropriate tax\_system** based on client type
3. **Use metadata** for custom business logic
4. **Validate clients** before important transactions
5. **Keep addresses updated** for compliance
6. **Use search functionality** to avoid duplicates

## Related Resources

* [Invoices API](/guides/invoices) - Create invoices for clients
* [Payments API](/guides/payments) - Process payments from clients
* [Teams API](/guides/teams) - Manage team settings that affect clients

## Error Handling

Common error scenarios:

### Invalid RFC Format

```json theme={null}
{
    "message": "Invalid tax_id format",
    "error": "RFC must be 12 or 13 characters"
}
```

### Client Not Found

```json theme={null}
{
    "message": "Client not found",
    "error": "The specified client does not exist"
}
```

### EFOS Validation Failed

```json theme={null}
{
    "message": "Client failed EFOS validation",
    "error": "Client is on SAT blacklist"
}
```

### Multiple Clients Match Search Criteria (409 Conflict)

When using the `search` parameter and multiple clients match the criteria:

```json theme={null}
{
    "success": false,
    "error": {
        "code": "resource_conflict",
        "message": "Multiple clients found matching tax_id=\"PEGJ800101ABC\". Please use a more specific search criteria.",
        "details": ["client_1234567890", "client_0987654321"]
    }
}
```

**Resolution:** Use one of the returned client IDs directly, or use a more specific search key (e.g., combine with `email` or use the client `id` directly).

***

For additional assistance, contact [support@gigstack.io](mailto:support@gigstack.io)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.