---
title: "Webhooks"
description: "28 events by subscription: configure URL, headers and event selection. Payload shape, example receiver and caveats on HMAC and retries."
canonical: https://factura.com.do/en/desarrolladores/webhooks
lang: en
generator: factura.com.do docs-index
---

Webhooks 

#  28 events by subscription and zero assumptions 

 You configure the URL from the app, choose the events and optionally add custom headers to authenticate your receiver. The payload shape is standard and the event list is seeded from the initial migration. 

## Summary

When an event occurs within the workspace, factura.com.do sends a POST with a JSON body to the URL you registered. Delivery is **fire-and-forget**: your receiver responds 2xx and the delivery is persisted in the webhook execution log. If it responds differently, there is no automatic retry.

Corporativo plan only

Outgoing webhooks are exclusive to the Corporativo plan. If the workspace is on another plan, no event is dispatched, even with an active subscription, and there is no warning anywhere.

## Subscription model

The subscription is managed from the **Webhooks** section of the workspace, not through a public API. You configure the destination URL, HTTP method (GET/POST/PUT), custom headers (name-value) and event selection. The app exposes an internal test endpoint to verify connectivity before activating.

## Payload shape

All events share the same body shape: `Event`, the event name; `Timestamp`, ISO 8601 in the **server's local time** (with its offset, not normalized to UTC); and event-specific `data`. Keys arrive in PascalCase.

dgii.aprobado.json

```

{

  "Event": "dgii.aprobado",

  "Timestamp": "2026-05-08T10:33:21.4830000-04:00",

  "data": {

    "DocumentId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",

    "StatusId": 5

  }

}


```

## Catalog of 28 events

The 28 events cover quotations, invoices, notes, expenses, DGII statuses and catalog. All are seeded from the initial migration; subscribe only to the ones your integration needs.

`cotizacion.created` Quotations 

A quotation was created in the workspace.

`cotizacion.updated` Quotations 

A quotation was edited.

`cotizacion.anulated` Quotations 

A quotation was voided.

`factura.created` Invoices 

An invoice (E31, E32, E44, E45 or E46) was created.

`factura.updated` Invoices 

An existing invoice was modified and saved from the app. edit-amount and edit-text do not fire it: they issue a new note.

`factura.anulated` Invoices 

An invoice was voided.

`nota_credito.created` Credit notes 

A credit note (E34) was issued, including the one edit-amount and edit-text generate on an invoice.

`nota_credito.updated` Credit notes 

An existing credit note was modified.

`nota_credito.anulated` Credit notes 

A credit note was voided.

`nota_debito.created` Debit notes 

A debit note (E33) was issued, including the one edit-amount and edit-text generate on an expense, purchase or foreign payment.

`nota_debito.updated` Debit notes 

An existing debit note was modified.

`nota_debito.anulated` Debit notes 

A debit note was voided.

`gasto.created` Expenses 

A minor expense (E43), purchase (E41) or foreign payment (E47) was registered.

`gasto.updated` Expenses 

An existing expense was modified.

`gasto.anulated` Expenses 

An expense was voided.

`dgii.aprobado` DGII status 

The DGII accepted the document in full (StatusId 5).

`dgii.rechazado` DGII status 

The DGII rejected the document (StatusId 6). Reconcile with a GET to the resource to read the persisted reason.

`dgii.en_proceso` DGII status 

The DGII is still evaluating the document (StatusId 7). Polling continues.

`dgii.aceptado_condicional` DGII status 

The DGII accepted with observations (StatusId 8). Log the entry.

`customer.created` Contacts 

A customer was registered in the workspace.

`customer.updated` Contacts 

A customer was updated.

`customer.deactivated` Contacts 

A customer was deactivated.

`product.created` Catalog 

A product was added to the catalog.

`product.updated` Catalog 

A product was updated.

`product.deactivated` Catalog 

A product was deactivated.

`supplier.created` Contacts 

A supplier was registered.

`supplier.updated` Contacts 

A supplier was updated.

`supplier.deactivated` Contacts 

A supplier was deactivated.

### The `data` shape per event

The envelope (<code>Event</code>, <code>Timestamp</code>, <code>data</code>) is invariant. What changes is the content of <code>data</code>. These are the fields the dispatcher emits; ignore the ones you do not use, since new fields may appear.

| Events                                                                                   | Fields in data                                                                                                                                                                                                                                                                                                                   |
| ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| cotizacion.created, cotizacion.updated                                                   | DocumentId, SequenceTypeId, StatusId                                                                                                                                                                                                                                                                                             |
| factura.created                                                                          | DocumentId, SequenceTypeId, StatusId, Number, Lines, CustomerId, CurrencyId, AmountDue, PaymentTermId, LimitDate, CustomerName, CustomerTaxId, CustomerPhone, CustomerEmail, CustomerAddress, Accounting                                                                                                                         |
| factura.updated                                                                          | DocumentId, SequenceTypeId, StatusId, Lines, Number, Accounting                                                                                                                                                                                                                                                                  |
| nota\_credito.created, nota\_credito.updated, nota\_debito.created, nota\_debito.updated | DocumentId, SequenceTypeId, StatusId, Number, Lines, CustomerId, SupplierId, CurrencyId, AmountDue, ModifiedReferenceId, ReferenceSecuence, ModificationCode, CustomerName, CustomerTaxId, CustomerPhone, CustomerEmail, CustomerAddress, SupplierName, SupplierTaxId, SupplierPhone, SupplierEmail, SupplierAddress, Accounting |
| gasto.created                                                                            | DocumentId, SequenceSourceId, StatusId, Number, Lines, SupplierId, CurrencyId, AmountDue, PaymentTermId, LimitDate, SupplierName, SupplierTaxId, SupplierPhone, SupplierEmail, SupplierAddress, Accounting                                                                                                                       |
| gasto.updated                                                                            | DocumentId, SequenceSourceId, StatusId, Lines, Number, Accounting                                                                                                                                                                                                                                                                |
| cotizacion.anulated, nota\_credito.anulated, nota\_debito.anulated                       | DocumentId                                                                                                                                                                                                                                                                                                                       |
| factura.anulated, gasto.anulated                                                         | DocumentId, WasAcceptedByDGII, CreditNoteId                                                                                                                                                                                                                                                                                      |
| dgii.aprobado, dgii.rechazado, dgii.en\_proceso, dgii.aceptado\_condicional              | DocumentId, StatusId                                                                                                                                                                                                                                                                                                             |
| customer.created, customer.updated                                                       | CustomerId, LegalName, TaxIdentification                                                                                                                                                                                                                                                                                         |
| supplier.created, supplier.updated                                                       | SupplierId, LegalName, TaxIdentification                                                                                                                                                                                                                                                                                         |
| product.created, product.updated                                                         | ProductId, Code, Description                                                                                                                                                                                                                                                                                                     |
| customer.deactivated                                                                     | CustomerId                                                                                                                                                                                                                                                                                                                       |
| supplier.deactivated                                                                     | SupplierId                                                                                                                                                                                                                                                                                                                       |
| product.deactivated                                                                      | ProductId                                                                                                                                                                                                                                                                                                                        |

Each `Lines` item carries `LineId`, `ProductId`, `ProductVariantId`, `WarehouseId`, `Code`, `Order`, `Description`, `IsService`, `IsExempt`, `Quantity`, `UnitId`, `UnitPrice`, `Discount`, `DiscountPercentage`, `Recharge`, `NetAmount`, `TotalDiscount`, `TotalTax`, `TotalWithholdings`, `Subtotal`, `Taxes` and `Withholdings`. `Accounting` is the header accounting breakdown: `DocumentTypeId`, `BranchId`, `SequenceTypeId`, `IssuedAt`, `LimitDate`, `PaymentTermId`, `CurrencyCode`, `CurrencyRate`, `TaxableAmount`, `ExemptAmount`, `TotalDiscount`, `DiscountPercentage`, `TotalTaxes`, `TotalWithholdings`, `Total`, `AmountDue`, `Freight`, `Tip`, `TratamientoItbis`, `TipoBienServicioComprado` and `AwaitsFiscalValidation`.

We do not sign the payload with HMAC today

If your audit requires authenticating delivery, configure a custom header (for example, `X-Webhook-Secret` with a shared value) when registering the subscription and validate that value in your receiver. Always use HTTPS.

Delivery is fire-and-forget

If your receiver responds with something other than 2xx, there is no automatic server-side retry. Treat each event as at-most-once and reconcile with a targeted GET to the resource if the status matters for your financial flow.

## Example receiver

A minimal receiver validates the secret header, reads the event and routes it. Return 2xx as soon as you persist or enqueue the work; if your logic fails, persist in the background and reconcile later.

POST /webhooks/factura — receiver

TypeScript Python 

webhook-handler.ts

```

// webhook-handler.ts (Express)

import type { Request, Response } from "express";


const SECRETO = process.env.FACTURA_WEBHOOK_SECRET!;


export function manejarWebhook(req: Request, res: Response) {

  if (req.header("X-Webhook-Secret") !== SECRETO) {

    return res.status(401).end();

  }


  const { Event, Timestamp, data } = req.body;


  switch (Event) {

    case "dgii.aprobado":

      return procesar(res, data, "aprobado");

    case "dgii.rechazado":

      return procesar(res, data, "rechazado");

    case "dgii.en_proceso":

      return procesar(res, data, "en_proceso");

    case "dgii.aceptado_condicional":

      return procesar(res, data, "aceptado_condicional");

    default:

      return res.status(204).end();

  }

}


function procesar(res: Response, data: unknown, estado: string) {

  return res.status(200).json({ estado });

}


```

webhook\_handler.py

```

# webhook_handler.py (FastAPI)

import os

from fastapi import FastAPI, Header, HTTPException, Request


app = FastAPI()

SECRETO = os.environ["FACTURA_WEBHOOK_SECRET"]


@app.post("/webhooks/factura")

async def recibir(req: Request, x_webhook_secret: str = Header(default="")):

    if x_webhook_secret != SECRETO:

        raise HTTPException(status_code=401)


    body = await req.json()

    event = body["Event"]


    if event == "dgii.aprobado":

        # persiste en tu ERP

        return {"estado": "aprobado"}

    if event == "dgii.rechazado":

        return {"estado": "rechazado"}

    return {}


```

## Reconciliation and plugin events

Because delivery is not retried, a lost event never arrives again. These routes let you recover what was missed by sweeping a date range: `accounting-sync` returns the same accounting projection as the webhook and `treasury-sync`, the receivables and payables (what is owed and by whom). They accept the `X-Api-Key`. `plugins/events` is different: only installed plugins use it, with their own token, to publish events to other plugins.

[  GET /api/v1/documents/accounting-sync X-Api-Key Stable Devuelve las ventas, notas y gastos emitidos en un rango de fechas con su desglose contable, la misma proyección del webhook. Responde { items, count, hasMore }. ](#) 

| Name                 | Type                       | Description                                                                                              |
| -------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------- |
| from query           | string (ISO 8601) Required | Inicio del rango (fecha de emisión).                                                                     |
| to query             | string (ISO 8601) Required | Fin del rango. Si es anterior a \`from\`, la API responde \`400\`.                                       |
| documentTypeId query | integer Optional           | Limita el barrido a un tipo de documento.                                                                |
| pageSize query       | integer Optional           | Por defecto 200, máximo 500\. Si \`hasMore\` es \`true\`, vuelve a pedir desde la última fecha devuelta. |

[  GET /api/v1/documents/treasury-sync X-Api-Key Stable Devuelve ventas, compras y notas que representan una deuda, con cliente o proveedor y formas de pago. Responde { items, count, nextFrom, nextAfterDocumentId, hasMore }. ](#) 

| Name                  | Type                       | Description                                                                                  |
| --------------------- | -------------------------- | -------------------------------------------------------------------------------------------- |
| from query            | string (ISO 8601) Optional | Cursor de fecha. En la página siguiente, envía el \`nextFrom\` recibido.                     |
| afterDocumentId query | string (uuid) Optional     | Segunda parte del cursor. En la página siguiente, envía el \`nextAfterDocumentId\` recibido. |
| to query              | string (ISO 8601) Optional | Fecha de emisión máxima. Si es anterior a \`from\`, la API responde \`400\`.                 |
| documentTypeId query  | integer Optional           | Limita el resultado a un tipo de documento.                                                  |
| pageSize query        | integer Optional           | Por defecto 200, máximo 500.                                                                 |

[  POST /api/v1/plugins/events Stable Un plugin instalado publica un evento propio y el host lo reparte a los demás plugins suscritos. Solo acepta el token del plugin; con X-Api-Key responde 403. ](#) 

| Name           | Type            | Description                                                                                                               |
| -------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------- |
| eventType body | string Required | Uno de \`cobro.registrado\`, \`pago\_proveedor.registrado\` o \`inventario.movimiento\`. Cualquier otro responde \`400\`. |
| data body      | object Optional | Contenido del evento. El host no lo interpreta: lo reenvía tal cual.                                                      |

## Next steps

Next step 

## Continue here

- [  e-CF lifecycle How each dgii.\* event originates in the submission and polling flow. ](/en/desarrolladores/ciclo-ecf)
- [  Errors HTTP codes and standard shape of error responses. ](/en/desarrolladores/errores)
- [  Invoices Resource that emits the factura.\* and dgii.\* events. ](/en/desarrolladores/facturas)
