Skip to content
↑↓ navigate ↵ open Esc close
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.

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.

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

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.

Next steps

Next step

Continue here