Ir al contenido principal

Emitir Factura a la DIAN [BETA]

Funcionalidad en desarrollo y pruebas: este servicio está disponible en el ambiente de pruebas y todavía no debe usarse en producción. El funcionamiento, los campos y los mensajes de error pueden cambiar antes del despliegue definitivo.

Este servicio emite a la DIAN una factura de venta que está en borrador: nuby asigna el consecutivo real con la resolución del documento, pasa la factura a ACTIVA (estado_id = 1) y envía el documento electrónico al proveedor tecnológico.

Emitir es un acto separado y explícito: no existe emitir: true al crear, y la emisión no vuelve a crear el detalle ni la contabilidad —reutiliza lo que el borrador ya guardó—. Una vez emitida, la factura no se puede editar. Y como el envío no es transaccional con la DIAN, el reintento forma parte del flujo normal: está diseñado para que puedas repetirlo sin duplicar el documento.

¿Para qué sirve este servicio?
Sirve para convertir un borrador en un documento legal. Es el único momento en que la factura recibe su número definitivo y en que sale hacia la DIAN. Hasta aquí el borrador era un documento interno sin numerar; después de emitir es una factura electrónica con CUFE, número y estado ante la DIAN.

Casos de uso concretos
  • Cerrar el ciclo de facturación. Creas el borrador, lo revisas, lo ajustas y lo emites cuando el área contable da el visto bueno.
  • Facturar por lotes de negocio. Emites una factura por petición, en el orden que decidas: por cliente, por fecha de corte o por contrato.
  • Recuperar un envío fallido. Si la DIAN rechazó o no respondió, la factura queda activa con el envío fallido y puedes reintentar con la misma referencia_externa sin duplicar el documento.
  • Reaccionar a caídas de red. Si la petición se cortó y no sabes si la emisión ocurrió, repites la operación: si la factura ya tiene CUFE, nuby te devuelve su estado sin reenviar nada.
  • Sincronizar el estado fiscal. Después de emitir consultas el detalle para obtener cufe y estado_dian, y pides el PDF para enviarlo a tu cliente.
Resumen del servicio
Aspecto Valor
Método y ruta POST /service/v2/public/invoices/{factura_id}/issue
Permiso (scope) requerido invoices:create
Respuesta exitosa 200 con factura_id, estado_id, consecutivo, cufe, estado_dian, fecha_envio_dian, dian_result e idempotent_replay
Efecto del documento Estado 1 (Activa), con consecutivo real, CUFE y estado DIAN persistidos
Protección contra duplicados Idempotencia por referencia_externa + emisión idempotente por cufe
¿Se puede repetir? Sí: repetir una emisión nunca duplica el documento
Lo que este servicio NO hace
  • No edita el documento. No acepta cambios de cabecera ni de ítems: usa el borrador tal como está. Si necesitas corregirlo, hazlo antes con Editar Factura en Borrador.
  • No reenvía una factura ya emitida. Si la factura ya tiene CUFE, nuby devuelve su estado actual y no contacta a la DIAN.
  • No revalida el período contable. Se validó al crear el borrador.
  • No cambia la resolución. Usa la que el documento ya tiene asignada.
  • No es transaccional con la DIAN. El envío puede fallar después de asignar el consecutivo; por eso el reintento es parte del flujo normal y no un caso excepcional.
  • No acepta lotes. Se emite una factura por petición.
  • No anula ni corrige. Anulaciones y notas crédito se gestionan en la interfaz de nuby.
Dónde encaja en el ciclo
1. Crear borrador         POST /invoices                              (guía anterior)
2. Editar borrador        PUT|PATCH /invoices/{factura_id}
3. Emitir a la DIAN       POST /invoices/{factura_id}/issue            (esta página)
4. Consultar detalle      GET /invoices/{factura_id}
5. Obtener el PDF         GET /invoices/{factura_id}/pdf

El borrador tiene que existir y ser tuyo. Si todavía no lo creaste, empieza por Crear Factura en Borrador: el factura_id que devuelve es el que va en el path de esta operación.


1. El Endpoint (La dirección web)

Apunta tu sistema a la siguiente dirección de petición POST:

POST https://{{instancia}}/service/v2/public/invoices/{factura_id}/issue
¿Qué debo colocar en {{instancia}}?

La dirección web principal que utilizas a diario para ingresar a tu plataforma, incluyendo la terminación .nuby.app o .arrendasoft.co. Por ejemplo inmobiliaria.nuby.app.

Importante: no incluyas https:// ni barras diagonales (/) al final. La dirección se compone así: https:// + tu instancia + /service/v2/public/invoices/{factura_id}/issue.

¿Qué debes colocar en {factura_id}? El identificador interno del borrador (campo body.factura_id de la creación). Debe ser un entero mayor que 0. Si no lo es, la respuesta es 400 con Debe indicar un factura_id entero mayor que 0 para operar sobre la factura. No lo construyas a partir del consecutivo del borrador: ese valor todavía no es un número de factura.

Encabezados requeridos
Encabezado Valor Obligatorio
Content-Type application/json Sí
Authorization Bearer <token>, con el token obtenido en el servicio Login Sí
Permiso requerido (scope)

Este servicio exige el permiso invoices:create, el mismo permiso de escritura de facturas que la creación. No existe un permiso invoices:issue: emitir consume consecutivo y envía a la DIAN, así que se autoriza con el permiso de escritura que ya tienes.

  • 403 CONSUMIDOR_NO_AUTENTICADO → el token es válido pero no corresponde a un cliente OAuth habilitado. Mensaje: El cliente autenticado no está autorizado para consumir este endpoint.
  • 403 RECURSO_NO_AUTORIZADO → al cliente le falta invoices:create. Mensaje: El consumidor no tiene habilitado el recurso solicitado.

Si aún no tienes un token, consulta la página Login del libro API nuby V2.

Precondiciones de la emisión

nuby las evalúa en este orden y se detiene en la primera que falle. Ninguna consume consecutivo.

# Precondición Qué pasa si no se cumple
1 La factura es de venta (no nació de un contrato de arrendamiento). 409 FACTURA_CONTRATO_NO_SOPORTADA con warnings[]. No se evalúa nada más.
2 La factura la creó tu consumidor por API. 403 RECURSO_AJENO.
3 La facturación electrónica está habilitada (ENABLE_FACT_ELECT = 1). 422 FACTURACION_ELECTRONICA_DESHABILITADA. No se toca la factura.
4 La factura existe. 404 FACTURA_NO_ENCONTRADA.
5 La factura no tiene CUFE. Si ya lo tiene, no se reenvía nada. No es un error: responde 200 con idempotent_replay: true.
6 La factura está en borrador (estado_id = 5). 409 FACTURA_NO_EMITIBLE con el estado actual en el mensaje.

Período contable y resolución. La emisión no vuelve a evaluar el período contable: se validó al crear el borrador. Tampoco recibe una resolución en el cuerpo: se usa la que el documento ya tiene asignada. Si el período se cerró entre la creación y la emisión, esta API no lo detecta por sí sola: verifícalo con tu área contable antes de emitir.

2. La Petición (¿Qué debes enviarnos?)

El cuerpo es un objeto JSON con un solo dato: la referencia externa de esta emisión. La factura que se emite la decide el factura_id del path, no el cuerpo.

Elemento Valor
Método POST
Content-Type application/json
Authorization Bearer <token>
Cuerpo Objeto JSON con referencia_externa
Campos del cuerpo
Parámetro Tipo Requerido Por defecto Descripción
referencia_externa texto Sí — Identificador único de esta emisión, en tu sistema. Si falta o viene vacía: 422 con El campo "referencia_externa" es obligatorio. No reutilices la referencia de la creación ni la de una edición: son operaciones distintas.

No cambies el cuerpo entre reintentos. Cualquier campo adicional que envíes se ignora para la emisión, pero cuenta para la huella de idempotencia. Si reintentas tras un fallo, envía exactamente el mismo cuerpo con la misma referencia_externa; si lo cambias, la operación se trata como distinta (409 IDEMPOTENCY_PAYLOAD_MISMATCH).

La emisión no lleva datos del documento. Ni ítems, ni fecha, ni tercero: todo eso ya está en el borrador. Si necesitas cambiarlo, edita el borrador antes de emitir.

Ejemplo de petición
POST /service/v2/public/invoices/10231/issue
Authorization: Bearer <token>
Content-Type: application/json
{ "referencia_externa": "INV-2026-0001-ISSUE-1" }
Referencias: una por operación lógica
Operación Referencia de ejemplo Se puede reutilizar en…
Crear el borrador INV-2026-0001 Nada más: ya quedó consumida por la creación.
Editar el borrador INV-2026-0001-EDIT-1 Nada más (una referencia por edición).
Emitir INV-2026-0001-ISSUE-1 Solo el reintento de esta misma emisión.

3. La Respuesta (¿Qué te entregaremos?)

Cuando la DIAN acepta el documento, la factura queda activa con su consecutivo real y la respuesta es 200:

{
  "success": true,
  "status": 200,
  "message": "La factura de venta se emitió correctamente.",
  "body": {
    "factura_id": 10231,
    "estado_id": 1,
    "consecutivo": 1534,
    "prefijo": "FE",
    "cufe": "b3f1c0d2e5a94f7c8d10b6a2e4f70c93",
    "estado_dian": "Aprobado",
    "fecha_envio_dian": "2026-10-21 10:42:11",
    "dian_result": { "Result": 0, "State": "Aprobado", "Message": null },
    "idempotent_replay": false
  },
  "idempotent_replay": false
}
Estructura principal de la respuesta
Campo Tipo Descripción
success booleano true cuando la emisión se completó (o cuando la factura ya estaba emitida).
status entero Código HTTP replicado en el cuerpo. Aquí siempre 200.
message texto La factura de venta se emitió correctamente. En un replay: La factura ya había sido emitida; se devuelve su estado actual sin reenviarla a la DIAN.
body objeto Estado del documento después de la emisión (tabla siguiente).
idempotent_replay booleano Viaja en la raíz y dentro de body: true cuando no se envió nada a la DIAN porque la factura ya estaba emitida, o cuando se devolvió una respuesta guardada.
Campos de body
Campo Tipo Descripción
factura_id entero Identificador de la factura emitida: siempre el del path.
estado_id entero Estado del documento después de emitir. Aquí 1 (Activa o Facturada).
consecutivo entero Consecutivo real asignado por nuby con la resolución del documento. Este sí es el número de la factura.
cufe texto Código único de factura electrónica que devuelve la DIAN. Es la marca de que el documento se envió: si está presente, la factura ya no se reenvía.
estado_dian texto Estado del documento ante la DIAN, tal como lo reportó el proveedor tecnológico (por ejemplo Aprobado).
fecha_envio_dian texto AAAA-MM-DD HH:MM:SS Fecha y hora del envío registrado en la factura. null cuando la factura no se ha enviado.
dian_result objeto | null Respuesta cruda del proveedor tecnológico. Su forma la define el integrador; llega en null cuando no hubo respuesta o cuando la emisión fue un replay.
idempotent_replay booleano Copia del indicador de la raíz, dentro del cuerpo.

4. Idempotencia y reintentos

Idempotencia por CUFE

Repetir la emisión de una factura que ya tiene CUFE no reenvía nada a la DIAN: responde 200 con el estado actual y idempotent_replay: true. Es la forma de salir de una duda por un corte de red: si no sabes si la emisión ocurrió, repite la operación y compara.

{
  "success": true,
  "status": 200,
  "message": "La factura ya había sido emitida; se devuelve su estado actual sin reenviarla a la DIAN.",
  "body": {
    "factura_id": 10231,
    "estado_id": 1,
    "consecutivo": 1534,
    "prefijo": "FE",
    "cufe": "b3f1c0d2e5a94f7c8d10b6a2e4f70c93",
    "estado_dian": "Aprobado",
    "fecha_envio_dian": "2026-10-21 10:42:11",
    "dian_result": null,
    "idempotent_replay": true
  },
  "idempotent_replay": true
}

La emisión tampoco reescribe el detalle ni la contabilidad. Reutiliza lo que el borrador ya guardó: no vuelve a grabar ítems, impuestos ni asientos. Eso evita que un reintento altere montos ya revisados.

Reglas de reintento
Situación Estado de la factura Qué debes hacer
Éxito 200 Activa, con CUFE Guarda consecutivo, cufe y estado_dian. No reutilices la referencia.
200 con idempotent_replay: true Activa, ya estaba emitida Nada: la emisión ya había ocurrido. Sincroniza tu copia con el estado devuelto.
502 DIAN_ERROR Activa, con envío fallido Reintenta con la misma referencia_externa y el mismo cuerpo.
500 Sin cambios o estado incierto Reintenta con la misma referencia: si la emisión ya ocurrió, obtendrás un replay.
422 de negocio Sin cambios La referencia quedó liberada: corrige la causa y reintenta con la misma.
409 FACTURA_NO_EMITIBLE Fuera de borrador No insistas. Consulta el estado con Ver Detalle de una Factura.

5. Seguridad y Posibles Errores

¡Tu pase tiene fecha de caducidad!

Por medidas de seguridad, el token que te entregamos solo dura 1 hora. Una vez transcurrido ese tiempo, el pase expira y el sistema bloquea el acceso. Cuando esto ocurra, tu sistema simplemente debe volver a consumir el servicio de Login para pedir un pase nuevo y reintentar la emisión con la misma referencia_externa.

{
  "statusCode": 401,
  "error": {
    "type": "SERVER_ERROR",
    "description": "JWT Token expired."
  }
}

Ese 401 lo emite el middleware de autenticación, no el servicio de facturas, así que no trae success ni error_code. Los rechazos de la operación sí usan el sobre uniforme descrito abajo.

Sobre uniforme de error

Todos los rechazos de la operación traen success: false, status, message y error_code. Además:

  • errors: solo en rechazos de validación, con las causas por campo.
  • warnings: solo cuando el rechazo es por alcance. Sus elementos traen code, factura_id y scope.
  • dian_result: solo en el fallo de la DIAN, cuando hubo respuesta del proveedor.
Orden de evaluación

nuby evalúa en este orden exacto. Importa porque determina qué error recibes cuando hay varios problemas:

# Qué se evalúa Rechazo
1 Scope del consumidor 403
2 El factura_id del path es un entero positivo 400 VALIDATION_ERROR
3 El cuerpo es un objeto JSON 400 VALIDATION_ERROR
4 Alcance: ¿es una factura de contrato? 409 FACTURA_CONTRATO_NO_SOPORTADA
5 Pertenencia: ¿la creó tu consumidor? 403 RECURSO_AJENO
6 referencia_externa presente 422 VALIDATION_ERROR
7 Idempotencia de la solicitud 409 IDEMPOTENCY_IN_PROGRESS / 409 IDEMPOTENCY_PAYLOAD_MISMATCH
8 ENABLE_FACT_ELECT = 1 422 FACTURACION_ELECTRONICA_DESHABILITADA
9 La factura existe 404 FACTURA_NO_ENCONTRADA
10 La factura no tiene CUFE No es error: 200 con idempotent_replay: true
11 Estado 5 (Borrador) 409 FACTURA_NO_EMITIBLE
Tabla de códigos HTTP, causas y mensajes
HTTP error_code Causa y mensaje exacto
400 VALIDATION_ERROR El factura_id del path no es un entero positivo. Debe indicar un factura_id entero mayor que 0 para operar sobre la factura.
400 VALIDATION_ERROR El cuerpo no es un objeto JSON. El cuerpo de la solicitud debe ser un objeto JSON.
400 — El encabezado Authorization no tiene el formato Bearer {token}. JWT Token not send.
401 — Falta el encabezado Authorization: JWT Token required.
401 — El token no existe: JWT Token not found.
401 — El token expiró: JWT Token expired. Vuelve a consumir Login.
403 CONSUMIDOR_NO_AUTENTICADO El token no corresponde a un cliente OAuth habilitado. El cliente autenticado no está autorizado para consumir este endpoint.
403 RECURSO_NO_AUTORIZADO Al cliente le falta invoices:create. El consumidor no tiene habilitado el recurso solicitado.
403 RECURSO_AJENO La factura la creó la interfaz de nuby u otro consumidor. La factura indicada no pertenece al consumidor autenticado.
404 FACTURA_NO_ENCONTRADA La factura no existe. La factura {factura_id} no existe.
409 FACTURA_CONTRATO_NO_SOPORTADA La factura nació de un contrato de arrendamiento. Incluye warnings[] con factura_id. Mensaje: La factura {factura_id} fue generada desde un contrato de arrendamiento. Esta API solo admite facturas de venta; las facturas de contrato se gestionan en el flujo de contratos de nuby.
409 FACTURA_NO_EMITIBLE La factura no está en borrador y no tiene CUFE. Mensaje: La factura {factura_id} no está en borrador (estado {estado_id}). Solo se pueden emitir facturas en estado borrador.
409 IDEMPOTENCY_IN_PROGRESS La misma referencia está siendo procesada por otra solicitud. La solicitud con esta referencia externa está en proceso.
409 IDEMPOTENCY_PAYLOAD_MISMATCH La misma referencia se envió con contenido distinto (o el cuerpo cambió entre reintentos). La referencia externa ya fue usada con un payload distinto.
422 VALIDATION_ERROR Falta referencia_externa. El campo "referencia_externa" es obligatorio.
422 FACTURACION_ELECTRONICA_DESHABILITADA ENABLE_FACT_ELECT no vale 1. No se toca la factura. La facturación electrónica no está habilitada en este sistema, así que no se pueden emitir facturas de venta por API.
500 INTERNAL_ERROR Fallo técnico, con mensaje genérico a propósito: el detalle queda en el log de nuby.
500 IDEMPOTENCY_STORAGE_ERROR No se pudo registrar la solicitud para controlar duplicados. No se pudo registrar la solicitud de idempotencia.
502 DIAN_ERROR La DIAN rechazó el envío o no respondió. Arrastra dian_result cuando hubo respuesta y la factura queda activa con el envío fallido. El message es la causa reportada por el proveedor; si no hay causa disponible: No se pudo emitir la factura {factura_id} ante la DIAN. La factura quedó activa con el envío fallido y puede reintentarse.
Forma exacta de warnings[] en el rechazo de contrato
{
  "success": false,
  "status": 409,
  "message": "La factura 990 fue generada desde un contrato de arrendamiento. Esta API solo admite facturas de venta; las facturas de contrato se gestionan en el flujo de contratos de nuby.",
  "error_code": "FACTURA_CONTRATO_NO_SOPORTADA",
  "warnings": [
    { "code": "FACTURA_CONTRATO_NO_SOPORTADA", "factura_id": 990, "scope": "solo_facturas_de_venta" }
  ]
}
Si la DIAN falla: 502 DIAN_ERROR

Cuando el envío se rechaza o no hay respuesta:

  • La factura queda activa (estado_id = 1) con el estado_dian fallido: el que reportó el proveedor o Fallida si no hubo respuesta.
  • La causa se guarda en la factura y en el registro de errores de la DIAN de nuby.
  • La solicitud de idempotencia queda fallida, así que puedes reintentar con la misma referencia_externa.
  • El documento no se duplica: la emisión es idempotente por CUFE.
{
  "success": false,
  "status": 502,
  "message": "No se pudo emitir la factura 10231 ante la DIAN. La factura quedó activa con el envío fallido y puede reintentarse.",
  "error_code": "DIAN_ERROR",
  "dian_result": { "Result": 1, "State": "Rechazado", "Message": "Documento duplicado" }
}

El reintento es seguro: usa la MISMA referencia_externa y el MISMO cuerpo. Generar una referencia nueva para "volver a intentar" crea una operación distinta, pierde la trazabilidad del primer intento y es la única forma de complicar este flujo.

Revisa la causa antes de reintentar. Un 502 puede ser transitorio (timeout, caída del proveedor) o definitivo (documento rechazado por datos). Si el dian_result.Message describe un problema de datos, corrígelo —cuando sea posible, editando antes de emitir— en lugar de reintentar en bucle.

Un factura_id que no existe suele responder 403 RECURSO_AJENO. La pertenencia se evalúa antes de la existencia: si nuby no tiene registrado que tu consumidor creó ese identificador, no puede distinguir "no es tuya" de "no existe". El 404 FACTURA_NO_ENCONTRADA aparece cuando la pertenencia sí está registrada pero la factura ya no existe.


6. Ejemplos de integración

Todos los ejemplos tratan el 502 como un caso esperado: guardan el error_code y reintentan con la misma referencia_externa.

cURL
TOKEN="TU_TOKEN_AQUI"
INSTANCIA="mi-inmobiliaria.nuby.app"
FACTURA_ID=10231

curl -X POST "https://${INSTANCIA}/service/v2/public/invoices/${FACTURA_ID}/issue" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{ "referencia_externa": "INV-2026-0001-ISSUE-1" }'
PHP
<?php

$instancia = 'mi-inmobiliaria.nuby.app'; // Reemplaza con tu dirección web completa
$token = 'TU_TOKEN_AQUI';                // Token obtenido del servicio Login
$facturaId = 10231;                      // factura_id devuelto al crear el borrador
$referencia = 'INV-2026-0001-ISSUE-1';   // Una referencia por emisión

$url = "https://{$instancia}/service/v2/public/invoices/{$facturaId}/issue";

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['referencia_externa' => $referencia]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    "Authorization: Bearer {$token}",
]);

$respuesta = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if (curl_errno($ch)) {
    // Timeout o caída de red: reintenta con la MISMA referencia y el MISMO cuerpo.
    echo 'Error de conexión: ' . curl_error($ch);
} else {
    $info = json_decode($respuesta, true);

    if ($httpCode === 200) {
        $body = $info['body'];

        echo "Factura emitida: factura_id={$body['factura_id']}"
            . " consecutivo={$body['consecutivo']}"
            . " estado_dian={$body['estado_dian']}\n";
        echo "CUFE: {$body['cufe']}\n";

        if (!empty($info['idempotent_replay'])) {
            echo "Nota: la factura ya estaba emitida; no se reenvió a la DIAN.\n";
        }
    } elseif ($httpCode === 502) {
        echo "La DIAN no aceptó el envío: {$info['message']}\n";
        echo "Detalle del proveedor: " . json_encode($info['dian_result'] ?? null) . "\n";
        // Reintenta con la MISMA referencia: la emisión es idempotente por CUFE.
    } else {
        echo "Rechazo HTTP {$httpCode}: {$info['message']}\n";
        echo "error_code: " . ($info['error_code'] ?? 'N/A') . "\n";
    }
}

curl_close($ch);

?>
Python
import requests

# 1. Configura tus datos de acceso
instancia = 'mi-inmobiliaria.nuby.app'  # Reemplaza con tu dirección web completa
token = 'TU_TOKEN_AQUI'                 # Token obtenido del servicio Login
factura_id = 10231                      # factura_id devuelto al crear el borrador
referencia = 'INV-2026-0001-ISSUE-1'    # Una referencia por emisión

# 2. Prepara la dirección y el cuerpo
url = f"https://{instancia}/service/v2/public/invoices/{factura_id}/issue"

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {token}"
}

payload = {"referencia_externa": referencia}

# 3. Envía la petición y procesa la respuesta
try:
    response = requests.post(url, json=payload, headers=headers, timeout=60)

    print(f"Código de estado HTTP: {response.status_code}")

    info = response.json()

    if response.status_code == 200:
        body = info.get("body", {})

        print(f"Factura emitida: factura_id={body.get('factura_id')}")
        print(f"Consecutivo real: {body.get('consecutivo')}")
        print(f"Estado DIAN: {body.get('estado_dian')} | CUFE: {body.get('cufe')}")

        if info.get("idempotent_replay"):
            print("Nota: la factura ya estaba emitida; no se reenvió a la DIAN.")
    elif response.status_code == 502:
        print(f"La DIAN no aceptó el envío: {info.get('message')}")
        print(f"Detalle del proveedor: {info.get('dian_result')}")
        # Reintenta con la MISMA referencia y el MISMO cuerpo.
    else:
        print(f"Mensaje: {info.get('message')}")
        print(f"error_code: {info.get('error_code')}")

        for aviso in info.get("warnings") or []:
            print(f"  advertencia: {aviso}")

except Exception as e:
    # Ante un timeout, reintenta con la MISMA referencia y el MISMO cuerpo.
    print(f"Ocurrió un error de conexión: {e}")
JavaScript (Fetch API)
// 1. Configura tus datos de acceso
const instancia = 'mi-inmobiliaria.nuby.app'; // Reemplaza con tu dirección web completa
const token = 'TU_TOKEN_AQUI';                // Token obtenido del servicio Login
const facturaId = 10231;                      // factura_id devuelto al crear el borrador
const referencia = 'INV-2026-0001-ISSUE-1';   // Una referencia por emisión

const url = `https://${instancia}/service/v2/public/invoices/${facturaId}/issue`;

// 2. Función para emitir la factura
async function emitirFactura() {
    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`
            },
            body: JSON.stringify({ referencia_externa: referencia })
        });

        console.log(`Código de estado HTTP: ${response.status}`);

        const info = await response.json();

        if (response.status === 200) {
            const body = info.body || {};

            console.log(`Factura emitida: factura_id=${body.factura_id}`);
            console.log(`Consecutivo real: ${body.consecutivo}`);
            console.log(`Estado DIAN: ${body.estado_dian} | CUFE: ${body.cufe}`);

            if (info.idempotent_replay) {
                console.log('Nota: la factura ya estaba emitida; no se reenvió a la DIAN.');
            }
        } else if (response.status === 502) {
            console.log(`La DIAN no aceptó el envío: ${info.message}`);
            console.log('Detalle del proveedor:', info.dian_result);
            // Reintenta con la MISMA referencia y el MISMO cuerpo.
        } else {
            console.log(`Mensaje: ${info.message}`);
            console.log(`error_code: ${info.error_code}`);

            (info.warnings || []).forEach((aviso) => console.log('  advertencia:', aviso));
        }
    } catch (error) {
        // Ante un timeout, reintenta con la MISMA referencia y el MISMO cuerpo.
        console.error(`Ocurrió un error de conexión: ${error}`);
    }
}

// 3. Ejecutamos la función
emitirFactura();
Power Query M (Excel / Power BI)

La emisión es POST, así que Power Query puede consumirla directamente. Si el envío falla, Web.Contents genera una excepción: captúrala con try ... otherwise para registrar el error_code y el detalle del proveedor antes de reintentar.

let
    // 1. Configura tus datos de acceso
    instancia = "mi-inmobiliaria.nuby.app",  // Reemplaza con tu dirección web completa
    token = "TU_TOKEN_AQUI",                 // Token obtenido del servicio Login
    facturaId = "10231",                     // factura_id devuelto al crear el borrador

    // 2. Prepara la dirección de la petición
    url = "https://" & instancia & "/service/v2/public/invoices/" & facturaId & "/issue",

    // 3. Construye el cuerpo JSON (una referencia por emisión)
    cuerpo = Json.FromValue([ referencia_externa = "INV-2026-0001-ISSUE-1" ]),

    // 4. Envía la petición POST
    response = Web.Contents(url, [
        Headers = [
            #"Content-Type" = "application/json",
            #"Authorization" = "Bearer " & token
        ],
        Content = cuerpo
    ]),

    // 5. Decodifica la respuesta JSON
    jsonResponse = Json.Document(response),
    body = jsonResponse[body],

    // 6. Convierte el resultado en tabla
    tabla = Table.FromRecords({body}),
    expandido = Table.ExpandRecordColumn(tabla, "Column1",
        {"factura_id", "estado_id", "consecutivo", "prefijo", "cufe", "estado_dian", "fecha_envio_dian"},
        {"FacturaID", "EstadoID", "Consecutivo", "CUFE", "EstadoDian", "FechaEnvioDian"})
in
    expandido

Después de emitir, puedes confirmar el estado fiscal con Ver Detalle de una Factura y entregar el documento a tu cliente con Obtener el PDF de una Factura.

¿Necesita ayuda adicional?

Nuestro equipo de soporte está disponible para asistirle:

Escríbenos a nuestro correo:

📞 (604) 444-7417 Opción 2

📧 soporte@nuby.ai

Escríbenos a nuestra línea de Whatsapp:

Whatsapp

Síguenos en nuestras redes sociales: