> ## 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.

# Create webhook

> Create a new webhook endpoint to receive event notifications.

The response includes the webhook's signing `secret`. **It is shown only once**, so store it
immediately. It signs only `sat.invoice.synced` and `invoice_batch.completed` deliveries, in the
`X-Gigstack-Signature` header (`sha256=` + hex HMAC-SHA256 of the raw body). Resource events are not
signed with it. There is
no endpoint to reveal or rotate the secret later; if you lose it, delete the webhook and create
a new one.

Webhooks created here send resource events in the `v1` format unless your team's default is
`v2`. Delivery formats, headers and retry behavior are described in the `webhookEvent` callback
below.

**gigstack Connect:** Create webhooks for other teams using the `team` parameter.




## OpenAPI

````yaml openapi.json POST /webhooks
openapi: 3.0.3
info:
  title: gigstack API v2
  description: >
    # gigstack API v2


    Build customer, invoice and payment integrations. Start with the
    [quickstart](https://docs.gigstack.io/quickstart), then select the [issuing
    country](https://docs.gigstack.io/concepts/shared-fields).


    ## Authentication and environments


    Use Authorization: Bearer YOUR_API_KEY. Standard live and test keys use
    https://api.gigstack.io/v2. Internal staging uses
    https://gigstack-staging-9z9nnaat.uc.gateway.dev/v2 and separate keys. Test
    mode is a data mode within an environment; a body field does not switch a
    key's mode.


    See [authentication](https://docs.gigstack.io/authentication) for key
    handling, OAuth team scope and gigstack Connect. Signup requires its
    separately documented internal credential; health probes have their own
    security declarations. Operations marked unavailable have no configured
    public gateway route.


    ## Read the operation contract


    Response envelopes, pagination and retry rules differ by endpoint. HTTP
    success alone does not prove external fiscal completion or movement of
    money. Reconcile ambiguous writes before retrying. Use [responses and
    recovery](https://docs.gigstack.io/responses) and the operation's error
    codes; document credits and daily limits are not generic retryable rate
    limits.


    The issuing team's configuration selects Mexico or Colombia; customer
    country and currency do not. Read
    [Mexico](https://docs.gigstack.io/countries/mexico) or
    [Colombia](https://docs.gigstack.io/countries/colombia) before choosing
    fiscal fields. Accepted public input differs from a provider's direct
    payload.


    Examples and schema checks do not imply every operation has been executed
    live. [Verification coverage](https://docs.gigstack.io/verification)
    distinguishes source review, offline checks and observed staging behavior.
    Documentation describes operations and does not authorize execution.
  version: 2.0.0
  contact:
    name: Gigstack API Support
    url: https://gigstack.io
    email: support@gigstack.io
  license:
    name: Proprietary
    url: https://gigstack.io/terms
  termsOfService: https://gigstack.io/terms
servers:
  - url: https://api.gigstack.io/v2
    description: >
      Public API for standard live and test-mode keys. Test data is isolated by
      the key mode

      (`livemode: false`). Internal staging is a separate deployment and key
      store; see

      https://docs.gigstack.io/authentication#environments for its base URL.
security:
  - bearerAuth: []
tags:
  - name: gigstack Connect
    description: >
      **Multi-Team Resource Access**


      gigstack Connect lets a master team act on other teams that share its
      billing account.


      ## 🔗 How It Works


      Add the `team` query parameter to a request made with an **API key**:


      ```bash

      # Access team_xyz789's clients

      GET /clients?team=team_xyz789


      # Create an income invoice for team_abc123

      POST /invoices/income?team=team_abc123


      # Update service in team_def456

      PUT /services/service_456?team=team_def456

      ```


      ## ✅ Requirements


      - The API key's team must be a master team (gigstack Connect enabled)

      - The target team must exist and share the master team's billing account

      - The plan must include the `multipleIssuerAccounts` feature

      - OAuth access tokens cannot act on another team


      ## ⚠️ Error Responses


      Raw `{ "message": … }` bodies from the authentication layer:


      - `401 Unauthorized, not a master team` - gigstack Connect not enabled on
      your key's team

      - `404 Team not found` - Target team doesn't exist

      - `401 Unauthorized, no matched teams` - Target team could not be resolved
      within your billing account

      - `403 Tu plan no incluye múltiples cuentas emisoras. …` - Plan lacks
      `multipleIssuerAccounts`

      - `403 Team mismatch with OAuth token` - OAuth access token used with
      another team's id


      ## 🌐 Availability


      The `team` parameter is read by the authentication layer, so it is
      accepted on every authenticated

      endpoint, including ones whose parameter list does not show it.
  - name: Clients
    description: Client management operations with Mexican tax compliance
  - name: Services
    description: Service and product catalog management with SAT product keys
  - name: Invoices
    description: Invoice management with CFDI 4.0 compliance and SAT integration
  - name: Draft Invoices (Pre-Facturas)
    description: >
      **Create, edit, preview and stamp draft invoices (pre-facturas)**


      Draft invoices let you build CFDI invoices incrementally before stamping
      them with SAT. Use them as **pre-facturas** — generate a preview PDF with
      a "Sin Validez Fiscal" watermark to share with your client for approval,
      then stamp the draft to create a valid CFDI when ready.


      ## Typical Workflow


      1. `POST /invoices/draft` — Create a draft with minimal data

      2. `PUT /invoices/draft/{id}` — Update as more data becomes available

      3. `POST /invoices/draft/{id}/preview` — Generate a preview PDF
      (pre-factura)

      4. `POST /invoices/draft/{id}/stamp` — Finalize into a valid CFDI invoice
  - name: Descarga Masiva SAT
    description: >
      **SAT bulk invoice download via FIEL authentication**


      Download all your issued and received CFDI invoices directly from the SAT
      (Servicio de Administración Tributaria) using your FIEL (Firma Electrónica
      Avanzada).


      ## Setup flow

      1. `GET /invoices/download/activate/status` — Check if activated and
      what's needed

      2. `POST /invoices/download/activate` — Activate billing (adds Stripe
      meter/add-on)

      3. `POST /invoices/download/fiel` — Upload `.cer` + `.key` files
      (auto-registers with SAT)

      4. `PUT /invoices/download/schedule` — Configure daily auto-sync

      5. `POST /invoices/download/request` — Submit manual download requests


      ## Pricing

      - Included in Pro/Business plans: only the download meter ($0.20 MXN/XML)

      - Other paid plans: the same $0.20 MXN/XML download meter, no monthly base
      fee
  - name: Payments
    description: Payment processing, tracking, and refund management
  - name: Receipts
    description: Receipt creation and management with CFDI stamping capabilities
  - name: Retentions
    description: >-
      Tax retention documents (CFDI Retenciones 2.0) — creation, stamping,
      cancellation, and file retrieval
  - name: Platform Payouts
    description: >
      **Plan and stamp a marketplace's provider payouts from two files.**


      For marketplaces and digital platforms that pay providers under the SAT
      digital-platforms scheme

      (Plataformas Tecnológicas, RESICO 625, retention key 26): ride-hailing
      drivers, delivery couriers,

      lodging hosts, sellers of goods. Available to marketplace **master teams**
      whose billing account has

      platform payouts enabled. Upload a movements file and a commissions file.
      gigstack matches each row

      to a provider's team in your billing account and plans three kinds of
      CFDI: the provider's income

      invoice to you, your retention certificate (key 26) to the provider, and
      your monthly commission

      invoice to the provider.


      **Not for your own sales.** This is for a platform invoicing on behalf of
      its providers. A company

      invoicing its own sales, one CFDI per sale, should use `POST
      /invoices/income`.


      **Tax policy per account.** Which documents are issued, and with which SAT
      keys and rates, is set

      per master account at onboarding: allowed tax regimes, CSD requirement,
      service type (`tipoDeServ` /

      `subTipServ`), ISR and IVA withholding rates, product keys and concept
      descriptions. The defaults are

      for ground passenger transport (service type `01`, 2.1% ISR withholding).
      Contact support to

      configure your account's policy; it can't be changed through the API.


      1. `POST /platform-payouts` with an `Idempotency-Key` - upload and plan
      (synchronous)

      2. `GET /platform-payouts/{id}` and `GET /platform-payouts/{id}/movements`
      - review the plan

      3. `POST /platform-payouts/{id}/confirm` - irreversible hand-off to the
      stamping worker

      4. `GET /platform-payouts/{id}` - poll until `completed` or `failed`, then
      read `result`
         (`completed`, `partially_completed` or `failed`)

      Error messages and exclusion reasons are Spanish sentences for end users;
      branch on `error.code`,

      `reason_code` and `exclusion_codes`.
  - name: Documents
    description: >
      SAT supporting documentation (contracts, delivery/payment proofs,
      communications) — upload

      metadata, compliance review, AI extraction, and linking to invoices,
      payments, receipts and clients.
  - name: Teams
    description: Team management, settings, and member administration
  - name: Users
    description: User account management and password operations
  - name: Auth
    description: >
      **API-only signup for AI agents and partner integrations.**


      A single `POST /v2/auth/signup` call provisions a Firebase Auth user,
      billing

      account, team, plan subscription, and returns API keys — no UI, no FIEL
      upload,

      no human onboarding required.


      Designed for the AI-agents-as-customers use case. RFC defaults to genérico

      (`XAXX010101000`) so agents can transact under público en general
      until/unless

      their customer uploads their own RFC.


      Authenticated with `X-Internal-API-Key` (partner-issued) — Bearer tokens
      are

      team-scoped and a brand-new caller doesn't have one yet.


      See `AGENTS.md` in the gigstack-cli repo for end-to-end integration guide.
  - name: Webhooks
    description: Webhook management for real-time event notifications
  - name: Catalogs
    description: >
      **Read-only search over the SAT catalogs**


      Look up the SAT codes required on CFDI line items without hardcoding them:


      - `GET /catalogs/product-keys` — `c_ClaveProdServ`, the item `product_key`

      - `GET /catalogs/unit-keys` — `c_ClaveUnidad`, the item `unit_key` /
      `unit_name`


      Both are typo-tolerant full-text searches ranked by relevance. The
      catalogs are

      published by the SAT and identical for every team, so results carry no
      team data

      and ignore `livemode` — but a valid API key is still required.
  - name: SAT Lists
    description: >
      Read-only access to the RFC lists the SAT publishes under Artículo 69,
      69-B and 69-B Bis.


      gigstack re-syncs all 22 lists from the SAT's published CSVs every Sunday,
      so you can screen a counterparty's

      RFC before invoicing it without scraping the SAT yourself.
externalDocs:
  description: Complete API Documentation & Guides
  url: https://docs.gigstack.io
paths:
  /webhooks:
    post:
      tags:
        - Webhooks
      summary: Create webhook
      description: >
        Create a new webhook endpoint to receive event notifications.


        The response includes the webhook's signing `secret`. **It is shown only
        once**, so store it

        immediately. It signs only `sat.invoice.synced` and
        `invoice_batch.completed` deliveries, in the

        `X-Gigstack-Signature` header (`sha256=` + hex HMAC-SHA256 of the raw
        body). Resource events are not

        signed with it. There is

        no endpoint to reveal or rotate the secret later; if you lose it, delete
        the webhook and create

        a new one.


        Webhooks created here send resource events in the `v1` format unless
        your team's default is

        `v2`. Delivery formats, headers and retry behavior are described in the
        `webhookEvent` callback

        below.


        **gigstack Connect:** Create webhooks for other teams using the `team`
        parameter.
      operationId: createWebhooks
      parameters:
        - $ref: '#/components/parameters/TeamParameter'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookInput'
            example:
              url: https://your-domain.com/webhooks/gigstack
              events:
                - payment.created
                - payment.succeeded
              description: Production webhook for payment events
              status: active
      responses:
        '201':
          description: Webhook created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    example: true
                    type: boolean
                  message:
                    type: string
                    example: >-
                      Webhook created successfully. Save the secret — it will
                      not be shown again.
                  data:
                    $ref: '#/components/schemas/WebhookCreated'
                  timestamp:
                    type: integer
                    format: int64
              example:
                success: true
                message: >-
                  Webhook created successfully. Save the secret — it will not be
                  shown again.
                data:
                  id: wh_dyS2ZVTj
                  url: https://your-domain.com/webhooks/gigstack
                  events:
                    - payment.created
                    - payment.succeeded
                  status: active
                  description: Production webhook for payment events
                  owner: 8UWdgXELUhf022vuoq249mtGytG2
                  created_at: 1709090576567
                  secret: >-
                    3f9a1c0e7b2d4f6a8c1e3b5d7f9a2c4e6b8d0f1a3c5e7b9d2f4a6c8e0b1d3f5a
                timestamp: 1709090576600
        '400':
          description: Invalid webhook data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/AuthForbidden'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalServerError'
      callbacks:
        webhookEvent:
          '{$request.body#/url}':
            post:
              summary: Event delivery (sent by gigstack to your URL)
              description: >
                gigstack sends a `POST` for each event to every **active**
                webhook subscribed to it.

                The body formats are described in `WebhookEventPayload`.


                **Resource events** (`payment.*`, `invoice.*`, `receipt.*`,
                `customer.*`, `service.*`)

                - Body: `WebhookPayloadV1` or `WebhookPayloadV2`, depending on
                the webhook's payload version.

                - Headers: `Content-Type: application/json`, plus any custom
                headers configured on the
                  webhook in the gigstack dashboard (for example `Authorization`). These deliveries are
                  not signed.
                - Each attempt times out after 10 seconds.

                - `408`, `429`, `5xx`, timeouts and network errors are retried
                with exponential backoff,
                  up to 16 attempts in total. Any other `4xx` is final.
                - Webhooks are never disabled automatically because of failed
                deliveries.


                **`sat.invoice.synced`**

                - Body: `SatInvoiceSyncedWebhookEvent`.

                - Headers: `Content-Type: application/json`, `X-Gigstack-Event`,
                and `X-Gigstack-Signature`.
                  The signature is `sha256=` + hex HMAC-SHA256 of the raw body, keyed with the webhook's
                  `secret`. It is absent for webhooks created before signing existed.
                - One attempt with a 10-second timeout. It is never retried, and
                the response is ignored.


                **`invoice_batch.completed`**

                - Sent once when every accepted item of a `POST
                /invoices/income/batch` batch has a final
                  status. Body: `InvoiceBatchCompletedWebhookEvent`.
                - Headers, signature and delivery are the same as for
                `sat.invoice.synced`: signed, one
                  attempt, never retried. Poll `GET /invoices/income/batch/{id}` as a fallback.

                Order across events is not guaranteed, and the same event can
                arrive more than once.

                De-duplicate, and respond with a `2xx` quickly.
              parameters:
                - name: X-Gigstack-Event
                  in: header
                  required: false
                  description: >-
                    `sat.invoice.synced` and `invoice_batch.completed`
                    deliveries only: the event type.
                  schema:
                    $ref: '#/components/schemas/WebhookEventType'
                - name: X-Gigstack-Signature
                  in: header
                  required: false
                  description: >-
                    `sat.invoice.synced` and `invoice_batch.completed`
                    deliveries only: `sha256=<hex HMAC-SHA256 of the raw body,
                    keyed with the webhook secret>`
                  schema:
                    type: string
                    pattern: ^sha256=[0-9a-f]{64}$
              requestBody:
                required: true
                content:
                  application/json:
                    schema:
                      $ref: '#/components/schemas/WebhookEventPayload'
                    examples:
                      v1:
                        summary: Resource event, v1 body
                        value:
                          event: payment.succeeded
                          team: team_1234567890
                          webhook: wh_dyS2ZVTj
                          livemode: true
                          data:
                            id: payment_1234567890
                            status: succeeded
                      v2:
                        summary: Resource event, v2 body
                        value:
                          id: log_4GqT7mZx9LpR2vWc8NdK--wh_dyS2ZVTj
                          type: payment.succeeded
                          created: 1767225600000
                          livemode: true
                          data:
                            object:
                              id: payment_1234567890
                              status: succeeded
                      satInvoiceSynced:
                        summary: sat.invoice.synced
                        value:
                          id: evt_4f1c9a7e2b3d5c6a
                          event: sat.invoice.synced
                          created_at: 1767225600
                          data:
                            uuid: 9D9B0E5B-0341-4C2B-8F3A-6E1D2C4B5A70
                            direction: received
                            resource_status: ready
                            issuer:
                              rfc: EKU9003173C9
                              name: ESCUELA KEMPER URGATE
                            receiver:
                              rfc: MEE200101ABC
                              name: MI EMPRESA EJEMPLO
                            total: 1160
                            currency: MXN
                            issue_date: '2026-01-15T10:30:00'
                            invoice_type: I
                            status: Vigente
                            team: team_1234567890
                            credit_charged: true
                      invoiceBatchCompleted:
                        summary: invoice_batch.completed
                        value:
                          id: evt_9a2b7c4d1e6f3a8b
                          event: invoice_batch.completed
                          created_at: 1790784000
                          data:
                            id: ibatch_5d41402abc4b2a76b9719d911017c592
                            livemode: true
                            total: 250
                            accepted: 248
                            rejected: 2
                            counts:
                              queued: 0
                              stamped: 245
                              failed: 2
                              duplicate: 1
                              needs_review: 0
                            result: partially_completed
              responses:
                '200':
                  description: >
                    Any `2xx` acknowledges the delivery.

                    - For resource events, `408`, `429` and `5xx` trigger a
                    retry, and any other `4xx` is final.

                    - For `sat.invoice.synced` and `invoice_batch.completed` the
                    response is ignored.
              method: post
              type: path
            path: '{$request.body#/url}'
      security:
        - bearerAuth: []
components:
  parameters:
    TeamParameter:
      name: team
      in: query
      required: false
      schema:
        type: string
      description: >
        **gigstack Connect:** Target team ID for multi-team access.


        Requires gigstack Connect enabled on your team and shared billing
        account.


        Also requires the `multipleIssuerAccounts` feature on your plan.
        Requests targeting a

        team other than the one your API key belongs to return `403` without it.


        Only API keys can use it: an OAuth access token sent with another team's
        id is rejected with

        `403 Team mismatch with OAuth token`.


        **Optional — omit it entirely** unless you are acting on another team.
        It deliberately

        carries no example value so generated snippets do not emit
        `?team=undefined`; when the

        parameter is absent, the team is derived from your API key.


        **Example:** `?team=team_xyz789`
  schemas:
    WebhookInput:
      description: >
        Unknown top-level keys are rejected (`400 validation_failed` /
        `unexpected_key`) — body

        validation runs in strict allowlist mode. `team`, `livemode` and `owner`
        are reserved

        and injected by the auth middleware.
      type: object
      additionalProperties: false
      required:
        - url
        - events
      properties:
        url:
          description: >-
            Endpoint URL that receives the events. Any valid URL is accepted;
            use HTTPS in production
          example: https://your-domain.com/webhooks/gigstack
          type: string
          format: uri
        events:
          description: Array of event types to subscribe to (at least one required)
          example:
            - payment.created
            - payment.succeeded
          type: array
          items:
            type: string
            enum:
              - payment.created
              - payment.updated
              - payment.succeeded
              - payment.canceled
              - payment.deleted
              - payment.upcoming_due_date
              - invoice.created
              - invoice.canceled
              - invoice.failed
              - invoice_batch.completed
              - receipt.created
              - receipt.updated
              - receipt.completed
              - receipt.deleted
              - customer.created
              - customer.updated
              - customer.deleted
              - service.created
              - service.updated
              - service.deleted
              - sat.invoice.synced
          minItems: 1
        description:
          description: Optional description of the webhook purpose
          example: Production webhook for payment events
          type: string
          nullable: true
        status:
          description: Webhook status - defaults to active
          example: active
          default: active
          type: string
          enum:
            - active
            - inactive
            - null
          nullable: true
    WebhookCreated:
      description: >-
        The created webhook, plus its signing secret. The secret is returned
        **only in this response**.
      allOf:
        - $ref: '#/components/schemas/ApiPublicWebhook'
        - type: object
          required:
            - secret
          properties:
            secret:
              type: string
              description: >
                HMAC-SHA256 signing secret for this webhook: 64 hexadecimal
                characters. gigstack uses

                this string (as-is, as a UTF-8 key) to sign `sat.invoice.synced`
                and

                `invoice_batch.completed` deliveries in the
                `X-Gigstack-Signature` header. Resource

                events (`payment.*`, `invoice.*`, `receipt.*`, `customer.*`,
                `service.*`) are **not**

                signed with it.


                **Shown once.** It is never returned again by `GET`, `PUT` or
                list calls, and there is

                no endpoint to reveal or rotate it. If you lose it, delete the
                webhook and create a new one.
              example: 3f9a1c0e7b2d4f6a8c1e3b5d7f9a2c4e6b8d0f1a3c5e7b9d2f4a6c8e0b1d3f5a
    ErrorResponse:
      allOf:
        - $ref: '#/components/schemas/StandardErrorResponse'
      description: Standardized error envelope.
      example:
        success: false
        error:
          code: invalid_request_body
          message: An error occurred
        timestamp: 1767225600000
    InternalServerError:
      allOf:
        - $ref: '#/components/schemas/StandardErrorResponse'
      example:
        success: false
        error:
          code: internal_server_error
          message: An internal server error occurred
        timestamp: 1767225600000
    WebhookEventType:
      type: string
      description: Event type a webhook can subscribe to.
      enum:
        - payment.created
        - payment.updated
        - payment.succeeded
        - payment.canceled
        - payment.deleted
        - payment.upcoming_due_date
        - invoice.created
        - invoice.canceled
        - invoice.failed
        - invoice_batch.completed
        - receipt.created
        - receipt.updated
        - receipt.completed
        - receipt.deleted
        - customer.created
        - customer.updated
        - customer.deleted
        - service.created
        - service.updated
        - service.deleted
        - sat.invoice.synced
      example: sat.invoice.synced
    WebhookEventPayload:
      description: >
        Body of a webhook delivery (`Content-Type: application/json`). There are
        three formats:


        - **Resource events** (`payment.*`, `invoice.*`, `receipt.*`,
        `customer.*`, `service.*`) use the
          webhook's payload version:
          - `WebhookPayloadV1`: the default for webhooks created with `POST /webhooks`.
          - `WebhookPayloadV2`: for webhooks created or last saved in the gigstack dashboard, or on teams
            whose default is v2.
        - **`sat.invoice.synced`** always uses `SatInvoiceSyncedWebhookEvent`.

        - **`invoice_batch.completed`** always uses
        `InvoiceBatchCompletedWebhookEvent`.


        Tell them apart by shape:

        - v2 has `type` and `data.object`.

        - v1 has `event`, `team` and `webhook`.

        - The SAT and batch events have `event` and `created_at`; read `event`
        to tell them apart.
      oneOf:
        - $ref: '#/components/schemas/WebhookPayloadV1'
        - $ref: '#/components/schemas/WebhookPayloadV2'
        - $ref: '#/components/schemas/SatInvoiceSyncedWebhookEvent'
        - $ref: '#/components/schemas/InvoiceBatchCompletedWebhookEvent'
    ApiPublicWebhook:
      type: object
      required:
        - id
        - url
        - events
        - status
        - owner
        - created_at
      properties:
        id:
          type: string
          example: wh_dyS2ZVTj
          description: Unique webhook identifier
        url:
          type: string
          format: url
          example: https://your-domain.com/webhooks/gigstack
          description: >-
            Endpoint URL that receives the events. Any valid URL is accepted;
            use HTTPS in production
        events:
          type: array
          items:
            type: string
            enum:
              - payment.created
              - payment.updated
              - payment.succeeded
              - payment.canceled
              - payment.deleted
              - payment.upcoming_due_date
              - invoice.created
              - invoice.canceled
              - invoice.failed
              - invoice_batch.completed
              - receipt.created
              - receipt.updated
              - receipt.completed
              - receipt.deleted
              - customer.created
              - customer.updated
              - customer.deleted
              - service.created
              - service.updated
              - service.deleted
              - sat.invoice.synced
          example:
            - payment.created
            - payment.succeeded
            - invoice.created
          description: Array of event types to subscribe to
        status:
          type: string
          enum:
            - active
            - inactive
          example: active
          description: Webhook status - active or inactive
        description:
          type: string
          nullable: true
          example: Production payment notifications
          description: Optional description of the webhook purpose
        owner:
          type: string
          example: 8UWdgXELUhf022vuoq249mtGytG2
          description: User ID who created the webhook
        created_at:
          type: number
          example: 1709090576567
          description: Unix timestamp (milliseconds) of webhook creation
    StandardErrorResponse:
      type: object
      description: >
        Standardized error envelope emitted by `sendErrorResponse` and its
        helpers

        (`sendValidationError`, `sendNotFoundError`, `sendUnauthorizedError`,

        `sendForbiddenError`, `sendConflictError`, `sendBadRequestError`,

        `sendInternalServerError`).
      required:
        - success
        - error
        - timestamp
      properties:
        success:
          type: boolean
          enum:
            - false
          example: false
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: >
                Stable machine-readable code. Values emitted by the shared
                helpers:

                `unauthorized`, `forbidden`, `invalid_token`,
                `validation_failed`,

                `invalid_request_body`, `missing_required_field`,
                `invalid_field_value`,

                `resource_not_found`, `resource_already_exists`,
                `resource_conflict`,

                `business_rule_violation`, `operation_not_allowed`,

                `insufficient_permissions`, `external_service_error`,

                `payment_processor_error`, `cfdi_service_error`,

                `internal_server_error`, `service_unavailable`,
                `rate_limit_exceeded`,

                `database_error`, `data_integrity_error`, `file_not_found`,

                `file_upload_error`, `invalid_file_format`. Individual handlers
                may

                emit additional endpoint-specific codes, documented per
                operation.
              example: validation_failed
            message:
              type: string
              example: Request validation failed
            details:
              oneOf:
                - type: string
                - type: array
                  items:
                    type: string
              description: >
                Present only when the handler supplies detail. Validation
                failures

                emit an array of `"<field path>: <message>"` strings.
              example:
                - 'currency: Field is required'
        timestamp:
          type: integer
          format: int64
          description: Server time in epoch milliseconds.
          example: 1767225600000
    AuthMiddlewareError:
      type: object
      description: >
        Raw error body produced by the authentication layer (before any endpoint
        runs). It is not

        the standardized envelope: there is no `success` and no `timestamp`.
      required:
        - message
      properties:
        message:
          type: string
          description: >-
            Human-readable reason. Some messages are in Spanish; branch on the
            HTTP status, not on this text.
          example: Unauthorized
        error:
          type: string
          description: >-
            OAuth error code. Present only when an OAuth access token was
            presented.
          enum:
            - invalid_token
            - token_expired
            - token_revoked
            - server_error
          example: token_expired
        error_description:
          type: string
          description: Same text as `message`. Present only alongside `error`.
          example: Access token has expired. Please refresh your token.
        details:
          description: >-
            Extra detail, e.g. `Invalid API Key` when a revoked key is rejected
            with `403`.
          oneOf:
            - type: string
            - type: object
          example: Invalid API Key
    UnauthorizedError:
      allOf:
        - $ref: '#/components/schemas/StandardErrorResponse'
      example:
        success: false
        error:
          code: unauthorized
          message: Unauthorized access
        timestamp: 1767225600000
    WebhookPayloadV1:
      type: object
      description: >
        `v1` body of a resource event. It has no event id, so de-duplicate on
        `event` plus `data.id`.


        Teams on the legacy `v1.1` default receive this object wrapped as `{
        "payload": { … } }`. For

        `invoice.created`, their `data` also includes a `customer` object with
        the full address, and

        `date` is shifted by −6 hours.
      required:
        - event
        - team
        - webhook
        - livemode
        - data
      properties:
        event:
          $ref: '#/components/schemas/WebhookEventType'
        team:
          type: string
          description: Team that owns the webhook.
          example: team_1234567890
        webhook:
          type: string
          description: ID of the webhook receiving this delivery.
          example: wh_dyS2ZVTj
        livemode:
          type: boolean
          description: '`false` for test-mode resources.'
          example: true
        data:
          type: object
          description: >
            The resource at the moment of the event, in gigstack's internal
            (camelCase) format.

            `metadata` values are sent as strings.
          additionalProperties: true
        connectedTeam:
          type: string
          description: >-
            Only on deliveries to a gigstack Connect master team. The connected
            team the event came from.
        connectedTeamMetadata:
          type: object
          description: >-
            Only with `connectedTeam`, when that team has metadata. Values are
            strings.
          additionalProperties:
            type: string
    WebhookPayloadV2:
      type: object
      description: '`v2` body of a resource event (Stripe-style envelope).'
      required:
        - id
        - type
        - created
        - livemode
        - data
      properties:
        id:
          type: string
          description: >
            Unique per event and webhook. Stays the same across every retry and
            resend of that delivery,

            so use it to de-duplicate.
          example: log_4GqT7mZx9LpR2vWc8NdK--wh_dyS2ZVTj
        type:
          $ref: '#/components/schemas/WebhookEventType'
        created:
          type: integer
          format: int64
          description: When the delivery was queued, Unix epoch **milliseconds**.
          example: 1767225600000
        livemode:
          type: boolean
          description: '`false` for test-mode resources.'
          example: true
        data:
          type: object
          required:
            - object
          properties:
            object:
              type: object
              description: >
                The resource in the same format the API returns. It is read when
                the delivery is sent,

                so a retried delivery can show a newer state than the event. For
                `*.deleted` events it

                is the last known state.
              additionalProperties: true
    SatInvoiceSyncedWebhookEvent:
      type: object
      description: >
        Body of a `sat.invoice.synced` delivery. It is the same regardless of
        the webhook's payload version.

        Note the field names: the type is in `event` (not `type`), and
        `created_at` is in **seconds**.

        There is no `livemode`.
      required:
        - id
        - event
        - created_at
        - data
      properties:
        id:
          type: string
          description: >-
            Unique event id, `evt_` followed by 16 hexadecimal characters. Use
            it to de-duplicate.
          example: evt_4f1c9a7e2b3d5c6a
        event:
          type: string
          enum:
            - sat.invoice.synced
          example: sat.invoice.synced
        created_at:
          type: integer
          description: When the event was dispatched, Unix epoch **seconds**.
          example: 1767225600
        data:
          $ref: '#/components/schemas/SatInvoiceSyncedWebhookData'
    InvoiceBatchCompletedWebhookEvent:
      type: object
      description: >
        Body of an `invoice_batch.completed` delivery, sent once when every
        accepted item of an income invoice

        batch (`POST /invoices/income/batch`) has a final status. It is the same
        regardless of the webhook's

        payload version. As with `sat.invoice.synced`, the type is in `event`
        (not `type`) and `created_at`

        is in **seconds**. A batch whose items were all rejected up front
        completes without any stamping and

        also sends this event.
      required:
        - id
        - event
        - created_at
        - data
      properties:
        id:
          type: string
          description: >-
            Unique event id, `evt_` followed by 16 hexadecimal characters. Use
            it to de-duplicate.
          example: evt_9a2b7c4d1e6f3a8b
        event:
          type: string
          enum:
            - invoice_batch.completed
          example: invoice_batch.completed
        created_at:
          type: integer
          description: When the event was dispatched, Unix epoch **seconds**.
          example: 1790784000
        data:
          $ref: '#/components/schemas/InvoiceBatchCompletedWebhookData'
    SatInvoiceSyncedWebhookData:
      type: object
      description: >
        `data` of a `sat.invoice.synced` delivery. Sent when the XML of an
        invoice downloaded from the

        SAT (Descarga Masiva) has been fetched and stored, either by the
        automatic download or by

        `POST /invoices/sat/{uuid}/retry-xml`.
      required:
        - uuid
        - resource_status
        - team
      properties:
        uuid:
          type: string
          description: Folio fiscal (UUID) of the CFDI.
          example: 9D9B0E5B-0341-4C2B-8F3A-6E1D2C4B5A70
        direction:
          type: string
          enum:
            - issued
            - received
          description: Whether your team issued or received the CFDI.
          example: received
        resource_status:
          type: string
          enum:
            - ready
          description: Always `ready` — the XML is stored and the invoice can be read.
          example: ready
        issuer:
          type: object
          description: Issuer of the CFDI, as stored for the SAT invoice.
          additionalProperties: true
          example:
            rfc: EKU9003173C9
            name: ESCUELA KEMPER URGATE
        receiver:
          type: object
          description: Receiver of the CFDI, as stored for the SAT invoice.
          additionalProperties: true
          example:
            rfc: MEE200101ABC
            name: MI EMPRESA EJEMPLO
        total:
          type: number
          description: CFDI total.
          example: 1160
        currency:
          type: string
          example: MXN
        issue_date:
          type: string
          description: CFDI issue date as stored for the SAT invoice.
          example: '2026-01-15T10:30:00'
        invoice_type:
          type: string
          enum:
            - I
            - E
            - P
            - 'N'
            - T
          description: SAT comprobante type.
          example: I
        status:
          type: string
          description: SAT status of the CFDI.
          example: Vigente
        team:
          type: string
          description: Team that owns the invoice.
          example: team_1234567890
        credit_charged:
          type: boolean
          description: Whether a Descarga Masiva credit was charged for this XML.
          example: true
        retried:
          type: boolean
          description: >-
            Present and `true` only when the delivery was triggered by `POST
            /invoices/sat/{uuid}/retry-xml`.
          example: true
    InvoiceBatchCompletedWebhookData:
      type: object
      description: >
        Summary of the finished batch. It carries counts only: to see each
        invoice, page through

        `GET /invoices/income/batch/{id}/items`; for the rejected items'
        reasons, read `rejected` on

        `GET /invoices/income/batch/{id}`.
      required:
        - id
        - livemode
        - total
        - accepted
        - rejected
        - counts
        - result
      properties:
        id:
          type: string
          description: Batch id, as returned by `POST /invoices/income/batch`.
          example: ibatch_5d41402abc4b2a76b9719d911017c592
        livemode:
          type: boolean
          description: Mode of the credential that created the batch.
          example: true
        total:
          type: integer
          description: Invoices in the request.
          example: 250
        accepted:
          type: integer
          description: Invoices that passed validation and were processed.
          example: 248
        rejected:
          type: integer
          description: >
            **Number** of invoices refused by validation before anything was
            stamped. Note that on the batch

            object `rejected` is the list of those items, not a count.
          example: 2
        counts:
          $ref: '#/components/schemas/InvoiceBatchCounts'
        result:
          $ref: '#/components/schemas/InvoiceBatchResultEnum'
    InvoiceBatchCounts:
      type: object
      description: >
        Accepted items by status. `queued` is what is still in flight, so it
        reaches `0` when the batch completes.

        The five counts add up to `accepted`; rejected items are not counted
        here.
      required:
        - queued
        - stamped
        - failed
        - duplicate
        - needs_review
      properties:
        queued:
          type: integer
          description: Waiting for, or in, a stamping attempt.
          example: 0
        stamped:
          type: integer
          description: Stamped by this batch.
          example: 245
        failed:
          type: integer
          description: Ended without an invoice. See each item's `error`.
          example: 2
        duplicate:
          type: integer
          description: >-
            Already issued under the same `idempotency_key` before; not issued
            again.
          example: 1
        needs_review:
          type: integer
          description: >-
            The PAC could not confirm whether the invoice was stamped. gigstack
            support resolves these.
          example: 0
    InvoiceBatchResultEnum:
      type: string
      nullable: true
      description: >
        How a finished batch turned out. `null` while `status` is `processing`.


        - `completed`: every item was issued (`stamped`, or `duplicate` because
        it had been issued before), and
          nothing was rejected.
        - `partially_completed`: at least one item was issued, and at least one
        was not (`failed`,
          `needs_review` or rejected up front).
        - `failed`: no item was issued.
      enum:
        - completed
        - partially_completed
        - failed
        - null
      example: partially_completed
  responses:
    Unauthorized:
      description: >
        Authentication failed. Two different bodies can come back with a `401`:


        1. **From the authentication layer**, before the endpoint runs: a raw
        object with
           `message` and — for OAuth access tokens only — `error` / `error_description`. It does
           **not** use the standardized envelope (`success` and `timestamp` are absent). Messages:
           `Unauthorized` (missing header, missing `Bearer ` prefix, unparseable token),
           `Unauthorized, missing team in token`, `Unauthorized, not a master team` and
           `Unauthorized, no matched teams` (gigstack Connect), `Invalid access token`,
           `Access token has expired. Please refresh your token.`, `Access token has been revoked`.
        2. **From the endpoint itself**: the standardized envelope with
        `error.code: unauthorized`.


        Branch on the HTTP status, not on the body shape.
      content:
        application/json:
          schema:
            anyOf:
              - $ref: '#/components/schemas/AuthMiddlewareError'
              - $ref: '#/components/schemas/UnauthorizedError'
          examples:
            missing_or_invalid_key:
              summary: Authentication layer — missing or unparseable API key
              value:
                message: Unauthorized
            oauth_token_expired:
              summary: Authentication layer — expired OAuth access token
              value:
                message: Access token has expired. Please refresh your token.
                error: token_expired
                error_description: Access token has expired. Please refresh your token.
            connect_not_master_team:
              summary: >-
                Authentication layer — `team` sent by a team without gigstack
                Connect
              value:
                message: Unauthorized, not a master team
            endpoint_envelope:
              summary: Endpoint — standardized envelope
              value:
                success: false
                error:
                  code: unauthorized
                  message: Unauthorized access
                timestamp: 1767225600000
    AuthForbidden:
      description: >
        Rejected by the authentication layer after the credential was
        recognised. The body is a raw

        object (`message`, sometimes `details`), **not** the standardized
        envelope. Causes:


        - The API key was revoked or disabled: `message: "API Key inválida."`,
        `details: "Invalid API Key"`.

        - The billing account's plan does not include API access. `message` is a
        Spanish sentence
          containing an HTML link to `https://app.gigstack.pro/memberships`. Branch on the status
          code; do not display or match the text.
        - gigstack Connect: the plan lacks the `multipleIssuerAccounts` feature
        (Spanish message
          starting `Tu plan no incluye múltiples cuentas emisoras.`), or an OAuth access token was
          sent with a `team` other than its own (`Team mismatch with OAuth token`).

        An endpoint may also answer `403` for its own reasons; those are
        documented on the

        operation when they exist.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AuthMiddlewareError'
          examples:
            revoked_api_key:
              summary: API key revoked or disabled
              value:
                message: API Key inválida.
                details: Invalid API Key
            plan_without_api_access:
              summary: Plan does not include API access
              value:
                message: >-
                  La API se encuentra disponible para un plan más grande, por
                  favor actualiza tu plan <a
                  href='https://app.gigstack.pro/memberships'>aquí</a> o ponte
                  en contacto con soporte.
            connect_plan_without_multiple_issuers:
              summary: gigstack Connect — plan lacks multipleIssuerAccounts
              value:
                message: >-
                  Tu plan no incluye múltiples cuentas emisoras. Actualiza tu
                  plan en https://app.gigstack.pro/memberships o ponte en
                  contacto con soporte para operar sobre otros equipos.
            oauth_team_mismatch:
              summary: gigstack Connect — OAuth token used for another team
              value:
                message: Team mismatch with OAuth token
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >
        **Authentication Method:** HTTP Bearer token.


        The runtime requires the literal `Bearer ` prefix — a bare token in the

        `Authorization` header is rejected with `401 unauthorized`.


        **Header Format:** `Authorization: Bearer YOUR_API_KEY`


        Your API key is a JWT. Live keys operate on live data (`livemode:
        true`);

        test keys operate on isolated test data (`livemode: false`).


        **Get your key at:**
        [app.gigstack.pro/settings?tab=api](https://app.gigstack.pro/settings?tab=api)


        **Errors:** credential failures are answered by the authentication layer
        with a raw

        `{ "message": … }` body, not the standardized envelope — `401` for a
        missing, malformed or

        expired token, `403` for a revoked key or a plan without API access. See
        the `Unauthorized`

        and `AuthForbidden` responses.

````

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