Ir al contenido

Visión general

La API pública de Faturei Hoje permite que otro sistema lea y escriba los datos de su empresa sin pasar por el panel. Son llamadas HTTP con JSON, autenticadas por una clave que usted crea, rota y revoca en el panel.

Cada clave pertenece a una empresa y actúa como un miembro de ella. La clave nunca hace más de lo que ese miembro haría en la pantalla.

  • Un ERP, un CRM o un sistema propio que necesita los mismos datos que la empresa ve en el panel.
  • Un sitio o un formulario que envía contactos hacia la empresa.
  • Una herramienta de automatización que dispara llamadas HTTP, como n8n, Make y Zapier.

La llamada sale de su servidor. La clave no debe aparecer en el navegador de su cliente ni dentro de una aplicación instalada en el teléfono de él.

https://api.fatureihoje.com/public/v1

En esa dirección solo existe /public/v1 y el archivo /.well-known/security.txt. Cualquier otra ruta responde 404. Toda respuesta trae el encabezado Request-Id y Cache-Control: no-store.

La v1 está en construcción. Hoy la API publica trece áreas:

ÁreaRutas
CuentaGET /public/v1/me: la empresa de la clave, los permisos concedidos, el miembro representado y el límite de llamadas
AgendaGET, POST, PATCH y DELETE en /public/v1/appointments, más POST /public/v1/appointments/{id}/status
ClientesGET, POST, PATCH y DELETE en /public/v1/clients y en /public/v1/clients/{id}/addresses
LeadsPOST /public/v1/leads y GET /public/v1/leads/{id}: captación de formulario, con la tarea de contacto
PresupuestosGET, POST, PATCH y DELETE en /public/v1/quotes, más POST en /{id}/send, /{id}/approve, /{id}/reject, /{id}/cancel y /{id}/convert_to_sale
Órdenes de servicioGET, POST, PATCH y DELETE en /public/v1/service_orders, más POST en /{id}/status, /{id}/start, /{id}/complete y /{id}/cancel, POST /public/v1/service_orders/from_quote para convertir un presupuesto aprobado en orden, y GET /public/v1/service_order_types con los tipos de orden de la empresa
TareasGET, POST, PATCH y DELETE en /public/v1/tasks, más POST /public/v1/tasks/{id}/status
EquipoGET /public/v1/team: quién trabaja en la empresa, en una sola lista, para asignar la tarea de contacto
Productos y stockGET, POST, PATCH y DELETE en /public/v1/products, en /public/v1/products/{id}/variations y en /public/v1/product_categories, más el saldo en GET /public/v1/products/{id}/stock y el historial en GET /public/v1/stock_movements (stock de solo lectura)
VentasGET, POST y PATCH en /public/v1/sales, más POST y GET en /{id}/versions, POST en /{id}/cancel, /{id}/payments y /{id}/payments/{payment_id}/cancel, y PATCH /{id}/installments (sin eliminación: la venta se cancela)
ServiciosGET, POST, PATCH y DELETE en /public/v1/services y en /public/v1/service_categories
FinanzasGET, POST, PATCH y DELETE en /public/v1/financial_transactions, /public/v1/financial_accounts y /public/v1/financial_categories, más POST /public/v1/financial_transactions/{id}/mark_paid, /transfer, /recurring y /installments
WebhooksGET, POST, PATCH y DELETE en /public/v1/webhook_endpoints, más POST en /{id}/rotate_secret y /{id}/ping, el registro de entregas en GET /{id}/deliveries y el reenvío en POST /{id}/deliveries/{delivery_id}/retry, solo para dueño y administradores; y los eventos de los últimos 30 días en GET /public/v1/events y /public/v1/events/{id}, recortados por los módulos que la clave lee

El envío de webhooks está descrito en Webhooks: el formato de la entrega, la firma, los reintentos y el catálogo de eventos. Mientras un área no aparece en la Referencia, no responde: la Referencia se genera del código de la API, así que nunca lista una ruta que no existe.

Crear un lead no sobrescribe un registro que ya existe: si el teléfono o el correo ya está en la base, la API reutiliza el registro, responde deduplicated: true y no cambia ni los datos ni la situación.

La lista de equipo trae solo el nombre, el tipo y si la persona está activa. Contacto, costo por hora, comisión y código de acceso al portal no salen en la API.

La tarea sigue las mismas reglas del panel: nace como todo, cambia de situación por POST /public/v1/tasks/{id}/status (bloquear exige el motivo), y notify_via_whatsapp avisa al responsable por WhatsApp al crearla. Eliminar una tarea la borra definitivamente, y se lleva el historial, los comentarios, los adjuntos, las dependencias y, cuando la tarea es la primera de una serie que se repite, las demás ocurrencias.

La tarea interna del módulo de Facilities no aparece en esta área ni puede ser alterada por ella, ni en la lista ni en la consulta por identificador: para la API responde el mismo 404 de una tarea que no existe.

La cita de la agenda sigue las mismas reglas del panel: cuenta en la cuota mensual del plan, acepta una franja aproximada (morning, afternoon, evening, all_day) que mueve el horario al comienzo de esa franja en el huso de la empresa, y la dirección elegida tiene que ser una dirección del cliente de la cita. Quien conectó el Google Calendar ve la cita aparecer, cambiar y salir de allí junto: crear refleja el evento, editar lo actualiza, cancelar y eliminar lo quitan. El recordatorio de WhatsApp que la empresa configura en las automatizaciones vale para la cita agendada por la API igual que para la agendada en la pantalla, y usa a quien la agendó como uno de los destinatarios.

Aquí la API es diferente del panel, a propósito: el PATCH de la cita no acepta status. En la pantalla la edición graba la situación sin verificar la transición, lo que permite revivir una cita cancelada o saltar directo a completada; la API no copia eso. Enviar status en el PATCH responde 422 nombrando el campo, y el cambio de situación ocurre en POST /public/v1/appointments/{id}/status, que valida la transición: de agendada y de confirmada se puede ir a cualquier otra, completada solo vuelve a confirmada, y cancelada y no_show son finales. Otra transición responde 409 conflict. Esta divergencia es deliberada, y no es la única de esta fase: la tarea interna del módulo de Facilities responde 404 aquí, y assignee_type es obligatorio junto con assignee_id en la tarea, donde el panel adivina el tipo de persona. Donde la API diverge del panel a propósito, la Referencia lo dice en el campo o en la operación.

Eliminar una cita la borra definitivamente, y solo el dueño y los administradores de la empresa pueden. La orden de servicio que apuntaba a ella sigue existiendo, sin el vínculo. El identificador y el enlace del evento en el Google Calendar, y la marca de recordatorio ya enviado, nunca salen en la respuesta.

La orden de servicio sigue las mismas reglas del panel: cuenta en las cuotas del plan, recibe el número de la empresa, tiene el total siempre calculado (subtotal menos descuento más recargos), exige que cliente, técnicos, tipo y dirección sean de la empresa, y exige por la API los campos que la empresa marcó como obligatorios en la configuración de órdenes. Cuando la empresa exige tipo, los valores aceptados en type_id están en GET /public/v1/service_order_types, que también dice, en required_on_create, si el tipo es obligatorio. Lo que manda es la situación (status); la columna del tablero del panel es presentación y no sale en la v1. Como en el panel, no existe transición prohibida: cualquier situación va a cualquier otra por POST /public/v1/service_orders/{id}/status, y start, complete y cancel hacen lo que hacen los botones del panel. El técnico responsable es technician_id, y el equipo de apoyo es support_technicians; technician_id en la lista trae las órdenes en que el técnico es el responsable o está en el equipo.

Los tres efectos secundarios de la orden son los del panel, por el mismo código: send_to_technician en la creación avisa al técnico y al equipo por WhatsApp, y solo cuando la orden tiene técnico responsable (technician_id completo): sin técnico no se avisa a nadie; create_appointment en la creación (con scheduled_start) crea la cita en la agenda y la refleja en Google Calendar; y POST /public/v1/service_orders/{id}/complete con link_financial_transaction registra el ingreso en finanzas. Crear la cita y registrar en finanzas escriben en otro módulo, así que, cuando la cita se crea de verdad o se pide el registro, la clave necesita también appointments:create y finance:create, respectivamente; sin ellos la respuesta es 403 permission_missing y no se graba nada.

Una orden completada o cancelada no se puede editar, como en el panel: el PATCH responde 409 conflict. Por el mismo motivo, start, complete y cancel responden 409 en una orden completada o cancelada, y repetir la conclusión no crea otro ingreso; para reabrir, use POST /public/v1/service_orders/{id}/status. Una orden antigua vinculada a un cliente de otra empresa no registra ingreso al completarse (422). Eliminar una orden la archiva, que es lo que hace el panel: no se borra de la base, pero pasa a responder 404 en toda ruta y sale de toda lista. Solo el dueño y los administradores pueden. En una orden antigua vinculada a un cliente de otra empresa, no sale ningún dato de ese cliente: nombre, correo, teléfono y dirección vienen null. El enlace público del documento, el borrador que arma WhatsApp, la ubicación del técnico, el costo de mano de obra y el teléfono del técnico nunca salen en la respuesta. Dos diferencias con el panel, ambas a favor de quien integra: la orden archivada responde 404 también en la consulta por identificador, y un PATCH sin priority mantiene la prioridad que la orden tenía.

El presupuesto sigue las mismas reglas del panel: cuenta en las cuotas del plan, recibe el número de la empresa, tiene el subtotal, el total y la mensualidad siempre calculados a partir de los ítems (un ítem opcional solo suma cuando está incluido, y la mensualidad, que son los ítems de la sección recurring_monthly, nunca suma con el total), deriva el tipo de los ítems y usa los términos y las condiciones de pago estándar de la empresa cuando el campo no viene. Cliente y vendedor tienen que ser de la empresa. La lista es el embudo del panel: plantillas de presupuesto, versiones antiguas y presupuestos absorbidos en una unión no aparecen en ella; los dos últimos se pueden consultar por identificador, y la plantilla responde 404. Armar bloques, unir presupuestos, versionar y el espejo de costos siguen solo en el panel.

Las acciones del presupuesto hacen lo que hace el panel, por el mismo código, y como en el panel no existe transición prohibida. send marca el presupuesto como enviado y activa el portal del cliente vinculado; no envía mensaje ni correo y no genera enlace público, porque en el panel el mensaje y el código de acceso al portal son pasos aparte. Enviar por la API solo marca el presupuesto como enviado: el código de acceso del cliente al portal lo genera y lo entrega el panel, y sin él el cliente no abre el portal. approve es el selector de situación del panel: registra la aprobación en el historial con el nombre de quien aprobó, aprueba todos los bloques, dispara las automatizaciones de presupuesto aprobado y, cuando la empresa activó el contrato automático y el presupuesto tiene mensualidad, crea el contrato, una sola vez. reject exige el motivo. convert_to_sale crea la venta con ítems, plan de pago, movimientos en finanzas y descuento de stock, como la ventana de conversión del panel, y POST /public/v1/service_orders/from_quote crea la orden a partir de un presupuesto aprobado, como la ventana “Convertir en orden”, con el mismo aviso al técnico y la misma cita de la creación de orden; la clave también necesita quotes:read, y un presupuesto antiguo vinculado a un cliente de otra empresa se rechaza con 422. Convertir en venta escribe en otros módulos: la clave necesita sales:create, y también finance:create cuando la solicitud pide el registro en finanzas (track_in_finance: true o una cuenta); el descuento de stock no pide products:update, porque descontar stock es parte de vender. Sin track_in_finance, la venta va a finanzas por la configuración de la empresa, y eso no exige finance:create (vea Permisos).

Un presupuesto aprobado o cancelado no se puede editar, como en el panel: el PATCH responde 409 conflict. Un presupuesto absorbido en una unión se puede leer, pero no acepta edición, acción, eliminación ni conversión (409): vale el presupuesto padre, y la pantalla del panel también lo bloquea. Las dos conversiones cobran las cuotas del plan, como la creación directa de orden y de venta. En la edición, items reemplaza la lista, y el ítem que ya existía mantiene el costo congelado de cuando se presupuestó, como en el panel. Eliminar un presupuesto lo borra definitivamente, y solo el dueño y los administradores pueden; un presupuesto que ya se convirtió en venta activa no se puede eliminar (409) hasta que la venta se cancele. El mismo documento (o el mismo bloque) no se convierte en dos ventas activas, y el presupuesto no se convierte en dos órdenes: la segunda conversión responde 409. El costo unitario de los ítems, la formación de precio y el enlace público del documento nunca salen en la respuesta, y en un presupuesto antiguo vinculado a un cliente de otra empresa no sale ningún dato de ese cliente.

El catálogo sigue las mismas reglas del panel: producto y servicio cuentan en las cuotas del plan, el código interno (sku) no se repite en la empresa, la categoría debe ser de la misma empresa (otra responde 422 con param category_id), y el árbol de categorías de producto tiene dos niveles. Toda escritura en producto, variación, categoría y servicio revalida la tienda virtual, como al guardar desde la pantalla. El costo de reposición, el costo promedio, el costo de material y el costo de los movimientos de stock nunca salen en la respuesta, y tampoco son campo de entrada.

El stock es de solo lectura. GET /public/v1/products/{id}/stock trae el saldo del producto y de cada variación, el stock mínimo y la unidad, y GET /public/v1/stock_movements trae el historial: cada entrada, salida y ajuste, con el saldo después del movimiento. El saldo cambia por el registro (saldo de apertura), por la venta, por la entrada de mercadería y por el stock del PATCH del producto o de la variación. Como en el panel, ese stock es el saldo absoluto (“déjelo en 40”): la diferencia con el saldo actual se vuelve un ajuste manual (manual_adjustment) en el historial, firmado por el miembro que representa la clave.

Eliminar un producto lo borra definitivamente, como en el panel, y se lleva las variaciones, todo el historial de stock del producto y de las variaciones, y los vínculos con proveedores. La entrada de mercadería sigue existiendo, con la descripción del ítem y sin el vínculo, y presupuestos, órdenes de servicio y ventas guardan el ítem como texto y no cambian. Eliminar una variación se lleva su historial. Una categoría con productos o servicios no se puede eliminar (409). Eliminar un servicio también lo borra definitivamente.

Quien tiene más de una tienda en la misma suscripción puede compartir productos entre ellas desde el panel. Cada tienda tiene su copia del producto, y la clave de una tienda solo ve y solo cambia la copia de su tienda: la copia de otra tienda responde 404 en toda ruta. is_shared y is_share_master dicen si el producto está compartido y si esta copia es la ficha maestra. Editar la maestra por la API lleva los campos sincronizados a las copias de las otras tiendas, exactamente como en el panel, y revalida la tienda virtual de cada una; editar una copia cambia solo su tienda. El saldo nunca viaja entre tiendas. Eliminar la maestra no borra las copias: la más antigua pasa a ser la maestra.

El PATCH de producto, variación, categoría y servicio cambia solo los campos que usted envíe: un campo que no viene queda exactamente como está, incluidos precio, saldo, descripción y la situación del servicio. Algunas verificaciones que el panel hace en el formulario valen aquí en el servidor y responden 422 nombrando el campo: el video debe ser una dirección de YouTube y solo aparece con la dirección cargada; el texto del botón personalizado y la opción “reemplazar los botones estándar” solo valen con la dirección del botón; las imágenes (image_url) deben ser una dirección http o https completa; y el lc116_code del servicio debe ser un ítem de la lista de servicios de la LC 116 que ofrece el panel, porque va directo a la emisión de la factura.

La venta sigue las mismas reglas del panel, por el mismo código: cuenta en la cuota del plan, recibe el número de la empresa, arma el plan de pago, registra los cobros, registra en finanzas y descuenta el stock de los productos del catálogo, como la pantalla. Subtotal y total se calculan a partir de los ítems exactamente como los calcula el formulario, con un solo modo de descuento (discount_cents o discount_percentage, de 0 a 100). El ítem del catálogo tiene que ser un producto de la empresa, con la variación cuando el producto tiene variaciones, y el nombre y el precio promocional vienen del catálogo; el ítem libre lleva el nombre que usted envíe. La venta a crédito (unpaid) necesita cliente. La venta, la versión y el cobro hechos por la API quedan marcados con created_channel igual a public_api, incluida la venta que nace de convert_to_sale; lo que se hace en el panel sigue siendo panel.

El id de la venta es uno solo, para siempre. En el panel, todo cambio comercial (ítems, valores, cliente, vendedor o plan del saldo) crea una versión nueva de la venta; por la API ese cambio es POST /public/v1/sales/{id}/versions, con el motivo, y la venta sigue respondiendo por el mismo id, con version mayor. En la versión nueva, el ítem del catálogo que ya estaba en la venta mantiene el nombre y la promoción grabados, como en la pantalla; solo el ítem nuevo viene del catálogo. GET /public/v1/sales/{id}/versions trae el historial. La lista muestra cada venta una vez, en la versión vigente hoy, y el sale_id del historial de stock y del presupuesto convertido es este mismo id. El PATCH solo cambia observaciones y producción; un campo comercial en el PATCH responde 422 nombrando el campo, porque el cambio comercial tiene ruta propia. No existe eliminación: la venta se cancela, y cancelar anula los cobros, devuelve el stock descontado y libera el presupuesto o la OS de origen para convertir de nuevo.

Los bloqueos de la venta son los del panel, acción por acción, incluido lo que en el panel solo impide la pantalla:

Situación de la ventaResponde 409Sigue funcionando
Canceladaeditar, versión nueva, cancelar de nuevo, cobrar, anular cobro, reprogramarleer
Con factura activa (de la venta, del origen o de un cobro), invoice_locked: trueversión nueva y cancelar; anular el cobro que tiene la factura (has_active_invoice: true)editar observaciones y producción, cobrar, reprogramar, anular los otros cobros
Con la comisión del vendedor ya pagadaversión nuevacancelar, cobrar, reprogramar, editar observaciones y producción
De contrato (receivable_managed_by: contract)cobrar, reprogramar, anular cobroversión nueva, cancelar, editar observaciones y producción

También como la pantalla: cobrar solo en una cuota abierta (installment_id de una cuota saldada responde 422), reprogramar solo con cuota abierta (409), anular solo un cobro que no fue anulado (409), versión nueva solo cuando algo comercial cambia (422) y los campos de producción solo con la producción activa (422).

Cuenta y movimientos son datos de finanzas. account_id (de la venta y de cada cobro) y finance_transactions solo vienen en la respuesta cuando la clave tiene finance:read y el miembro que representa tiene acceso a finanzas (el dueño y los administradores lo tienen). Sin eso, esos campos no vienen, exactamente como en el panel. Las cuotas (installments) y los cobros (payments) vienen siempre, y el miembro sin finanzas cobra, anula y reprograma como lo hace en la pantalla; solo no elige la cuenta ni decide si la venta va a finanzas (403). La comisión del vendedor, si la venta descontó stock, la distribución interna de los cobros entre las cuotas, los ids de usuario y los ids de las versiones internas nunca salen. En una venta antigua ligada a un cliente, vendedor, cuenta o producto de otra empresa, ese vínculo sale null.

Finanzas sigue las mismas reglas del panel, por el mismo código: el movimiento confirma cuenta, categoría y cliente, el gasto en una cuenta de tarjeta va al resumen del ciclo (on_statement), y el vencido se recalcula cuando el movimiento se graba. Como en el formulario de la pantalla, la categoría tiene que ser del mismo tipo del movimiento, “mostrar en el portal del cliente” solo vale en un gasto con cliente, la fecha de pago solo existe en el movimiento pagado (sin ella, la del movimiento), y sin account_id el movimiento va a la cuenta predeterminada de la empresa, que la pantalla ya deja marcada; account_id: null es “sin cuenta”. La recurrencia y las cuotas crean la serie entera de una vez, con los mismos límites de la pantalla, y la transferencia crea la salida y la entrada vinculadas entre sí. El movimiento hecho por la API queda con created_channel igual a public_api.

En la serie, PATCH y DELETE aceptan ?scope=this, this_and_future o all, como la ventana de serie del panel, y el DELETE acepta también keep_paid (el predeterminado es true, como la ventana, que ya viene marcada). El PATCH cambia solo los campos enviados: lo que no viene, la situación incluida, queda como está. Un movimiento generado por una venta (source: sale) solo cambia categoría, observaciones y portal, y no se elimina; sale_id es el mismo id de GET /public/v1/sales/{id}. Un movimiento importado del banco (source: bank) también tiene los campos que la pantalla bloquea y no se elimina. Cambiar un campo protegido responde 409 conflict: es un conflicto con el estado del movimiento, no falta de permiso. mark_paid salda el movimiento abierto, y en un movimiento ya pagado lo devuelve como está, sin error; en una cuota de venta registra el cobro de la venta, y la respuesta es el nuevo movimiento cobrado; la cuota queda cancelada, así que repetir en el mismo id responde 409, sin cobrar dos veces. Para repetir con seguridad después de un timeout, envíe Idempotency-Key.

La eliminación nunca borra más de lo pedido. Los movimientos de una serie cuelgan de su PRIMER movimiento, así que eliminar ese primero con scope=this se llevaría la serie entera, y eliminar con keep_paid cuando ese primero no está pagado se llevaría también los pagados. En esos dos casos la API responde 409 conflict con param: "scope" y no elimina nada: elimine con scope=all (y keep_paid=false, para eliminar también los pagados) o a partir de otro movimiento de la serie.

Toda lectura de finanzas (movimientos, cuentas y categorías) agenda, como la pantalla, la sincronización de las ventas antiguas con finanzas, así que lo que usted lee es lo mismo que muestra el panel. Las cuentas traen el saldo actual calculado como la pantalla (current_balance_cents) y, en la tarjeta, el límite usado y el disponible; límite, cierre, vencimiento y alerta solo existen en la tarjeta y en el crédito de proveedor. Una cuenta con movimientos no se elimina (409), y solo el dueño y los administradores eliminan cuentas. Solo el dueño y los administradores crean, editan y eliminan categorías, y el color es uno de los 18 de la paleta de la pantalla (otro responde 422 con param color); la primera lectura de una empresa sin categorías crea las predeterminadas, como el panel. Los datos de Open Finance (identificadores externos, saldo sincronizado, conexión con el banco), de la conciliación y del adjunto nunca salen.

Eliminar un cliente borra el registro definitivamente, como en el panel, y se lleva direcciones, observaciones, vínculos de grupo, acceso al portal, inventario y obras. Órdenes de servicio, ventas, presupuestos, tareas y movimientos financieros siguen existiendo, sin el cliente.

La tabla de permisos de esta sección ya muestra el vocabulario completo de módulos y acciones, porque forma parte del contrato y es lo que el panel usa para crear claves. Tener el permiso marcado en la clave no significa que la ruta de ese módulo ya exista.

  • No existe un entorno de prueba. Toda llamada usa el dato real de su empresa.
  • La clave no gestiona claves. Crear, rotar y revocar ocurre en el panel, con la contraseña del usuario.
  • El costo y el margen no se exponen en la API.
  • La API no configura la empresa. El plan, los miembros y las preferencias siguen en el panel.

La API está disponible a partir del plan Start. En el plan gratuito la empresa no crea claves, y una llamada hecha con la clave de una empresa que perdió el recurso responde 403 con el código plan_feature_unavailable.

El límite de llamadas por minuto es de la empresa, sumando todas sus claves. GET /public/v1/me devuelve el límite vigente en limits.requests_per_minute.

Esta documentación existe en portugués, inglés y español. Lo único que cambia es el texto. El nombre de campo, la ruta, el valor de enum y el código de error son iguales en los tres idiomas.

En la API, el encabezado Accept-Language elige el idioma del mensaje de error (pt-BR es el predeterminado, en y es también se aceptan). El campo code del error nunca cambia de idioma: programe por él, nunca por el texto.