{"openapi":"3.1.0","info":{"title":"Pitflo · API pública /v1","version":"2026-09-03","description":"API pública de solo lectura de TU empresa en Pitflo. Cada empresa arma su propia integración desde su pantalla de Integraciones: elige recursos y campos (negado por omisión: nada nace expuesto) y emite un token `pfk_` que se enseña UNA sola vez.\n\nReglas del contrato:\n- Solo `GET`. La escritura por API no existe en v1. Las BAJAS sí te llegan en los recursos que ya tienen lápida: `customer.deleted`, `vehicle.deleted` y `service.deleted` te avisan con `{ id, deleted: true }` para que quites esa fila de tu copia (`service.restored` deshace la baja de la orden y viaja con la fila completa). En los demás recursos el borrado sigue sin evento — si copias comprobantes o inventario, reconcilia con `updated_since` (documentado de frente).\n- Autenticación: `Authorization: Bearer pfk_…`. El token es de SERVIDOR: jamás en una app web o móvil. Una cookie de sesión sobre `/v1` es 403 de plano.\n- Cada respuesta trae SOLO los campos marcados en la integración; los campos con `x-pii: true` son datos personales y la pantalla los agrupa bajo advertencia.\n- El contrato entero está en INGLÉS: nombres de recursos, de campos, de filtros y códigos de error. Esta prosa es lo único bilingüe (`GET /v1/openapi.json?lang=en`).\n- Paginación por cursor firmado y opaco (`{ data, cursor }`; `cursor: null` = no hay más). El cursor está atado a tu token, al recurso y a los filtros: no es portable.\n- `updated_since` filtra por `updatedAt >= …`; en los libros append-only (`service_payments`, `expense_payments`, `inventory_movements`, `cash_movements`, `service_stage_transitions`), por `createdAt`. Deduplica por `id`: el filtro es `gte` a propósito.\n- Límite: 600 peticiones/min por token; sin token válido, cubo estricto por IP. 429 responde con `Retry-After`.\n- Dinero SIEMPRE en centavos, tal cual la base; el consumidor formatea. Las cantidades de inventario van en MILÉSIMAS de la unidad base (1000 = 1 pieza/litro).\n- Errores en formato canónico `{ code, message, details }`.\n\nUn cambio incompatible del contrato estrena `/v2`; este documento cubre `/v1` y se acompaña del changelog (`x-changelog`).","x-changelog":[{"fecha":"2026-09-03","cambios":["EL VOCABULARIO DE EVENTOS PASA DE 16 A 46, y ninguno estrena recurso: todos laten por recursos que /v1 ya expone con sus campos, así que lo único que tienes que hacer para oírlos es suscribirte. La regla no cambia: un evento solo late si tu integración tiene marcado su recurso.","LA ORDEN, más allá de nacer y de moverse de columna: `service.updated` (cambiaron sus datos sin cambiar de etapa), `service.delivered` (el coche YA SALIÓ: `service.stage_changed` no sabía distinguir «terminado» de «entregado»), `service.hold_changed` (entró o salió de espera, con su motivo), `service.quote_updated` (cambió el total cotizado porque una línea nació, se editó o se borró), `service.quote_expired` (se acabó la vigencia sin decisión del cliente: `service.quote_decided` solo cubre aprobado y rechazado), `service.technician_assigned`, `service.rework_opened` (esta orden es un retrabajo, y su proyección dice de cuál nació), `service.aging_alert` (el barrido de antigüedad la marcó estancada) y la pareja `service.deleted` / `service.restored`.","`service.delivered` ENTREGA LA FILA DE LA ORDEN ENTREGADA, ni más ni menos: los campos de `services` que tengas marcados, igual que cualquier otro evento de la orden. NO viajan ahí quién recibió el coche ni el odómetro de salida —no son columnas de `services` y esta API no inventa campos para un webhook—; el kilómetro de salida se lee en `vehicles.mileage`, que la entrega deja al día cuando se capturó y es mayor al último conocido.","`service.deleted` ES LÁPIDA, con la misma regla que las del expediente: entrega `data: { id, deleted: true }` y ningún campo de la fila. `service.restored` no lo es — ahí la orden vuelve a estar viva y se proyecta entera.","LA CAJA Y EL EGRESO, para que la contabilidad deje de esperar al reporte manual: `cash_session.opened`, `cash_session.closed` (con su arqueo y su diferencia), `cash_movement.recorded` (el dinero que entra o sale del cajón FUERA de un cobro: retiro, depósito, reposición, fondo inicial y la devolución en efectivo de una nota de crédito, que hasta hoy no latía por ningún evento), `opening_receivable.collected` (el cobro de la cartera que migraste, que no cuelga de ninguna orden y por eso `payment.recorded` nunca lo vio), `expense.created`, `expense.cancelled`, `expense_payment.recorded` y `expense_payment.reversed`.","EL ALMACÉN: `inventory_receipt.created` y `.cancelled`, `purchase_order.created` y `.cancelled`, `supplier_return.created` y `.cancelled`, `inventory_adjustment.recorded` (la merma o la corrección, como fila del kardex con su valor), `inventory_count.applied` e `inventory.stock_low`.","`inventory.stock_low` LATE POR `inventory_stock`, no por `products`, y es a propósito: el hecho es la EXISTENCIA que quedó bajo mínimo, con su sucursal y su cantidad dentro; con el producto solo no podrías contestar «¿de qué sucursal?». Se deduplica como el aviso interno — mientras no se reponga no vuelve a cruzar—, así que no recibes el mismo grito dos veces.","EL PAPEL Y LA GENTE: `remission.cancelled` (el par que le faltaba a `remission.issued`, igual que `invoice.cancelled` del lado de la factura) y `employee.clocked_in` / `employee.clocked_out`. Los dos de asistencia laten por `employees` y entregan a la PERSONA en su vista mínima: el marcaje crudo —GPS, foto y notas— no es recurso de esta API y no va a serlo; el cuándo es el `createdAt` del cuerpo de la entrega.","HACIA EL TOKEN DE RED solo se suman los de la ORDEN que su panel ya ve (`network_services`): `service.updated`, `service.delivered`, `service.hold_changed`, `service.quote_updated` y `service.quote_expired`. La caja, el gasto, el almacén, la remisión, la asistencia, el retrabajo, la alerta de antigüedad y el técnico asignado no tienen recurso de red y para ella no existen.","EL EXPEDIENTE ESTRENA EVENTOS. `customers` y `vehicles` eran los dos recursos que esta API exponía sin un solo evento que oír: para mantener tu copia al día no te quedaba más que barrer `updated_since`. Nacen seis: `customer.created`, `customer.updated`, `customer.deleted`, `vehicle.created`, `vehicle.updated` y `vehicle.deleted`. Se suscriben como cualquier otro y solo laten si tu integración tiene marcado `customers` o `vehicles` según corresponda.","LAS BAJAS LLEGAN COMO LÁPIDA, dicho de frente porque no se parece a los demás eventos: `customer.deleted` y `vehicle.deleted` entregan `data: { id, deleted: true }` y ningún campo de la fila, ni siquiera los que tengas marcados. El borrado es lógico y esta API nunca sirve filas borradas —esa promesa no se rompe ni para un webhook—, así que lo que te mandamos es lo único que necesitas: cuál quitar de tu copia. El `id` es el mismo con el que te llegó al crearse y el cuándo es el `createdAt` del cuerpo. Si el registro se restaura antes de que salga la entrega, el aviso no se manda.","BORRAR UN CLIENTE ARRASTRA SUS VEHÍCULOS y aun así recibes UN SOLO `customer.deleted`, no un `vehicle.deleted` por coche: da por bajas sus unidades con ese latido. Es la diferencia que le importa a quien copia una flotilla de cien camiones.","Los eventos del expediente son de EMPRESA y no existen para el token de RED: el panel de red ve las órdenes de sus talleres vinculados, jamás el padrón de clientes de otra empresa.","Los recursos y sus campos NO cambian: lo único que se movió es el vocabulario de eventos."],"huella":"328e0dea3d5ec29cf44f992fb738ce14561d5b3cf34050e23fa7de8226a667e7"},{"fecha":"2026-09-02","cambios":["`vehicles` ESTRENA `colorKey`: la clave del catálogo de colores comunes en México (`blanco`, `negro`, `gris`, `plata`, `gris_oxford`, `rojo`, `azul`, `azul_marino`, `verde`, `amarillo`, `naranja`, `cafe`, `beige`, `arena`, `dorado`, `vino`, `morado`, `rosa`). Es el campo con el que por fin se puede AGRUPAR o COMPARAR por color: hasta hoy solo viajaba `color` en texto libre, y «Blanco», «blanco» y «BLANCO PERLA» eran tres colores distintos para cualquier integración.","`vehicles.color` NO cambia de nombre ni de forma, pero SÍ de garantía: cuando `colorKey` viene, `color` es la etiqueta canónica de esa clave —«Blanco», «Azul marino»—, sincronizada por el sistema. Cuando `colorKey` es `null` el vehículo tiene color libre («Otro») y `color` sigue siendo, palabra por palabra, lo que capturó el taller. Los vehículos que ya existían se reconciliaron contra el catálogo por texto: lo que casó ganó su clave y su etiqueta; lo que no casó se quedó intacto con `colorKey: null` — ningún color capturado se reinterpretó a la fuerza.","Los demás recursos, campos y eventos no cambian."],"huella":"0c7a571009fefc103aace504654ac20e4d0be20a039d5bf295ea5e062657ac08"},{"fecha":"2026-09-02","cambios":["CAMBIO DE CONTRATO (rompe compatibilidad) — remissions deja de exponer `issuerLogoUrl`. El campo prometía «el logo que se imprimió en el papel» y lo que viajaba era un identificador interno de archivo, sin ninguna forma de canjearlo por una imagen: servirlo era una promesa que el contrato no podía cumplir. Sale por el mismo criterio con el que `workshops` nunca expuso su `logoAssetId`. Si tu integración lo tenía marcado, vuelve a marcar los campos en la pantalla de Integraciones — `issuerLogoUrl` ya no es elegible. El logo sigue imprimiéndose en el PDF de la remisión, que es donde el papel lo lleva.","workshops.logoUrl SE DOCUMENTA COMO LEGADO, dicho de frente porque su significado cambió y el nombre no: es una columna heredada del stack anterior que ninguna pantalla puede fijar hoy. Desde esta fecha el logo del taller se configura como ARCHIVO —a nivel empresa, y lo heredan todas sus sucursales, o propio de una sucursal que lo sobreescribe—, y ninguno de esos dos viaja por este campo. Una sucursal puede devolver `logoUrl: null` y aun así imprimir su logo en la propuesta, en la remisión y en el reporte público. El campo NO se retira: los talleres migrados lo traen y quitarlo les rompería el membrete.","Los demás recursos, campos y eventos no cambian."],"huella":"11b2cef44886488c3d8ad82c6c73e6940d53f2fc9290e352a94b31dfed869df9"},{"fecha":"2026-08-29","cambios":["NACEN DOS RECURSOS con dato de TRABAJADORES, en solo lectura y con las mismas reglas de siempre (allowlist negada por omisión, filtro `workshop`, `updated_since`, cursor firmado y marca pii): `employees` —el padrón de personas en vista MÍNIMA: `id`, `roleId`, `status`, `workshopIds`, `createdAt`, `updatedAt`, más `name` y `email` marcados como dato personal— y `audit_log` —la bitácora: `occurredAt`, `workshopId`, `actorType`, `actorId` (pii), `action`, `resourceType` y `resourceId`—. Con `employees` los `…ByUserId` que esta API devuelve por todos lados por fin se resuelven a un nombre.","LO QUE ESOS DOS RECURSOS NO TRAEN, dicho de frente para que nadie lo espere: de `employees`, ni el hash de la contraseña ni su reloj (`passwordChangedAt`, intentos fallidos, bloqueos), ni sesiones, ni las preferencias de la persona (tema, zona horaria, idioma, avisos), ni sus assets de avatar y firma, ni la última conexión, ni `scope` —el alcance útil ya viaja como `workshopIds`—; de `audit_log`, el campo `metadata`, que es JSON libre donde cabe dato personal de un tercero. Ninguna de esas exclusiones es un descuido: son la autorización tal como se dio.","`employees.workshopIds` es un DERIVADO, no una columna: las sucursales se re-resuelven en cada consulta desde los accesos VIVOS de la persona, así que un acceso revocado desaparece de la lista al instante, y `workshop={id}` filtra el padrón por ese mismo acceso vivo (una persona no tiene columna de sucursal). Arreglo vacío = todavía no le han dado ninguna.","`audit_log` estrena un CURSOR NUEVO: ordena, pagina y responde `updated_since` por `occurredAt`. No tiene `updatedAt` y no le hace falta — sus filas se escriben una vez y jamás se editan ni se borran, así que paginar por el hecho no te hace perder ningún cambio."],"huella":"5b2d0fa43415f7b24c63b35b4ba4d00937aa897f7be6d40160f27ee81013e6dc"},{"fecha":"2026-08-26","cambios":["El esquema `error` se documenta como cualquier otro: `code`, `message` y `details` estrenan descripción (bilingüe, como toda la prosa del portal) y el esquema trae EJEMPLO de respuesta. Nada cambia en el contrato — mismos campos, mismos códigos —: era el único esquema publicado sin prosa, y la prueba de CI ahora barre `components.schemas` ENTERO y no solo los recursos, para que ningún esquema nuevo vuelva a nacer pelado.","DICHO DE FRENTE: los VALORES del enum `remissions.status` siguen en español (`emitida`, `amparada`, `cancelada`). El contrato inglés cubre NOMBRES —recursos, campos, filtros, códigos de error—; estos son datos, viven así en la base y renombrarlos rompería a quien ya compara contra ellos sin ganar nada. Están descritos en español y en inglés en la documentación; si algún día cambian, será con su entrada aquí y aviso previo."],"huella":"f5433fe054ca576c39fe404dfaa17abc2ea9908e562bea7aa0b6fefd5b4d5bc5"},{"fecha":"2026-08-26","cambios":["CAMBIO DE CONTRATO (rompe compatibilidad) — el ÚLTIMO campo en español de /v1 pasa a inglés. remissions · amparadaAt → coveredAt (cuándo quedó amparada por una factura). El dato es el mismo y la columna interna no cambió; solo el nombre del contrato. Si tu integración lo tenía marcado, vuelve a marcarlo en la pantalla de Integraciones: `amparadaAt` ya no es elegible.","LAS ÚNICAS PALABRAS QUE NO SE TRADUCEN, dichas de frente: `folio` (el consecutivo del papel mexicano, en services, inventory_receipts, inventory_counts, purchase_orders, supplier_returns, cash_sessions, fiscal_documents, remissions y network_services), `objectImpDr` (el ObjetoImp del complemento de documento relacionado, en payment_applications), `receiverCfdiUse`, `satCode`, `satPaymentForm`, `satProductKey`, `satRelationType` y `satUnitKey`. Son vocabulario del SAT: traducirlos te obligaría a destraducirlos para hablar con la autoridad. Todo lo demás del contrato es inglés, y hay prueba de CI que revisa palabra por palabra cada nombre de campo y truena con cualquier otra."],"huella":"f5433fe054ca576c39fe404dfaa17abc2ea9908e562bea7aa0b6fefd5b4d5bc5"},{"fecha":"2026-08-26","cambios":["CAMBIO DE CONTRATO (rompe compatibilidad) — los dos recursos de RED pasan a inglés. El contrato de /v1 es 100 % INGLÉS: nombres de recursos, de campos, de filtros y de códigos de error. Los recursos de red nacieron con los nombres del panel (una pantalla en español) y eran la única mezcla que quedaba. Se hace HOY, en pre-lanzamiento y SIN clientes productivos sobre /v1: no hay periodo de gracia porque no hay a quién romperle nada. Si tu integración es de las de prueba, vuelve a marcar los campos en la pantalla de Integraciones — los nombres viejos ya no son elegibles.","network_workshops · nombre → name · marca → brand · ciudad → city · estado → state · huso → timezone · moneda → currency · activa → active · empresa → organization (y dentro, empresa.nombre → organization.name) · vinculadaDesde → linkedSince.","network_services · creadaEn → createdAt · etapa → stage · etapaNombre → stageName · estadoDelPresupuesto → quoteStatus · decididoEn → decidedAt · aprobadoCentavos → approvedCents · facturadoCentavos → invoicedCents · vehiculo → vehicle (y dentro, vehiculo.marca → vehicle.make · vehiculo.modelo → vehicle.model · vehiculo.anio → vehicle.year).","NACEN 14 recursos de solo lectura, con las mismas reglas de siempre (allowlist negada por omisión, filtro `workshop`, `updated_since`, cursor firmado y marca pii): workshops (el padrón de sucursales, para resolver a nombre cualquier workshopId), service_stage_transitions (la bitácora de etapas del tablero), inventory_movements (el kardex), inventory_receipts, inventory_counts, purchase_orders, supplier_returns, cash_sessions (los cortes de caja), cash_movements (el libro de la caja), payment_applications (el complemento de pago desglosado), opening_receivables (la cartera importada del arranque), expense_payments (los pagos del gasto), fixed_assets y ledger_account_maps (el mapeo concepto→cuenta del contador).","Los libros APPEND-ONLY se amplían: además de service_payments, ahora también expense_payments, inventory_movements, cash_movements y service_stage_transitions ordenan, paginan y filtran `updated_since` por `createdAt`. Ninguno tiene `updatedAt` porque una fila suya no se edita: se contra-asienta con otra fila, que te llega como página nueva.","DOS RECURSOS QUE SE PIDIERON Y NO ESTÁN, dichos de frente: `journal_entries` (pólizas) no existe como tabla — la póliza se regenera de las filas del mes cerrado, así que exponerla sería inventar un contrato derivado que nadie ha decidido; y `monthly_closes` (cierres de mes) se queda fuera porque su fila muta al REABRIR un mes y no tiene sello que se mueva: `updated_since` no vería la reapertura y te dejaría con un mes cerrado que ya no lo está.","La DOCUMENTACIÓN deja de ser una lista de nombres: cada recurso y cada campo llevan descripción y tipo real, y cada recurso trae un EJEMPLO de respuesta con datos ficticios. La prosa es bilingüe: `GET /v1/openapi.json` en español, `GET /v1/openapi.json?lang=en` en inglés (el contrato sigue siendo inglés en los dos).","Una integración puede marcar hasta 40 recursos (antes 20): con el vocabulario nuevo, el tope viejo dejaba fuera una integración completa."],"huella":"a7df22c68a595f5dcdc8cf56984c4f9f9e8c7b354a664053e964f36c6ecec8a6"},{"fecha":"2026-08-26","cambios":["Nace POST /v1/webhooks/{id}/ping: con el token de la integración DUEÑA del webhook, entrega el evento ping FIRMADO (misma firma y mismo transporte que una entrega real, con doble firma si la rotación del secreto sigue viva) a la URL registrada y devuelve el resultado — { delivery: { id, event, status, createdAt }, result: { ok, httpStatus, durationMs, reason? } }. Es la ÚNICA ruta no-GET de /v1 y no escribe datos de nadie. Un webhook ajeno o inexistente responde el mismo 404; apagado, 409 WEBHOOK_DISABLED. Límite: 10 pings/min por token.","CORS: /v1 responde cabeceras CORS al origen del portal de desarrolladores de Pitflo (y solo a él), con preflight que anuncia Authorization, para que su playground consulte con tu token desde tu navegador. La regla del token no cambia: sigue siendo de servidor y tu propio sitio no puede llamar /v1 desde el navegador.","Los recursos, sus campos y el vocabulario de eventos NO cambian: la huella de la superficie es la misma que la del 2026-08-19."],"huella":"0c7acfc38b70fee71f9272a40663181100667d4d06b91519aab3df4364695567"},{"fecha":"2026-08-19","cambios":["Nace la documentación pública del API: este documento OpenAPI (GET /v1/openapi.json, sin token), el portal /developers y este changelog. El contrato /v1 no cambia: 13 recursos de empresa y 2 de red, solo lectura, con los campos elegibles y su marca pii tal como estaban.","Vocabulario de eventos de webhook documentado: service.created, service.stage_changed, service.quote_decided, payment.recorded, payment.reversed, invoice.stamped, invoice.cancelled, appointment.created, appointment.status_changed, remission.issued y ping (solo prueba)."],"huella":"0c7acfc38b70fee71f9272a40663181100667d4d06b91519aab3df4364695567"}],"x-lang":"es","x-fleet":{"recursos":["fleet_units","fleet_unit_specs","fleet_meter_readings","fleet_assignments","fleet_reservations","fleet_charges","fleet_fuel_logs","fleet_expenses","fleet_expense_schedules","fleet_fuel_exceptions","fleet_fuel_cards","fleet_fuel_transactions","fleet_documents","fleet_renewals","fleet_warranties","fleet_inspections","fleet_inspection_items","fleet_inspection_schedules","fleet_issues","fleet_faults","fleet_work_orders","fleet_work_order_lines","fleet_service_tasks","fleet_service_entries","fleet_service_entry_lines","fleet_maintenance_programs","fleet_unit_programs","fleet_reminder_cycles","fleet_parts","fleet_part_locations","fleet_inventory_stock","fleet_part_movements","fleet_part_receipts","fleet_part_transfers","fleet_part_counts","fleet_supplier_returns","fleet_warranty_claims","fleet_purchase_orders","fleet_purchase_order_lines","fleet_vendors","fleet_contacts","fleet_groups","fleet_unit_types","fleet_labels","fleet_custom_fields","fleet_workshop_links","fleet_comments","fleet_bulk_jobs","fleet_import_batches"],"eventos":[{"evento":"fleet.unit.created","recursosHabilitantes":["fleet_units"]},{"evento":"fleet.unit.updated","recursosHabilitantes":["fleet_units"]},{"evento":"fleet.unit.status_changed","recursosHabilitantes":["fleet_units"]},{"evento":"fleet.unit.deleted","recursosHabilitantes":["fleet_units"]},{"evento":"fleet.meter_reading.created","recursosHabilitantes":["fleet_meter_readings"]},{"evento":"fleet.meter_reading.updated","recursosHabilitantes":["fleet_meter_readings"]},{"evento":"fleet.meter_reading.deleted","recursosHabilitantes":["fleet_meter_readings"]},{"evento":"fleet.meter_reading.voided","recursosHabilitantes":["fleet_meter_readings"]},{"evento":"fleet.meter_reading.unvoided","recursosHabilitantes":["fleet_meter_readings"]},{"evento":"fleet.meter_reading.reset","recursosHabilitantes":["fleet_meter_readings"]},{"evento":"fleet.meter_reading.imported","recursosHabilitantes":["fleet_meter_readings"]},{"evento":"fleet.meter_reading.ingested","recursosHabilitantes":["fleet_meter_readings"]},{"evento":"fleet.fuel_log.created","recursosHabilitantes":["fleet_fuel_logs"]},{"evento":"fleet.fuel_log.updated","recursosHabilitantes":["fleet_fuel_logs"]},{"evento":"fleet.fuel_log.voided","recursosHabilitantes":["fleet_fuel_logs"]},{"evento":"fleet.fuel_log.duplicate_suspected","recursosHabilitantes":["fleet_fuel_logs"]},{"evento":"fleet.fuel_log.deleted","recursosHabilitantes":["fleet_fuel_logs"]},{"evento":"fleet.fuel_exception.opened","recursosHabilitantes":["fleet_fuel_exceptions"]},{"evento":"fleet.fuel_exception.confirmed","recursosHabilitantes":["fleet_fuel_exceptions"]},{"evento":"fleet.fuel_exception.justified","recursosHabilitantes":["fleet_fuel_exceptions"]},{"evento":"fleet.fuel_transaction.imported","recursosHabilitantes":["fleet_fuel_transactions"]},{"evento":"fleet.fuel_transaction.matched","recursosHabilitantes":["fleet_fuel_transactions"]},{"evento":"fleet.fuel_transaction.discarded","recursosHabilitantes":["fleet_fuel_transactions"]},{"evento":"fleet.expense.created","recursosHabilitantes":["fleet_expenses"]},{"evento":"fleet.expense.updated","recursosHabilitantes":["fleet_expenses"]},{"evento":"fleet.expense.cancelled","recursosHabilitantes":["fleet_expenses"]},{"evento":"fleet.expense.deleted","recursosHabilitantes":["fleet_expenses"]},{"evento":"fleet.expense_schedule.generated","recursosHabilitantes":["fleet_expense_schedules"]},{"evento":"fleet.expense_schedule.paused","recursosHabilitantes":["fleet_expense_schedules"]},{"evento":"fleet.expense_schedule.resumed","recursosHabilitantes":["fleet_expense_schedules"]},{"evento":"fleet.document.created","recursosHabilitantes":["fleet_documents"]},{"evento":"fleet.document.expiring","recursosHabilitantes":["fleet_documents"]},{"evento":"fleet.document.expired","recursosHabilitantes":["fleet_documents"]},{"evento":"fleet.document.auto_deleted","recursosHabilitantes":["fleet_documents"]},{"evento":"fleet.document.deleted","recursosHabilitantes":["fleet_documents"]},{"evento":"fleet.renewal.due","recursosHabilitantes":["fleet_renewals"]},{"evento":"fleet.renewal.overdue","recursosHabilitantes":["fleet_renewals"]},{"evento":"fleet.renewal.renewed","recursosHabilitantes":["fleet_renewals"]},{"evento":"fleet.warranty.expiring","recursosHabilitantes":["fleet_warranties"]},{"evento":"fleet.inspection.submitted","recursosHabilitantes":["fleet_inspections"]},{"evento":"fleet.inspection.item_failed","recursosHabilitantes":["fleet_inspections"]},{"evento":"fleet.inspection.overdue","recursosHabilitantes":["fleet_inspections","fleet_inspection_schedules"]},{"evento":"fleet.inspection.dvir_defect_uncertified","recursosHabilitantes":["fleet_inspections"]},{"evento":"fleet.inspection.item_out_of_range","recursosHabilitantes":["fleet_inspections","fleet_inspection_items"]},{"evento":"fleet.issue.created","recursosHabilitantes":["fleet_issues"]},{"evento":"fleet.issue.assigned","recursosHabilitantes":["fleet_issues"]},{"evento":"fleet.issue.status_changed","recursosHabilitantes":["fleet_issues"]},{"evento":"fleet.issue.resolved","recursosHabilitantes":["fleet_issues"]},{"evento":"fleet.issue.closed","recursosHabilitantes":["fleet_issues"]},{"evento":"fleet.issue.reopened","recursosHabilitantes":["fleet_issues"]},{"evento":"fleet.fault.created","recursosHabilitantes":["fleet_faults"]},{"evento":"fleet.fault.triaged","recursosHabilitantes":["fleet_faults"]},{"evento":"fleet.fault.resolved","recursosHabilitantes":["fleet_faults"]},{"evento":"fleet.work_order.created","recursosHabilitantes":["fleet_work_orders"]},{"evento":"fleet.work_order.status_changed","recursosHabilitantes":["fleet_work_orders"]},{"evento":"fleet.work_order.completed","recursosHabilitantes":["fleet_work_orders"]},{"evento":"fleet.service_entry.created","recursosHabilitantes":["fleet_service_entries"]},{"evento":"fleet.reminder.due_soon","recursosHabilitantes":["fleet_unit_programs"]},{"evento":"fleet.reminder.overdue","recursosHabilitantes":["fleet_unit_programs"]},{"evento":"fleet.reminder.completed","recursosHabilitantes":["fleet_unit_programs"]},{"evento":"fleet.reminder.snoozed","recursosHabilitantes":["fleet_unit_programs","fleet_reminder_cycles"]},{"evento":"fleet.reminder.next_due_overridden","recursosHabilitantes":["fleet_unit_programs","fleet_reminder_cycles"]},{"evento":"fleet.reminder.cycle_reopened","recursosHabilitantes":["fleet_unit_programs","fleet_reminder_cycles"]},{"evento":"fleet.program.assigned","recursosHabilitantes":["fleet_maintenance_programs"]},{"evento":"fleet.assignment.created","recursosHabilitantes":["fleet_assignments"]},{"evento":"fleet.assignment.closed","recursosHabilitantes":["fleet_assignments"]},{"evento":"fleet.assignment.cancelled","recursosHabilitantes":["fleet_assignments"]},{"evento":"fleet.assignment.updated","recursosHabilitantes":["fleet_assignments"]},{"evento":"fleet.assignment.deleted","recursosHabilitantes":["fleet_assignments"]},{"evento":"fleet.reservation.created","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.reservation.approved","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.reservation.denied","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.reservation.cancelled","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.reservation.checked_out","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.reservation.checked_in","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.reservation.extended","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.reservation.returned_late","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.reservation.conflicted","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.reservation.no_show","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.reservation.deleted","recursosHabilitantes":["fleet_reservations"]},{"evento":"fleet.charge.drafted","recursosHabilitantes":["fleet_charges"]},{"evento":"fleet.charge.issued","recursosHabilitantes":["fleet_charges"]},{"evento":"fleet.charge.cancelled","recursosHabilitantes":["fleet_charges"]},{"evento":"fleet.charge.adjusted","recursosHabilitantes":["fleet_charges"]},{"evento":"fleet.comment.created","recursosHabilitantes":["fleet_comments"]},{"evento":"fleet.mention.created","recursosHabilitantes":["fleet_comments"]},{"evento":"fleet.alert.triggered","recursosHabilitantes":["fleet_units"]},{"evento":"fleet.place.created","recursosHabilitantes":["fleet_places"]},{"evento":"fleet.place.updated","recursosHabilitantes":["fleet_places"]},{"evento":"fleet.place.archived","recursosHabilitantes":["fleet_places"]},{"evento":"fleet.obra.created","recursosHabilitantes":["fleet_jobs"]},{"evento":"fleet.obra.status_changed","recursosHabilitantes":["fleet_jobs"]},{"evento":"fleet.obra.unit_assigned","recursosHabilitantes":["fleet_jobs"]},{"evento":"fleet.obra.unit_unassigned","recursosHabilitantes":["fleet_jobs"]},{"evento":"fleet.obra.cost_reattributed","recursosHabilitantes":["fleet_jobs"]},{"evento":"fleet.geofence.entered","recursosHabilitantes":["fleet_places"]},{"evento":"fleet.geofence.exited","recursosHabilitantes":["fleet_places"]},{"evento":"fleet.geofence.dwell_exceeded","recursosHabilitantes":["fleet_places"]},{"evento":"fleet.purchase_order.created","recursosHabilitantes":["fleet_purchase_orders"]},{"evento":"fleet.purchase_order.approval_requested","recursosHabilitantes":["fleet_purchase_orders"]},{"evento":"fleet.purchase_order.approved","recursosHabilitantes":["fleet_purchase_orders"]},{"evento":"fleet.purchase_order.rejected","recursosHabilitantes":["fleet_purchase_orders"]},{"evento":"fleet.purchase_order.sent","recursosHabilitantes":["fleet_purchase_orders"]},{"evento":"fleet.purchase_order.partially_received","recursosHabilitantes":["fleet_purchase_orders"]},{"evento":"fleet.purchase_order.received","recursosHabilitantes":["fleet_purchase_orders"]},{"evento":"fleet.purchase_order.cancelled","recursosHabilitantes":["fleet_purchase_orders"]},{"evento":"fleet.receipt.created","recursosHabilitantes":["fleet_part_receipts"]},{"evento":"fleet.receipt.cancelled","recursosHabilitantes":["fleet_part_receipts"]},{"evento":"fleet.part.created","recursosHabilitantes":["fleet_parts"]},{"evento":"fleet.part.archived","recursosHabilitantes":["fleet_parts"]},{"evento":"fleet.part.low_stock","recursosHabilitantes":["fleet_parts","fleet_inventory_stock"]},{"evento":"fleet.part.out_of_stock","recursosHabilitantes":["fleet_parts","fleet_inventory_stock"]},{"evento":"fleet.part_movement.created","recursosHabilitantes":["fleet_part_movements"]},{"evento":"fleet.part_movement.cancelled","recursosHabilitantes":["fleet_part_movements"]},{"evento":"fleet.count.opened","recursosHabilitantes":["fleet_part_counts"]},{"evento":"fleet.count.applied","recursosHabilitantes":["fleet_part_counts"]},{"evento":"fleet.transfer.created","recursosHabilitantes":["fleet_part_transfers"]},{"evento":"fleet.transfer.received","recursosHabilitantes":["fleet_part_transfers"]},{"evento":"fleet.supplier_return.created","recursosHabilitantes":["fleet_supplier_returns"]},{"evento":"fleet.supplier_return.cancelled","recursosHabilitantes":["fleet_supplier_returns"]},{"evento":"fleet.warranty_claim.opened","recursosHabilitantes":["fleet_warranty_claims"]},{"evento":"fleet.warranty_claim.approved","recursosHabilitantes":["fleet_warranty_claims"]},{"evento":"fleet.warranty_claim.rejected","recursosHabilitantes":["fleet_warranty_claims"]},{"evento":"fleet.warranty_claim.credited","recursosHabilitantes":["fleet_warranty_claims"]},{"evento":"fleet.import_batch.applied","recursosHabilitantes":["fleet_import_batches"]},{"evento":"fleet.import_batch.reverted","recursosHabilitantes":["fleet_import_batches"]},{"evento":"fleet.vendor.created","recursosHabilitantes":["fleet_vendors"]},{"evento":"fleet.vendor.merged","recursosHabilitantes":["fleet_vendors"]},{"evento":"fleet.quote.received","recursosHabilitantes":["fleet_quotes"]},{"evento":"fleet.quote.decided","recursosHabilitantes":["fleet_quotes","fleet_approvals"]},{"evento":"fleet.quote.auto_approved","recursosHabilitantes":["fleet_quotes","fleet_approvals"]},{"evento":"fleet.recall.opened","recursosHabilitantes":["fleet_recalls"]},{"evento":"fleet.recall.acknowledged","recursosHabilitantes":["fleet_recalls"]},{"evento":"fleet.recall.resolved","recursosHabilitantes":["fleet_recalls"]},{"evento":"fleet.recall.dismissed","recursosHabilitantes":["fleet_recalls"]},{"evento":"fleet.recall.reopened","recursosHabilitantes":["fleet_recalls"]},{"evento":"fleet.assistance.requested","recursosHabilitantes":["fleet_assistance_requests"]},{"evento":"fleet.assistance.authorized","recursosHabilitantes":["fleet_assistance_requests"]},{"evento":"fleet.assistance.accepted","recursosHabilitantes":["fleet_assistance_requests"]},{"evento":"fleet.assistance.resolved","recursosHabilitantes":["fleet_assistance_requests"]},{"evento":"fleet.assistance.cancelled","recursosHabilitantes":["fleet_assistance_requests"]},{"evento":"fleet.workshop_link.status_changed","recursosHabilitantes":["fleet_workshop_links"]},{"evento":"fleet.shop_review.created","recursosHabilitantes":["fleet_shop_reviews"]},{"evento":"fleet.shop_review.replied","recursosHabilitantes":["fleet_shop_reviews"]},{"evento":"fleet.work_order.cancelled","recursosHabilitantes":["fleet_work_orders"]},{"evento":"fleet.work_order.reopened","recursosHabilitantes":["fleet_work_orders"]},{"evento":"fleet.work_order.signed","recursosHabilitantes":["fleet_work_orders"]},{"evento":"fleet.work_order.signature_removed","recursosHabilitantes":["fleet_work_orders"]},{"evento":"fleet.work_order.sent","recursosHabilitantes":["fleet_work_orders"]},{"evento":"fleet.service_entry.updated","recursosHabilitantes":["fleet_service_entries"]},{"evento":"fleet.unit.disposed","recursosHabilitantes":["fleet_units"]},{"evento":"fleet.unit.disposal_reverted","recursosHabilitantes":["fleet_units"]},{"evento":"fleet.unit.odometer_corrected","recursosHabilitantes":["fleet_units"]},{"evento":"fleet.unit.group_changed","recursosHabilitantes":["fleet_units"]},{"evento":"fleet.warranty.created","recursosHabilitantes":["fleet_warranties"]},{"evento":"fleet.warranty.updated","recursosHabilitantes":["fleet_warranties"]},{"evento":"fleet.inspection_schedule.created","recursosHabilitantes":["fleet_inspection_schedules"]},{"evento":"fleet.inspection_schedule.updated","recursosHabilitantes":["fleet_inspection_schedules"]},{"evento":"fleet.issue.recurrence_seen","recursosHabilitantes":["fleet_issues"]},{"evento":"fleet.bulk.completed","recursosHabilitantes":["fleet_bulk_jobs"]}],"bulk":{"ruta":"/v1/bulk/{entidad}","estado":"/v1/bulk/jobs/{jobId}","cancelar":"/v1/bulk/jobs/{jobId}/cancelar","operaciones":["crear","actualizar","upsert"],"entidades":["fleet_units","fleet_unit_specs","fleet_contacts","fleet_groups","fleet_unit_types","fleet_labels","fleet_meter_readings","fleet_assignments","fleet_fuel_logs","fleet_expenses","fleet_expense_schedules","fleet_vendors","fleet_parts","fleet_part_locations","fleet_part_movements","fleet_part_receipts","fleet_purchase_orders","fleet_issues","fleet_faults","fleet_work_orders","fleet_service_tasks","fleet_service_entries","fleet_maintenance_programs","fleet_unit_programs","fleet_reminder_cycles","fleet_inspection_schedules","fleet_renewals","fleet_warranties","fleet_places","fleet_jobs"],"capacidadRequerida":"bulk"},"oauth":{"autorizar":"/oauth/authorize","token":"/oauth/token","revocar":"/oauth/revoke","grants":"/oauth/grants","cuentas":"/v1/accounts","alcances":["leer:fleet_units","leer:fleet_unit_specs","leer:fleet_meter_readings","leer:fleet_assignments","leer:fleet_reservations","leer:fleet_charges","leer:fleet_fuel_logs","leer:fleet_expenses","leer:fleet_expense_schedules","leer:fleet_fuel_exceptions","leer:fleet_fuel_cards","leer:fleet_fuel_transactions","leer:fleet_documents","leer:fleet_renewals","leer:fleet_warranties","leer:fleet_inspections","leer:fleet_inspection_items","leer:fleet_inspection_schedules","leer:fleet_issues","leer:fleet_faults","leer:fleet_work_orders","leer:fleet_work_order_lines","leer:fleet_service_tasks","leer:fleet_service_entries","leer:fleet_service_entry_lines","leer:fleet_maintenance_programs","leer:fleet_unit_programs","leer:fleet_reminder_cycles","leer:fleet_parts","leer:fleet_part_locations","leer:fleet_inventory_stock","leer:fleet_part_movements","leer:fleet_part_receipts","leer:fleet_part_transfers","leer:fleet_part_counts","leer:fleet_supplier_returns","leer:fleet_warranty_claims","leer:fleet_purchase_orders","leer:fleet_purchase_order_lines","leer:fleet_vendors","leer:fleet_contacts","leer:fleet_groups","leer:fleet_unit_types","leer:fleet_labels","leer:fleet_custom_fields","leer:fleet_workshop_links","leer:fleet_comments","leer:fleet_bulk_jobs","leer:fleet_import_batches","escribir:fleet_units","escribir:fleet_unit_specs","escribir:fleet_contacts","escribir:fleet_groups","escribir:fleet_unit_types","escribir:fleet_labels","escribir:fleet_meter_readings","escribir:fleet_assignments","escribir:fleet_fuel_logs","escribir:fleet_expenses","escribir:fleet_expense_schedules","escribir:fleet_vendors","escribir:fleet_parts","escribir:fleet_part_locations","escribir:fleet_part_movements","escribir:fleet_part_receipts","escribir:fleet_purchase_orders","escribir:fleet_issues","escribir:fleet_faults","escribir:fleet_work_orders","escribir:fleet_service_tasks","escribir:fleet_service_entries","escribir:fleet_maintenance_programs","escribir:fleet_unit_programs","escribir:fleet_reminder_cycles","escribir:fleet_inspection_schedules","escribir:fleet_renewals","escribir:fleet_warranties","escribir:fleet_places","escribir:fleet_jobs","cuentas:leer","perfil:leer"]},"limitesPorTier":[{"clave":"unidadesActivas","porPlan":{"fleet_5":5,"fleet_50":50,"fleet_100":100,"fleet_ilimitado":null}},{"clave":"usuarios","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"almacenamientoGiB","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"llamadasApiDelMes","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"apiReqPorMinuto","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"webhooksActivos","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"retencionDeDatos","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"rolesPersonalizados","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"recordSets","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"automatizacionesActivas","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"reportesGuardados","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"reportesProgramados","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"tableros","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"integracionesActivas","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"destinosDeDatos","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"conectoresActivos","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"bulkFilasPorJob","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"bulkJobsConcurrentes","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}},{"clave":"automatizacionEjecucionesPorDia","porPlan":{"fleet_5":null,"fleet_50":null,"fleet_100":null,"fleet_ilimitado":null}}]}},"servers":[{"url":"/"}],"security":[{"token_pfk":[]}],"paths":{"/v1/workshops":{"get":{"operationId":"listar_workshops","summary":"Lista workshops (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/workshops"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/workshops/{id}":{"get":{"operationId":"ver_workshops","summary":"Un workshops por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/workshops"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/services":{"get":{"operationId":"listar_services","summary":"Lista services (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/services"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/services/{id}":{"get":{"operationId":"ver_services","summary":"Un services por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/services"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/service_items":{"get":{"operationId":"listar_service_items","summary":"Lista service_items (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/service_items"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/service_items/{id}":{"get":{"operationId":"ver_service_items","summary":"Un service_items por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/service_items"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/service_stage_transitions":{"get":{"operationId":"listar_service_stage_transitions","summary":"Lista service_stage_transitions (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/service_stage_transitions"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/service_stage_transitions/{id}":{"get":{"operationId":"ver_service_stage_transitions","summary":"Un service_stage_transitions por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/service_stage_transitions"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/customers":{"get":{"operationId":"listar_customers","summary":"Lista customers (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/customers"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/customers/{id}":{"get":{"operationId":"ver_customers","summary":"Un customers por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/customers"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/vehicles":{"get":{"operationId":"listar_vehicles","summary":"Lista vehicles (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/vehicles"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/vehicles/{id}":{"get":{"operationId":"ver_vehicles","summary":"Un vehicles por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/vehicles"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/appointments":{"get":{"operationId":"listar_appointments","summary":"Lista appointments (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/appointments"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/appointments/{id}":{"get":{"operationId":"ver_appointments","summary":"Un appointments por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/appointments"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/products":{"get":{"operationId":"listar_products","summary":"Lista products (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/products"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/products/{id}":{"get":{"operationId":"ver_products","summary":"Un products por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/products"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/inventory_stock":{"get":{"operationId":"listar_inventory_stock","summary":"Lista inventory_stock (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/inventory_stock"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/inventory_stock/{id}":{"get":{"operationId":"ver_inventory_stock","summary":"Un inventory_stock por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inventory_stock"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/inventory_movements":{"get":{"operationId":"listar_inventory_movements","summary":"Lista inventory_movements (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/inventory_movements"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/inventory_movements/{id}":{"get":{"operationId":"ver_inventory_movements","summary":"Un inventory_movements por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inventory_movements"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/inventory_receipts":{"get":{"operationId":"listar_inventory_receipts","summary":"Lista inventory_receipts (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/inventory_receipts"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/inventory_receipts/{id}":{"get":{"operationId":"ver_inventory_receipts","summary":"Un inventory_receipts por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inventory_receipts"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/inventory_counts":{"get":{"operationId":"listar_inventory_counts","summary":"Lista inventory_counts (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/inventory_counts"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/inventory_counts/{id}":{"get":{"operationId":"ver_inventory_counts","summary":"Un inventory_counts por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/inventory_counts"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/purchase_orders":{"get":{"operationId":"listar_purchase_orders","summary":"Lista purchase_orders (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/purchase_orders"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/purchase_orders/{id}":{"get":{"operationId":"ver_purchase_orders","summary":"Un purchase_orders por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/purchase_orders"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/supplier_returns":{"get":{"operationId":"listar_supplier_returns","summary":"Lista supplier_returns (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/supplier_returns"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/supplier_returns/{id}":{"get":{"operationId":"ver_supplier_returns","summary":"Un supplier_returns por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/supplier_returns"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/service_templates":{"get":{"operationId":"listar_service_templates","summary":"Lista service_templates (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/service_templates"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/service_templates/{id}":{"get":{"operationId":"ver_service_templates","summary":"Un service_templates por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/service_templates"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/suppliers":{"get":{"operationId":"listar_suppliers","summary":"Lista suppliers (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/suppliers"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/suppliers/{id}":{"get":{"operationId":"ver_suppliers","summary":"Un suppliers por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/suppliers"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/service_payments":{"get":{"operationId":"listar_service_payments","summary":"Lista service_payments (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/service_payments"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/service_payments/{id}":{"get":{"operationId":"ver_service_payments","summary":"Un service_payments por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/service_payments"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/payment_applications":{"get":{"operationId":"listar_payment_applications","summary":"Lista payment_applications (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/payment_applications"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/payment_applications/{id}":{"get":{"operationId":"ver_payment_applications","summary":"Un payment_applications por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/payment_applications"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/opening_receivables":{"get":{"operationId":"listar_opening_receivables","summary":"Lista opening_receivables (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/opening_receivables"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/opening_receivables/{id}":{"get":{"operationId":"ver_opening_receivables","summary":"Un opening_receivables por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/opening_receivables"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/cash_sessions":{"get":{"operationId":"listar_cash_sessions","summary":"Lista cash_sessions (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/cash_sessions"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/cash_sessions/{id}":{"get":{"operationId":"ver_cash_sessions","summary":"Un cash_sessions por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/cash_sessions"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/cash_movements":{"get":{"operationId":"listar_cash_movements","summary":"Lista cash_movements (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/cash_movements"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/cash_movements/{id}":{"get":{"operationId":"ver_cash_movements","summary":"Un cash_movements por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/cash_movements"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/fiscal_documents":{"get":{"operationId":"listar_fiscal_documents","summary":"Lista fiscal_documents (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/fiscal_documents"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/fiscal_documents/{id}":{"get":{"operationId":"ver_fiscal_documents","summary":"Un fiscal_documents por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/fiscal_documents"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/expenses":{"get":{"operationId":"listar_expenses","summary":"Lista expenses (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/expenses"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/expenses/{id}":{"get":{"operationId":"ver_expenses","summary":"Un expenses por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/expenses"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/expense_payments":{"get":{"operationId":"listar_expense_payments","summary":"Lista expense_payments (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/expense_payments"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/expense_payments/{id}":{"get":{"operationId":"ver_expense_payments","summary":"Un expense_payments por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/expense_payments"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/remissions":{"get":{"operationId":"listar_remissions","summary":"Lista remissions (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/remissions"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/remissions/{id}":{"get":{"operationId":"ver_remissions","summary":"Un remissions por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/remissions"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/fixed_assets":{"get":{"operationId":"listar_fixed_assets","summary":"Lista fixed_assets (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/fixed_assets"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/fixed_assets/{id}":{"get":{"operationId":"ver_fixed_assets","summary":"Un fixed_assets por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/fixed_assets"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/ledger_account_maps":{"get":{"operationId":"listar_ledger_account_maps","summary":"Lista ledger_account_maps (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ledger_account_maps"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/ledger_account_maps/{id}":{"get":{"operationId":"ver_ledger_account_maps","summary":"Un ledger_account_maps por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ledger_account_maps"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/employees":{"get":{"operationId":"listar_employees","summary":"Lista employees (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/employees"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/employees/{id}":{"get":{"operationId":"ver_employees","summary":"Un employees por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/employees"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/audit_log":{"get":{"operationId":"listar_audit_log","summary":"Lista audit_log (orden estable, cursor firmado)","description":"Recurso de EMPRESA (token de empresa). Los borrados lógicos no salen.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/audit_log"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/audit_log/{id}":{"get":{"operationId":"ver_audit_log","summary":"Un audit_log por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/audit_log"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/network_workshops":{"get":{"operationId":"listar_network_workshops","summary":"Lista network_workshops (orden estable, cursor firmado)","description":"Recurso de RED (token de red): re-resuelve vínculos vivos y permisos de detalle vigentes en cada consulta.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/network_workshops"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/network_workshops/{id}":{"get":{"operationId":"ver_network_workshops","summary":"Un network_workshops por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/network_workshops"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/network_services":{"get":{"operationId":"listar_network_services","summary":"Lista network_services (orden estable, cursor firmado)","description":"Recurso de RED (token de red): re-resuelve vínculos vivos y permisos de detalle vigentes en cada consulta.","security":[{"token_pfk":[]}],"parameters":[{"name":"workshop","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra la sucursal en recursos operativos; se ignora en el expediente de empresa (customers, vehicles); en catálogos con fila corporativa (products, suppliers, service_templates, expenses, fixed_assets, ledger_account_maps) resuelve la sucursal MÁS el catálogo corporativo; en `workshops` filtra por el id de la sucursal misma."},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Solo filas actualizadas desde esta fecha (gte). En los libros append-only filtra por `createdAt`."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"El cursor opaco de la página anterior. De otro token, otro recurso u otros filtros: 400 INVALID_CURSOR."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Tamaño de página; 50 por omisión, 100 como tope."}],"responses":{"200":{"description":"Página con SOLO los campos marcados en tu integración.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/network_services"}},"cursor":{"type":["string","null"],"description":"null = no hay más páginas."}},"required":["data","cursor"]}}}},"400":{"$ref":"#/components/responses/error_400"},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/network_services/{id}":{"get":{"operationId":"ver_network_services","summary":"Un network_services por id","description":"El id ajeno y el id inexistente responden EL MISMO 404: fuera de alcance no existe.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del recurso (ULID). Un id de otra empresa responde el mismo 404 que uno inexistente."}],"responses":{"200":{"description":"El objeto, solo con los campos marcados.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/network_services"}}}},"401":{"$ref":"#/components/responses/error_401"},"403":{"$ref":"#/components/responses/error_403"},"404":{"$ref":"#/components/responses/error_404"},"429":{"$ref":"#/components/responses/error_429"}}}},"/v1/webhooks/{id}/ping":{"post":{"operationId":"ping_webhook","summary":"Manda un ping firmado a tu webhook y devuelve el resultado","description":"Entrega SÍNCRONA: el cuerpo y el header `Pitflo-Firma` son EXACTAMENTE los de una entrega real (incluida la doble firma durante la ventana de rotación del secreto), así que si tu verificador acepta este ping, aceptará tus eventos.\nEl webhook debe ser de la integración de TU token: cualquier otro id responde 404. Un webhook apagado responde 409.\nLímite: 10 pings/min por token (cada ping es un POST a un tercero). Queda en tu bitácora de entregas como un `ping` con su desenlace, sin tocar la racha de fallos del webhook.","security":[{"token_pfk":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"El id del webhook (lo da tu pantalla de Integraciones)."}],"responses":{"200":{"description":"El ping salió (haya contestado bien o mal tu URL): `result.ok` dice si tu servidor respondió 2xx.","content":{"application/json":{"schema":{"type":"object","properties":{"delivery":{"type":"object","properties":{"id":{"type":"string","description":"El mismo `id` que viaja firmado en el cuerpo entregado."},"event":{"type":"string","const":"ping"},"status":{"type":"string","enum":["delivered","dead"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","event","status","createdAt"]},"result":{"type":"object","properties":{"ok":{"type":"boolean","description":"true solo con 2xx (un 3xx es fallo)."},"httpStatus":{"type":["integer","null"],"description":"Status de tu servidor, o null si no hubo respuesta (timeout, DNS, IP vetada)."},"durationMs":{"type":"integer","description":"Ida y vuelta medida alrededor del POST."},"reason":{"type":"string","description":"Por qué no salió, cuando ni siquiera se conectó."}},"required":["ok","httpStatus","durationMs"]}},"required":["delivery","result"]}}}},"401":{"$ref":"#/components/responses/error_401"},"404":{"$ref":"#/components/responses/error_404"},"409":{"$ref":"#/components/responses/error_409"},"429":{"$ref":"#/components/responses/error_429"}}}}},"webhooks":{"service.created":{"post":{"summary":"Entrega firmada del evento service.created.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.created"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.stage_changed":{"post":{"summary":"Entrega firmada del evento service.stage_changed.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.stage_changed"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.quote_decided":{"post":{"summary":"Entrega firmada del evento service.quote_decided.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.quote_decided"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"payment.recorded":{"post":{"summary":"Entrega firmada del evento payment.recorded.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"payment.recorded"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"payment.reversed":{"post":{"summary":"Entrega firmada del evento payment.reversed.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"payment.reversed"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"invoice.stamped":{"post":{"summary":"Entrega firmada del evento invoice.stamped.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"invoice.stamped"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"invoice.cancelled":{"post":{"summary":"Entrega firmada del evento invoice.cancelled.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"invoice.cancelled"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"appointment.created":{"post":{"summary":"Entrega firmada del evento appointment.created.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"appointment.created"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"appointment.status_changed":{"post":{"summary":"Entrega firmada del evento appointment.status_changed.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"appointment.status_changed"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"remission.issued":{"post":{"summary":"Entrega firmada del evento remission.issued.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"remission.issued"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"customer.created":{"post":{"summary":"Se registra un cliente.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"customer.created"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"customer.updated":{"post":{"summary":"Se actualiza un cliente.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"customer.updated"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"customer.deleted":{"post":{"summary":"Se da de baja un cliente.","description":"Este evento NO entrega la fila: entrega su lápida. Úsalo para quitar ese `id` de tu copia. Si restauran el registro antes de que salga la entrega, el aviso no se manda.\n\nCada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"customer.deleted"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"La LÁPIDA de la baja: `{ id, deleted: true }`, y nada más. No lleva campos de la fila —ni los que tengas marcados— porque la fila ya no se sirve por esta API; el `id` es el mismo con el que te llegó al crearse, y el cuándo es el `createdAt` de este cuerpo."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"vehicle.created":{"post":{"summary":"Se registra un vehículo.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"vehicle.created"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"vehicle.updated":{"post":{"summary":"Se actualiza un vehículo.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"vehicle.updated"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"vehicle.deleted":{"post":{"summary":"Se da de baja un vehículo.","description":"Este evento NO entrega la fila: entrega su lápida. Úsalo para quitar ese `id` de tu copia. Si restauran el registro antes de que salga la entrega, el aviso no se manda.\n\nCada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"vehicle.deleted"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"La LÁPIDA de la baja: `{ id, deleted: true }`, y nada más. No lleva campos de la fila —ni los que tengas marcados— porque la fila ya no se sirve por esta API; el `id` es el mismo con el que te llegó al crearse, y el cuándo es el `createdAt` de este cuerpo."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.updated":{"post":{"summary":"Se actualiza una orden.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.updated"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.deleted":{"post":{"summary":"Se da de baja una orden.","description":"Este evento NO entrega la fila: entrega su lápida. Úsalo para quitar ese `id` de tu copia. Si restauran el registro antes de que salga la entrega, el aviso no se manda.\n\nCada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.deleted"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"La LÁPIDA de la baja: `{ id, deleted: true }`, y nada más. No lleva campos de la fila —ni los que tengas marcados— porque la fila ya no se sirve por esta API; el `id` es el mismo con el que te llegó al crearse, y el cuándo es el `createdAt` de este cuerpo."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.restored":{"post":{"summary":"Se restaura una orden.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.restored"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.delivered":{"post":{"summary":"Se entrega el vehículo.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.delivered"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.hold_changed":{"post":{"summary":"Una orden se detiene o se reanuda.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.hold_changed"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.quote_updated":{"post":{"summary":"Cambia el presupuesto.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.quote_updated"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.quote_expired":{"post":{"summary":"Vence un presupuesto.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.quote_expired"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.technician_assigned":{"post":{"summary":"Cambia el técnico de una orden.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.technician_assigned"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.rework_opened":{"post":{"summary":"Se abre un retrabajo.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.rework_opened"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"service.aging_alert":{"post":{"summary":"Una orden se estanca.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"service.aging_alert"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"cash_session.opened":{"post":{"summary":"Se abre una sesión de caja.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"cash_session.opened"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"cash_session.closed":{"post":{"summary":"Se cierra una sesión de caja.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"cash_session.closed"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"cash_movement.recorded":{"post":{"summary":"Se mueve efectivo en caja.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"cash_movement.recorded"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"opening_receivable.collected":{"post":{"summary":"Se cobra un saldo inicial.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"opening_receivable.collected"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"expense.created":{"post":{"summary":"Se registra un gasto.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"expense.created"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"expense.cancelled":{"post":{"summary":"Se cancela un gasto.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"expense.cancelled"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"expense_payment.recorded":{"post":{"summary":"Se paga un gasto.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"expense_payment.recorded"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"expense_payment.reversed":{"post":{"summary":"Se reversa el pago de un gasto.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"expense_payment.reversed"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"inventory_receipt.created":{"post":{"summary":"Entra mercancía al almacén.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"inventory_receipt.created"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"inventory_receipt.cancelled":{"post":{"summary":"Se cancela una recepción.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"inventory_receipt.cancelled"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"purchase_order.created":{"post":{"summary":"Se crea una orden de compra.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"purchase_order.created"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"purchase_order.cancelled":{"post":{"summary":"Se cancela una orden de compra.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"purchase_order.cancelled"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"supplier_return.created":{"post":{"summary":"Se devuelve mercancía al proveedor.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"supplier_return.created"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"supplier_return.cancelled":{"post":{"summary":"Se cancela una devolución al proveedor.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"supplier_return.cancelled"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"inventory_adjustment.recorded":{"post":{"summary":"Se ajusta el inventario.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"inventory_adjustment.recorded"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"inventory_count.applied":{"post":{"summary":"Se aplica un conteo físico.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"inventory_count.applied"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"inventory.stock_low":{"post":{"summary":"Una existencia queda bajo su mínimo.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"inventory.stock_low"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"remission.cancelled":{"post":{"summary":"Se cancela una remisión.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"remission.cancelled"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"employee.clocked_in":{"post":{"summary":"Alguien registra su entrada.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"employee.clocked_in"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"employee.clocked_out":{"post":{"summary":"Alguien registra su salida.","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"employee.clocked_out"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}},"ping":{"post":{"summary":"Entrega de prueba del botón «Mandar prueba».","description":"Cada entrega va firmada con el header `Pitflo-Firma: t=<epoch>,v1=<hex hmac_sha256(secret, \"<t>.<cuerpo>\")>` (el secreto `pfw_` se enseña una vez al crear el webhook).\nVerifícala así: rechaza si `|ahora − t| > 5 min`, compara el HMAC en tiempo constante y deduplica por `id` (estable entre los 6 intentos).\nEntrega: POST a tu URL `https` puerto 443, timeout 10 s; 2xx = entregada (un 3xx cuenta como fallo). Reintentos 1 m → 5 m → 30 m → 2 h → 8 h; al 6.º fallo la entrega muere; 10 muertas seguidas apagan el webhook con correo al gestor. Retención de entregas: 30 días (solo metadatos).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Estable entre reintentos: tu llave de idempotencia."},"event":{"type":"string","const":"ping"},"createdAt":{"type":"string","format":"date-time"},"version":{"type":"string","const":"v1"},"data":{"type":"object","description":"El recurso proyectado contra los campos marcados VIGENTES al entregar."}},"required":["id","event","createdAt","version","data"]}}}},"responses":{"200":{"description":"Cualquier 2xx cuenta como entregada (un 3xx no)."}}}}},"components":{"securitySchemes":{"token_pfk":{"type":"http","scheme":"bearer","bearerFormat":"pfk_…","description":"Token de integración (se emite en Integraciones y se enseña UNA vez). De SERVIDOR: no lo publiques en un navegador ni en una app tuya — la única excepción es el playground del portal de Pitflo, donde TÚ pegas tu propio token y vive solo en la pestaña. Viaja SOLO por header."}},"schemas":{"error":{"type":"object","description":"Formato canónico de error. Códigos de /v1: API_KEY_REQUIRED, API_KEY_INVALID, API_KEY_REVOKED, API_KEY_EXPIRED (401) · RESOURCE_NOT_ENABLED (403: el recurso no está habilitado en TU integración) · INVALID_CURSOR (400) · NOT_FOUND (404) · 429 con Retry-After.","properties":{"code":{"type":"string","description":"El código estable del error, en MAYÚSCULAS con guion bajo (p. ej. `API_KEY_INVALID`). Es lo único a lo que debes programar: el texto puede cambiar, el código no."},"message":{"type":"string","description":"La explicación legible del error, para tus registros y tu soporte. NO la parsees: cambia de redacción y de idioma."},"details":{"type":"object","description":"Detalle opcional del error, como objeto: presente solo cuando aporta algo (p. ej. el recurso no habilitado). Puede faltar."}},"required":["code","message"],"example":{"code":"RESOURCE_NOT_ENABLED","message":"El recurso `remissions` no está habilitado en tu integración.","details":{"resource":"remissions"}}},"workshops":{"type":"object","description":"El padrón de SUCURSALES de tu empresa: el diccionario con el que resuelves a nombre cualquier `workshopId` que devuelva esta API. Incluye la identidad fiscal con la que cada sucursal factura y sus series de folios.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"name":{"type":"string","description":"Nombre comercial de la sucursal, tal como lo ve el mostrador."},"slug":{"type":"string","description":"Identificador legible y estable de la sucursal dentro de la empresa (minúsculas y guiones)."},"code":{"type":"string","description":"Clave corta de la sucursal para reportes y etiquetas."},"brand":{"type":"string","description":"Marca o enseña bajo la que opera la sucursal; nula si opera con la de la empresa."},"status":{"type":"string","description":"Estado de la sucursal: `active` o `inactive`. Las sucursales dadas de baja no salen por esta API."},"timezone":{"type":"string","description":"Zona horaria IANA de la sucursal (p. ej. `America/Monterrey`). Toda fecha de esta API viaja en UTC: esta es la zona con la que se lee."},"currency":{"type":"string","description":"Moneda de la sucursal en ISO 4217 (`MXN`). Todo importe en centavos de esta moneda."},"legalName":{"type":"string","description":"Razón social con la que la sucursal emite comprobantes."},"taxId":{"type":"string","description":"RFC emisor de la sucursal."},"taxRegime":{"type":"string","description":"Régimen fiscal del SAT del emisor (clave de tres dígitos)."},"addressLine":{"type":"string","description":"Calle y número del domicilio de la sucursal."},"addressNeighborhood":{"type":"string","description":"Colonia."},"addressCity":{"type":"string","description":"Municipio o ciudad."},"addressState":{"type":"string","description":"Estado de la República."},"addressPostalCode":{"type":"string","description":"Código postal; es el que viaja como lugar de expedición en el CFDI."},"logoUrl":{"type":"string","description":"LEGADO. Columna heredada del stack anterior: una URL libre que ninguna pantalla puede fijar hoy. **No es «el logo de la sucursal»**: desde 2026-09-02 el logo se configura como archivo a nivel empresa (se hereda a todas sus sucursales) o propio de la sucursal, y ninguno de esos dos viaja por aquí. Una sucursal con logo puede devolver `null` en este campo y aun así imprimirlo en sus documentos; solo se conserva para no perder el membrete de los talleres migrados."},"workdayMinutes":{"type":"integer","description":"Minutos productivos de una jornada, con los que se calcula la ocupación del taller."},"series":{"type":"string","description":"Serie con la que la sucursal folia sus órdenes de servicio."},"remissionSeries":{"type":"string","description":"Serie con la que la sucursal folia sus remisiones."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","name":"Pitflo Cumbres","slug":"cumbres","code":"CUM","brand":"Pitflo","status":"active","timezone":"America/Monterrey","currency":"MXN","legalName":"Servicio Automotriz Cumbres SA de CV","taxId":"SAC120315M42","taxRegime":"601","addressLine":"Av. Paseo de los Leones 3120","addressNeighborhood":"Cumbres 3er Sector","addressCity":"Monterrey","addressState":"Nuevo León","addressPostalCode":"64610","logoUrl":"https://cdn.pitflo.com/talleres/cumbres/logo.png","workdayMinutes":480,"series":"A","remissionSeries":"R","createdAt":"2026-01-08T15:04:22.000Z","updatedAt":"2026-08-14T16:32:11.000Z"}},"services":{"type":"object","description":"LA ORDEN DE SERVICIO: el documento central del taller, desde que el coche entra hasta que se factura. Trae la posición en el tablero, el estado del presupuesto, la promesa de entrega y todos sus importes en centavos. `stage` es el ÚNICO campo derivado del contrato: la llave canónica de la etapa, comparable entre tableros que nadie nombró igual.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"boardId":{"type":"string","description":"Tablero al que pertenece la orden."},"columnId":{"type":"string","description":"Columna del tablero donde está la orden ahora mismo."},"stage":{"type":"string","description":"Llave CANÓNICA de la etapa de la columna (`recepcion`, `diagnostico`, `autorizacion`, `reparacion`, `calidad`, `entrega`…). Úsala en vez de `columnId`: no depende de cómo llamó el taller a su columna."},"columnEnteredAt":{"type":"string","format":"date-time","description":"Cuándo entró la orden a la columna actual. La historia completa está en `service_stage_transitions`."},"folio":{"type":"integer","description":"Consecutivo del documento dentro de su sucursal: es como el taller lo cita de viva voz."},"series":{"type":"string","description":"Serie del folio (por omisión \"A\"). El par serie + folio es el nombre humano del documento."},"customerId":{"type":"string","description":"Cliente al que pertenece la fila. Resuélvelo contra el recurso `customers`."},"vehicleId":{"type":"string","description":"Vehículo atendido. Resuélvelo contra el recurso `vehicles`."},"mileage":{"type":"integer","description":"Kilometraje del vehículo al recibirlo."},"fuelLevel":{"type":"integer","description":"Nivel de combustible al recibir, en octavos de tanque (0–8)."},"customerMessage":{"type":"string","description":"Mensaje del taller al cliente que se imprime en el presupuesto y en la remisión."},"tags":{"type":"string","description":"Etiquetas libres de la orden."},"quoteStatus":{"type":"string","description":"Estado del presupuesto: `borrador`, `enviado`, `aprobado`, `rechazado` o `parcial` (nulo si aún no hay presupuesto)."},"quoteStatusAt":{"type":"string","format":"date-time","description":"Cuándo el presupuesto llegó a ese estado."},"validUntil":{"type":"string","format":"date-time","description":"Vigencia del presupuesto. Pasada esta fecha el presupuesto está VENCIDO."},"paymentCondition":{"type":"string","description":"Condición de pago pactada: `contado` o `credito`."},"creditTermDays":{"type":"integer","description":"Días de crédito pactados en esta orden."},"creditDueDate":{"type":"string","format":"date-time","description":"Fecha de vencimiento del crédito de la orden."},"subtotalCents":{"type":"integer","description":"Subtotal de TODAS las líneas, aprobadas o no, en centavos."},"approvedSubtotalCents":{"type":"integer","description":"Subtotal de las líneas APROBADAS por el cliente, en centavos."},"approvedTotalCents":{"type":"integer","description":"Total aprobado con impuestos y descuentos aplicados, en centavos. Es la cifra que el taller espera cobrar."},"taxCents":{"type":"integer","description":"Impuesto trasladado de la orden, en centavos."},"taxRate":{"type":"number","description":"Tasa de impuesto aplicada (0.16 = 16 %)."},"taxIncluded":{"type":"boolean","description":"true = los precios de las líneas YA traen el impuesto dentro."},"discountCents":{"type":"integer","description":"Descuento total de la orden, en centavos."},"discountRate":{"type":"number","description":"Descuento aplicado como tasa (0.10 = 10 %)."},"totalCents":{"type":"integer","description":"Total de la orden con impuestos y descuentos, en centavos."},"invoicedAt":{"type":"string","format":"date-time","description":"Cuándo se facturó la orden; nulo mientras no tenga CFDI. El comprobante completo sale por `fiscal_documents`."},"remissionId":{"type":"string","description":"Remisión con la que se entregó el trabajo, si la hubo."},"globalDocumentId":{"type":"string","description":"Factura global que amparó esta orden, cuando el cliente no pidió comprobante propio."},"sourceAppointmentId":{"type":"string","description":"Cita de la que nació la orden. Resuélvela contra `appointments`."},"originServiceId":{"type":"string","description":"Orden de la que ésta se derivó (una cotización que se partió, por ejemplo)."},"promisedAt":{"type":"string","format":"date-time","description":"Fecha y hora PROMETIDA de entrega al cliente."},"holdReason":{"type":"string","description":"Motivo por el que la orden está detenida: `refaccion`, `autorizacion`, `pago`, `cliente` u `otro`. Nulo si avanza."},"heldAt":{"type":"string","format":"date-time","description":"Desde cuándo está detenida: el reloj de la detención corre desde aquí."},"keysStatus":{"type":"string","description":"Dónde están las llaves del coche: `con_cliente`, `en_tablero` o `entregadas`."},"reworkOfServiceId":{"type":"string","description":"Orden original de la que ésta es RETRABAJO en garantía."},"createdByUserId":{"type":"string","description":"Usuario que creó la fila."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."},"responsibleName":{"type":"string","description":"Quien trajo el vehículo, si no fue el titular. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"responsiblePhone":{"type":"string","description":"Teléfono de quien trajo el vehículo. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"responsibleEmail":{"type":"string","description":"Correo de quien trajo el vehículo. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true}},"example":{"id":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","boardId":"81c4f0a7-3e29-4d65-b104-9a72c6e8d503","columnId":"d2e6b415-8c73-4092-a5f1-0b3c7d94e618","stage":"reparacion","columnEnteredAt":"2026-08-14T15:10:00.000Z","folio":2841,"series":"A","customerId":"9d4b1e77-2c85-4a13-8f60-5b2e7a9c0d41","vehicleId":"c07e5a26-9134-42db-b8f5-6e1a3c7d9024","mileage":87450,"fuelLevel":3,"customerMessage":"Se recomienda cambiar balatas traseras en la próxima visita.","tags":["garantia_extendida"],"quoteStatus":"aprobado","quoteStatusAt":"2026-08-14T14:05:31.000Z","validUntil":"2026-08-21T05:59:59.000Z","paymentCondition":"contado","creditTermDays":null,"creditDueDate":null,"subtotalCents":894000,"approvedSubtotalCents":742000,"approvedTotalCents":860720,"taxCents":118720,"taxRate":0.16,"taxIncluded":false,"discountCents":0,"discountRate":null,"totalCents":1037040,"invoicedAt":null,"remissionId":null,"globalDocumentId":null,"sourceAppointmentId":"6f1d8b03-4a97-42e5-9c86-2d5b7e0a3f14","originServiceId":null,"promisedAt":"2026-08-15T22:00:00.000Z","holdReason":null,"heldAt":null,"keysStatus":"en_tablero","reworkOfServiceId":null,"createdByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-14T13:47:09.000Z","updatedAt":"2026-08-14T16:32:11.000Z","responsibleName":"María Fernanda Treviño","responsiblePhone":"+528112345678","responsibleEmail":"mf.trevino@example.mx"}},"service_items":{"type":"object","description":"Las LÍNEAS de la orden: refacciones, mano de obra y servicios de catálogo, cada una con su precio, su costo, su decisión del cliente y su garantía. Una línea puede colgar de otra (`parentItemId`) cuando es parte de un paquete.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"serviceId":{"type":"string","description":"Orden de servicio a la que pertenece la fila. Resuélvela contra el recurso `services`."},"parentItemId":{"type":"string","description":"Línea padre cuando ésta es parte de un paquete o de un servicio compuesto."},"kind":{"type":"string","description":"Tipo de línea: `refaccion`, `mano_obra`, `servicio` o `insumo`."},"description":{"type":"string","description":"Descripción de la línea tal como la lee el cliente en el presupuesto."},"quantity":{"type":"number","description":"Cantidad de la línea (piezas, litros u horas según el tipo)."},"unitPriceCents":{"type":"integer","description":"Precio unitario de venta, en centavos."},"unitCostCents":{"type":"integer","description":"Costo unitario del taller, en centavos. Es SU dato, por eso viaja."},"priceBeforeMarginCents":{"type":"integer","description":"Precio que la línea tenía antes de que el motor de margen lo ajustara."},"discountPct":{"type":"number","description":"Descuento de la línea como porcentaje."},"discountCents":{"type":"integer","description":"Descuento de la línea en centavos."},"totalCents":{"type":"integer","description":"Total de la línea ya con su descuento, en centavos."},"satCode":{"type":"string","description":"Clave de producto/servicio del SAT con la que se timbra la línea."},"priority":{"type":"string","description":"Urgencia técnica sugerida: `critica`, `recomendada` u `opcional`."},"approvalStatus":{"type":"string","description":"Decisión del cliente sobre la línea: `pendiente`, `aprobada`, `rechazada` o `pospuesta`."},"approvalSource":{"type":"string","description":"Por dónde llegó la decisión: `mostrador`, `liga`, `whatsapp` o `telefono`."},"approvalDecidedAt":{"type":"string","format":"date-time","description":"Cuándo se decidió la línea."},"approvalDecidedByUserId":{"type":"string","description":"Usuario del taller que registró la decisión (nulo si la tomó el cliente por la liga)."},"approvalNote":{"type":"string","description":"Nota que acompañó la decisión."},"productId":{"type":"string","description":"Producto del catálogo al que se refiere la fila. Resuélvelo contra el recurso `products`."},"serviceTemplateId":{"type":"string","description":"Plantilla de servicio de la que nació la línea. Resuélvela contra `service_templates`."},"pricingType":{"type":"string","description":"Cómo se precia la línea: `fijo` o `por_hora`."},"laborHours":{"type":"number","description":"Horas de mano de obra de la línea."},"laborRateCents":{"type":"integer","description":"Tarifa de mano de obra por hora aplicada, en centavos."},"position":{"type":"number","description":"Orden de la línea dentro de la orden, para reimprimirla igual."},"notes":{"type":"string","description":"Nota libre que capturó el taller en el documento."},"isSublet":{"type":"boolean","description":"true = el trabajo se TERCERIZÓ con un proveedor externo."},"subletSupplierId":{"type":"string","description":"Proveedor que hizo el trabajo tercerizado."},"subletCostCents":{"type":"integer","description":"Lo que el tercero le cobró al taller, en centavos."},"subletExpenseId":{"type":"string","description":"Gasto que ese trabajo tercerizado generó. Resuélvelo contra `expenses`."},"warrantyMonths":{"type":"integer","description":"Meses de garantía pactados en la línea."},"warrantyKm":{"type":"integer","description":"Kilómetros de garantía pactados en la línea."},"approvalReason":{"type":"string","description":"Motivo que dio el cliente al rechazar o posponer."},"approvalAmountCents":{"type":"integer","description":"Importe SELLADO al momento de decidir, en centavos: lo que se ganó o se perdió ese día, aunque el precio cambie después."},"deferredUntil":{"type":"string","format":"date-time","description":"Hasta cuándo pospuso el cliente esta línea: es la fecha del seguimiento."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"e4a71c58-2039-4bd6-8f47-1c9e5b30a726","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","serviceId":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29","parentItemId":null,"kind":"refaccion","description":"Balatas delanteras cerámicas (juego)","quantity":1,"unitPriceCents":189000,"unitCostCents":112000,"priceBeforeMarginCents":175000,"discountPct":null,"discountCents":0,"totalCents":189000,"satCode":"25172504","priority":"critica","approvalStatus":"aprobada","approvalSource":"liga","approvalDecidedAt":"2026-08-14T14:05:31.000Z","approvalDecidedByUserId":null,"approvalNote":"Autorizado por WhatsApp.","productId":"b6d0937f-51ac-4e28-9370-4f8a1c62d5be","serviceTemplateId":null,"pricingType":"fijo","laborHours":null,"laborRateCents":null,"position":1,"notes":"Se entrega la refacción usada al cliente.","isSublet":false,"subletSupplierId":null,"subletCostCents":null,"subletExpenseId":null,"warrantyMonths":6,"warrantyKm":20000,"approvalReason":null,"approvalAmountCents":189000,"deferredUntil":null,"createdAt":"2026-08-14T13:52:40.000Z","updatedAt":"2026-08-14T14:05:31.000Z"}},"service_stage_transitions":{"type":"object","description":"La BITÁCORA DE ETAPAS del tablero: un renglón por cada vez que una orden entró a una columna. Libro append-only con una sola mutación posterior — `leftAt`, que se sella en el MISMO instante en que nace la fila siguiente de esa orden. Por eso paginar por `createdAt` no pierde nada: el `leftAt` de una fila es el `enteredAt` de la siguiente, y la fila sin `leftAt` es la etapa donde la orden está ahora.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"serviceId":{"type":"string","description":"Orden de servicio a la que pertenece la fila. Resuélvela contra el recurso `services`."},"fromColumnId":{"type":"string","description":"Columna de la que salió la orden; nula en la primera etapa."},"toColumnId":{"type":"string","description":"Columna a la que entró la orden. Su llave canónica es la que `services.stage` publica."},"actorUserId":{"type":"string","description":"Usuario que movió la tarjeta."},"reason":{"type":"string","description":"Cómo se movió: `manual`, `automatico` o `regla`."},"note":{"type":"string","description":"Nota del actor al mover («el cliente pidió esperar a la quincena»)."},"enteredAt":{"type":"string","format":"date-time","description":"Cuándo entró la orden a esta etapa."},"leftAt":{"type":"string","format":"date-time","description":"Cuándo salió de esta etapa; nulo mientras siga en ella. `leftAt − enteredAt` es el tiempo en etapa."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."}},"example":{"id":"7c3e0d92-4b18-4a67-b503-e91d6c8f2740","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","serviceId":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29","fromColumnId":"a05f7c31-9d24-4e80-8b16-3f7c2e5a9014","toColumnId":"d2e6b415-8c73-4092-a5f1-0b3c7d94e618","actorUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","reason":"manual","note":"Cliente autorizó por la liga; pasa a reparación.","enteredAt":"2026-08-14T15:10:00.000Z","leftAt":null,"createdAt":"2026-08-14T15:10:00.000Z"}},"customers":{"type":"object","description":"El EXPEDIENTE DE CLIENTES de la empresa entera: se comparte entre sucursales a propósito, así que `workshop={id}` NO lo filtra. Los datos FISCALES del cliente no viven aquí y no son recurso de /v1: el receptor congelado de cada comprobante sale en `fiscal_documents`.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"homeWorkshopId":{"type":"string","description":"Sucursal donde el cliente se dio de alta. No limita su visibilidad: el expediente es de la empresa."},"isFleet":{"type":"boolean","description":"true = cliente de FLOTA (empresa con varias unidades); false = particular."},"paymentCondition":{"type":"string","description":"Condición de pago por defecto del cliente: `contado` o `credito`."},"creditDays":{"type":"integer","description":"Días de crédito autorizados al cliente."},"tags":{"type":"string","description":"Etiquetas libres del cliente."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."},"name":{"type":"string","description":"Nombre del cliente o razón social comercial con la que el taller lo llama. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"email":{"type":"string","description":"Correo del cliente. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"phone":{"type":"string","description":"Teléfono fijo del cliente. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"mobile":{"type":"string","description":"Móvil del cliente: es por donde viajan los avisos y la liga del presupuesto. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"addressLine":{"type":"string","description":"Calle y número. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"addressNeighborhood":{"type":"string","description":"Colonia. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"addressCity":{"type":"string","description":"Municipio o ciudad. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"addressState":{"type":"string","description":"Estado. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"addressPostalCode":{"type":"string","description":"Código postal. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"notes":{"type":"string","description":"Nota libre del taller SOBRE LA PERSONA: por eso cuenta como dato personal. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true}},"example":{"id":"9d4b1e77-2c85-4a13-8f60-5b2e7a9c0d41","homeWorkshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","isFleet":false,"paymentCondition":"contado","creditDays":null,"tags":["frecuente"],"createdAt":"2025-11-03T18:20:44.000Z","updatedAt":"2026-08-14T13:47:09.000Z","name":"José Ramón Villarreal","email":"jr.villarreal@example.mx","phone":"+528183451200","mobile":"+528118765432","addressLine":"Río Danubio 218","addressNeighborhood":"Del Valle","addressCity":"San Pedro Garza García","addressState":"Nuevo León","addressPostalCode":"66220","notes":"Prefiere que le llamen antes de las 10 a. m."}},"vehicles":{"type":"object","description":"Los VEHÍCULOS del expediente, colgados de su cliente. Como `customers`, son de la empresa entera y `workshop={id}` no los filtra. La placa, el VIN y el número de flota identifican al dueño tan bien como su nombre: van marcados como dato personal.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"customerId":{"type":"string","description":"Cliente al que pertenece la fila. Resuélvelo contra el recurso `customers`."},"make":{"type":"string","description":"Marca del vehículo, en texto, tal como quedó capturada."},"model":{"type":"string","description":"Modelo del vehículo, en texto."},"makeId":{"type":"string","description":"Marca en el catálogo vehicular global de Pitflo (id de plataforma, sin dato del cliente)."},"modelId":{"type":"string","description":"Modelo en el catálogo vehicular global."},"engineSpecId":{"type":"string","description":"Versión de motor en el catálogo vehicular global."},"year":{"type":"integer","description":"Año del vehículo."},"color":{"type":"string","description":"Color del vehículo, en texto: la etiqueta del catálogo cuando `colorKey` viene, o lo que capturó el taller cuando no."},"colorKey":{"type":"string","description":"Clave del catálogo de colores: `blanco`, `negro`, `gris`, `plata`, `gris_oxford`, `rojo`, `azul`, `azul_marino`, `verde`, `amarillo`, `naranja`, `cafe`, `beige`, `arena`, `dorado`, `vino`, `morado` o `rosa`. `null` = color libre («Otro»)."},"engineCode":{"type":"string","description":"Código de motor capturado por el taller."},"transmission":{"type":"string","description":"Transmisión: `manual`, `automatica` o `cvt`."},"fuelType":{"type":"string","description":"Combustible: `gasolina`, `diesel`, `hibrido` o `electrico`."},"mileage":{"type":"integer","description":"Último kilometraje conocido del vehículo."},"notes":{"type":"string","description":"Nota libre sobre el vehículo."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."},"plate":{"type":"string","description":"Placa del vehículo. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"vin":{"type":"string","description":"Número de serie (VIN). Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"fleetNumber":{"type":"string","description":"Número económico dentro de la flota del cliente. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"responsibleName":{"type":"string","description":"Responsable fijo de la unidad en una flota. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"responsiblePhone":{"type":"string","description":"Teléfono del responsable de la unidad. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"responsibleEmail":{"type":"string","description":"Correo del responsable de la unidad. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true}},"example":{"id":"c07e5a26-9134-42db-b8f5-6e1a3c7d9024","customerId":"9d4b1e77-2c85-4a13-8f60-5b2e7a9c0d41","make":"Nissan","model":"Versa","makeId":"1f7a3c50-8b62-4d19-9e07-2c4b8f6a1d35","modelId":"4b9e2d17-6c30-4f85-a231-9e60c7d43a85","engineSpecId":"a8c5f312-7e94-4b06-8d2f-51a7c3e9b640","year":2021,"color":"Gris Oxford","colorKey":"gris_oxford","engineCode":"HR16DE","transmission":"automatica","fuelType":"gasolina","mileage":87450,"notes":"Rines de aluminio con birlo de seguridad; llave en la guantera.","createdAt":"2025-11-03T18:24:02.000Z","updatedAt":"2026-08-14T13:47:09.000Z","plate":"SXH-482-C","vin":"3N1CN8AD5ML830472","fleetNumber":null,"responsibleName":null,"responsiblePhone":null,"responsibleEmail":null}},"appointments":{"type":"object","description":"Las CITAS de la agenda: las del mostrador y las que entran por la liga pública o por un sistema externo. Una cita se convierte en orden (`convertedServiceId`) y ahí termina su ciclo. Cuando el cliente todavía no existe en el expediente, sus datos viajan en los campos `prospect*`.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"customerId":{"type":"string","description":"Cliente al que pertenece la fila. Resuélvelo contra el recurso `customers`."},"vehicleId":{"type":"string","description":"Vehículo de la cita, si ya está en el expediente."},"scheduledAt":{"type":"string","format":"date-time","description":"Fecha y hora de la cita, en UTC."},"durationMin":{"type":"integer","description":"Duración reservada en la agenda, en minutos."},"reason":{"type":"string","description":"Motivo de la visita, en palabras del cliente."},"notes":{"type":"string","description":"Nota del mostrador sobre la cita."},"status":{"type":"string","description":"Estado de la cita: `agendada`, `confirmada`, `llego`, `cancelada`, `no_asistio` o `convertida`."},"confirmedAt":{"type":"string","format":"date-time","description":"Cuándo la confirmó el TALLER."},"confirmedByClientAt":{"type":"string","format":"date-time","description":"Cuándo la confirmó EL CLIENTE por la liga pública."},"arrivedAt":{"type":"string","format":"date-time","description":"Cuándo llegó el cliente."},"cancelledAt":{"type":"string","format":"date-time","description":"Fecha de cancelación, en UTC. Nula mientras el documento esté vivo: un documento cancelado NO desaparece de esta API, se marca."},"cancelReason":{"type":"string","description":"Motivo de la cancelación."},"noShowAt":{"type":"string","format":"date-time","description":"Cuándo se marcó como no asistió."},"convertedServiceId":{"type":"string","description":"Orden de servicio que nació de esta cita."},"convertedAt":{"type":"string","format":"date-time","description":"Cuándo se convirtió en orden."},"externalId":{"type":"string","description":"Identidad de la cita en el sistema externo que la creó: tu llave para no duplicarla."},"source":{"type":"string","description":"De dónde vino la cita (`liga`, `whatsapp`, `api`…). Nulo = mostrador."},"createdByUserId":{"type":"string","description":"Usuario que creó la fila."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."},"prospectName":{"type":"string","description":"Nombre de quien agendó cuando todavía no es cliente del expediente. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"prospectPhone":{"type":"string","description":"Teléfono del prospecto. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"prospectEmail":{"type":"string","description":"Correo del prospecto. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"prospectVehicle":{"type":"string","description":"Vehículo que el prospecto describió en texto. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true}},"example":{"id":"6f1d8b03-4a97-42e5-9c86-2d5b7e0a3f14","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","customerId":"9d4b1e77-2c85-4a13-8f60-5b2e7a9c0d41","vehicleId":"c07e5a26-9134-42db-b8f5-6e1a3c7d9024","scheduledAt":"2026-08-14T14:00:00.000Z","durationMin":60,"reason":"Rechinido al frenar","notes":"Llega temprano, deja el coche todo el día.","status":"convertida","confirmedAt":"2026-08-13T17:02:10.000Z","confirmedByClientAt":"2026-08-13T16:55:47.000Z","arrivedAt":"2026-08-14T13:44:52.000Z","cancelledAt":null,"cancelReason":null,"noShowAt":null,"convertedServiceId":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29","convertedAt":"2026-08-14T13:47:09.000Z","externalId":null,"source":"liga","createdByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-12T19:31:05.000Z","updatedAt":"2026-08-14T13:47:09.000Z","prospectName":null,"prospectPhone":null,"prospectEmail":null,"prospectVehicle":null}},"products":{"type":"object","description":"El CATÁLOGO DE PRODUCTOS: refacciones e insumos con su precio de venta, su costo de referencia y su mínimo. Todos los importes van SIN IVA. La fila con `workshopId` nulo es el producto corporativo, válido en toda sucursal — si filtras por sucursal, esas filas siguen viniendo, porque perderlas sería mentirte.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal dueña del producto; NULO = producto corporativo de toda la empresa."},"categoryId":{"type":"string","description":"Categoría del producto."},"subcategoryId":{"type":"string","description":"Subcategoría del producto."},"supplierId":{"type":"string","description":"Proveedor preferente del producto."},"name":{"type":"string","description":"Nombre del producto."},"sku":{"type":"string","description":"Clave interna del producto en el taller."},"satCode":{"type":"string","description":"Clave de producto/servicio del SAT con la que se timbra. Vacía significa que el producto la HEREDA de su subcategoría o de su categoría."},"description":{"type":"string","description":"Descripción del producto."},"baseUnit":{"type":"string","description":"Unidad BASE en la que se lleva la existencia (`pieza`, `litro`, `metro`…). Todas las cantidades de esta API están en milésimas de esta unidad."},"tracked":{"type":"boolean","description":"true = el producto lleva existencia y mueve kardex; false = se vende sin controlar inventario."},"salePriceCents":{"type":"integer","description":"Precio de venta por unidad base, en centavos y SIN IVA. Si el taller captura sus precios con el impuesto dentro, la conversión ocurre al capturar: aquí siempre llega neto."},"referenceCostCents":{"type":"integer","description":"Costo de referencia por unidad base, en centavos y SIN IVA. El costo REAL de cada capa está en `inventory_movements`."},"minStockMilli":{"type":"integer","description":"Mínimo de existencia en milésimas: por debajo, el producto entra a la lista de críticos."},"maintainMargin":{"type":"boolean","description":"true = al recibir mercancía más cara, el precio de venta se sube solo para sostener el margen."},"needsReview":{"type":"boolean","description":"true = el producto quedó marcado para que alguien lo revise (precio o costo sospechoso)."},"isActive":{"type":"boolean","description":"false = el producto ya no se vende, pero su historia se conserva."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"b6d0937f-51ac-4e28-9370-4f8a1c62d5be","workshopId":null,"categoryId":"5a7c1e38-4d92-4067-b85f-2e6a0c3d9147","subcategoryId":"c3f8b520-7a41-4e96-8d03-51b7e2c4a068","supplierId":"8e2c6a04-9f71-4b35-8d60-3a15c7e94b28","name":"Balatas delanteras cerámicas Versa 2019-2023","sku":"BAL-NIS-VER-D","satCode":"25172504","description":"Juego de 4 balatas cerámicas con herrajes.","baseUnit":"pieza","tracked":true,"salePriceCents":189000,"referenceCostCents":112000,"minStockMilli":4000,"maintainMargin":true,"needsReview":false,"isActive":true,"createdAt":"2025-06-11T17:03:29.000Z","updatedAt":"2026-08-10T21:14:05.000Z"}},"inventory_stock":{"type":"object","description":"La EXISTENCIA viva por producto y sucursal: cuánto hay y cuánto vale ahora mismo. Es la foto; la película —quién movió qué y a qué costo— está en `inventory_movements`.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"productId":{"type":"string","description":"Producto del catálogo al que se refiere la fila. Resuélvelo contra el recurso `products`."},"onHandMilli":{"type":"integer","description":"Existencia actual en milésimas de la unidad base (1000 = 1 pieza). Puede ser negativa si el taller autorizó una salida en descubierto."},"valueCents":{"type":"integer","description":"Valuación de esa existencia en centavos, con el costo real de las capas PEPS vivas."},"lastCostCents":{"type":"integer","description":"Último costo unitario con el que entró mercancía, en centavos."},"lastMovementAt":{"type":"string","format":"date-time","description":"Cuándo se movió por última vez esta existencia."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"0d6b3f81-92c5-4a07-8e14-7f2c5a90b634","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","productId":"b6d0937f-51ac-4e28-9370-4f8a1c62d5be","onHandMilli":7000,"valueCents":784000,"lastCostCents":112000,"lastMovementAt":"2026-08-14T16:02:55.000Z","createdAt":"2025-06-11T17:03:29.000Z","updatedAt":"2026-08-14T16:02:55.000Z"}},"inventory_movements":{"type":"object","description":"EL KARDEX: un renglón por cada vez que el inventario se movió, con el saldo y el valor que quedaron DESPUÉS del asiento. Libro append-only (pagina por `createdAt`): un asiento no se edita ni se borra — se CONTRA-ASIENTA con otra fila, que te llega como página nueva. `totalCostCents` es lo que el asiento movió en dinero, con el mismo signo que la cantidad; el COGS de una orden es la suma NEGADA de los `totalCostCents` de sus asientos.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"productId":{"type":"string","description":"Producto del catálogo al que se refiere la fila. Resuélvelo contra el recurso `products`."},"kind":{"type":"string","description":"Qué clase de asiento es: `entrada`, `salida`, `ajuste`, `devolucion_proveedor`, `reingreso`, `reversa` o `regularizacion`."},"quantityMilli":{"type":"integer","description":"LO ÚNICO que mueve el saldo: milésimas SIGNADAS (positivo entra, negativo sale). Cero solo en `regularizacion`, que mueve dinero sin mover materia."},"enteredQuantityMilli":{"type":"integer","description":"Lo que el usuario capturó, en la unidad que eligió (p. ej. 1 tambo), también en milésimas."},"purchaseUnitId":{"type":"string","description":"Unidad de compra que el usuario eligió al capturar; nula si capturó en la unidad base."},"conversionFactorMilli":{"type":"integer","description":"Factor CONGELADO de esa unidad a la base, en milésimas (1000 = 1:1). Congelarlo es lo que permite reimprimir el asiento aunque el catálogo cambie."},"unitCostCents":{"type":"integer","description":"Costo por unidad BASE, sin IVA. En una entrada es el de la capa; en una salida es informativo (el costo exacto son sus consumos de capa)."},"enteredUnitCostCents":{"type":"integer","description":"Costo por unidad de COMPRA, tal como venía en la factura del proveedor y sin IVA."},"totalCostCents":{"type":"integer","description":"Dinero que el asiento movió, con el MISMO signo que la cantidad. `valueCents += totalCostCents` no tiene una sola excepción: eso es lo que hace verificable la valuación."},"costEstimated":{"type":"boolean","description":"true = parte del costo de este asiento no salió de una capa real, sino de un estimador. El reporte de margen lo declara y nunca lo esconde."},"forcedNegative":{"type":"boolean","description":"true = fue una salida AUTORIZADA por encima de la existencia, con motivo obligatorio."},"uncoveredMilli":{"type":"integer","description":"Milésimas que salieron SIN capa que las respalde: la deuda contable del asiento."},"uncoveredSettledMilli":{"type":"integer","description":"Cuánto de esa deuda ya saldó una entrada posterior."},"balanceAfterMilli":{"type":"integer","description":"Existencia que quedó DESPUÉS del asiento, en milésimas. Es la cadena que permite decir dónde empezó una deriva, no solo que existe."},"balanceAfterValueCents":{"type":"integer","description":"Valuación que quedó DESPUÉS del asiento, en centavos."},"serviceId":{"type":"string","description":"Orden de servicio a la que pertenece la fila. Resuélvela contra el recurso `services`."},"serviceItemId":{"type":"string","description":"Línea de la orden que consumió la refacción. Resuélvela contra `service_items`."},"issueOrigin":{"type":"string","description":"Por qué salió la mercancía: `orden`, `venta_mostrador`, `consumo_interno` o `merma`."},"returnReason":{"type":"string","description":"Por qué volvió la mercancía: `sobrante`, `equivocada` o `defectuosa`."},"receiptId":{"type":"string","description":"Recepción que originó el asiento. Resuélvela contra `inventory_receipts`."},"countId":{"type":"string","description":"Conteo físico que originó el ajuste. Resuélvelo contra `inventory_counts`."},"countLineId":{"type":"string","description":"Línea del conteo que produjo exactamente este ajuste."},"supplierReturnId":{"type":"string","description":"Devolución a proveedor que sacó estas piezas. Resuélvela contra `supplier_returns`."},"reasonId":{"type":"string","description":"Motivo de catálogo del ajuste. Obligatorio en todo `ajuste` y en toda salida forzada: una venta no se «justifica», un ajuste sí."},"notes":{"type":"string","description":"Nota libre del asiento."},"salePriceBeforeCents":{"type":"integer","description":"Precio de venta que el producto tenía ANTES de que esta compra lo moviera (solo bajo «sostener el margen»)."},"salePriceAfterCents":{"type":"integer","description":"Precio de venta que quedó DESPUÉS de esta compra."},"reversesMovementId":{"type":"string","description":"Asiento que este asiento deshace, entero o en parte."},"reversedMilli":{"type":"integer","description":"Cuánto de ESTE asiento ya se revirtió, en milésimas."},"regularizesMovementId":{"type":"string","description":"Asiento en descubierto que esta `regularizacion` salda."},"cancelledAt":{"type":"string","format":"date-time","description":"Sello de cancelación. NO resta: la resta la hace el contra-asiento y el saldo suma TODAS las filas."},"cancelledByUserId":{"type":"string","description":"Usuario que canceló el documento."},"cancelReason":{"type":"string","description":"Motivo escrito de la cancelación."},"occurredAt":{"type":"string","format":"date-time","description":"Fecha del hecho según el documento, en UTC: admite retrodatar dentro del mes anterior."},"performedByUserId":{"type":"string","description":"Usuario que ejecutó el movimiento."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."}},"example":{"id":"3a9f1c47-6b02-4d85-9e73-0c5a8f2b6194","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","productId":"b6d0937f-51ac-4e28-9370-4f8a1c62d5be","kind":"salida","quantityMilli":-1000,"enteredQuantityMilli":-1000,"purchaseUnitId":null,"conversionFactorMilli":1000,"unitCostCents":112000,"enteredUnitCostCents":null,"totalCostCents":-112000,"costEstimated":false,"forcedNegative":false,"uncoveredMilli":0,"uncoveredSettledMilli":0,"balanceAfterMilli":7000,"balanceAfterValueCents":784000,"serviceId":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29","serviceItemId":"e4a71c58-2039-4bd6-8f47-1c9e5b30a726","issueOrigin":"orden","returnReason":null,"receiptId":null,"countId":null,"countLineId":null,"supplierReturnId":null,"reasonId":null,"notes":null,"salePriceBeforeCents":null,"salePriceAfterCents":null,"reversesMovementId":null,"reversedMilli":0,"regularizesMovementId":null,"cancelledAt":null,"cancelledByUserId":null,"cancelReason":null,"occurredAt":"2026-08-14T16:02:55.000Z","performedByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-14T16:02:55.000Z"}},"inventory_receipts":{"type":"object","description":"Las RECEPCIONES de mercancía: la factura del proveedor entrando al almacén. El costo de las líneas es SIN IVA siempre —es lo que valúa las capas y el COGS—; el IVA acreditable del documento viaja aparte en `taxCents` y, junto con el RFC del proveedor, es el insumo de la DIOT. Lo que cada recepción metió al almacén, pieza por pieza, sale en `inventory_movements` filtrando por `receiptId`.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"supplierId":{"type":"string","description":"Proveedor del documento. Resuélvelo contra el recurso `suppliers`."},"folio":{"type":"integer","description":"Consecutivo del documento dentro de su sucursal: es como el taller lo cita de viva voz."},"series":{"type":"string","description":"Serie del folio (por omisión \"A\"). El par serie + folio es el nombre humano del documento."},"documentRef":{"type":"string","description":"Folio de la factura o remisión del proveedor. Es único por proveedor entre las recepciones vivas: la doble captura de la misma factura es el error más caro del módulo."},"documentDate":{"type":"string","format":"date-time","description":"Fecha del documento del proveedor."},"documentTotalCents":{"type":"integer","description":"Total de la factura tal como lo tecleó el usuario, en centavos. Si no cuadra con las líneas más el IVA, el sistema AVISA, no bloquea."},"taxCents":{"type":"integer","description":"IVA ACREDITABLE del documento, en centavos, fuera del costo de las líneas."},"purchaseOrderId":{"type":"string","description":"Orden de compra contra la que se recibió; nula en la recepción directa, sin pedido previo."},"notes":{"type":"string","description":"Nota libre que capturó el taller en el documento."},"cancelledAt":{"type":"string","format":"date-time","description":"Fecha de cancelación, en UTC. Nula mientras el documento esté vivo: un documento cancelado NO desaparece de esta API, se marca."},"cancelledByUserId":{"type":"string","description":"Usuario que canceló el documento."},"cancelReason":{"type":"string","description":"Motivo escrito de la cancelación."},"receivedAt":{"type":"string","format":"date-time","description":"Cuándo se recibió la mercancía."},"receivedByUserId":{"type":"string","description":"Usuario que recibió la mercancía."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"f19d4c73-0b28-4e61-9a35-6c80d2f5a417","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","supplierId":"8e2c6a04-9f71-4b35-8d60-3a15c7e94b28","folio":318,"series":"A","documentRef":"F-99214","documentDate":"2026-08-11T00:00:00.000Z","documentTotalCents":1299200,"taxCents":179200,"purchaseOrderId":"7d4a2f60-3c95-4e18-b072-8a6f1c53d029","notes":"Llegó completo, sin faltantes.","cancelledAt":null,"cancelledByUserId":null,"cancelReason":null,"receivedAt":"2026-08-11T18:40:12.000Z","receivedByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-11T18:40:12.000Z","updatedAt":"2026-08-11T18:40:12.000Z"}},"inventory_counts":{"type":"object","description":"Los CONTEOS FÍSICOS: la hoja con la que el taller cuenta el anaquel y cuadra el sistema. Las LÍNEAS del conteo no son recurso de V1 a propósito — lo que el conteo le hizo al inventario ya viaja, asiento por asiento y con su motivo, en `inventory_movements` (`countId` y `countLineId`).","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"folio":{"type":"integer","description":"Consecutivo del documento dentro de su sucursal: es como el taller lo cita de viva voz."},"series":{"type":"string","description":"Serie del folio (por omisión \"A\"). El par serie + folio es el nombre humano del documento."},"status":{"type":"string","description":"Estado del conteo: `abierta`, `aplicada` o `cancelada`."},"scope":{"type":"string","description":"Alcance del conteo: `todo`, `categoria` o `subcategoria`."},"categoryId":{"type":"string","description":"Categoría que acota el conteo, según su alcance."},"subcategoryId":{"type":"string","description":"Subcategoría que acota el conteo, según su alcance."},"name":{"type":"string","description":"Nombre con el que el taller cita el conteo («Corte de agosto»)."},"notes":{"type":"string","description":"Nota libre que capturó el taller en el documento."},"startedAt":{"type":"string","format":"date-time","description":"Cuándo se abrió el conteo."},"appliedAt":{"type":"string","format":"date-time","description":"Cuándo se aplicaron sus ajustes al inventario."},"cancelledAt":{"type":"string","format":"date-time","description":"Fecha de cancelación, en UTC. Nula mientras el documento esté vivo: un documento cancelado NO desaparece de esta API, se marca."},"createdByUserId":{"type":"string","description":"Usuario que creó la fila."},"appliedByUserId":{"type":"string","description":"Usuario que aplicó el conteo."},"cancelledByUserId":{"type":"string","description":"Usuario que canceló el documento."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"9b1e7d54-2a83-4c06-8f92-5d7a0c3e6b81","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","folio":12,"series":"A","status":"aplicada","scope":"categoria","categoryId":"5a7c1e38-4d92-4067-b85f-2e6a0c3d9147","subcategoryId":null,"name":"Conteo de frenos · agosto","notes":"Se contó con el almacén cerrado, después de las 19:00.","startedAt":"2026-08-01T02:00:00.000Z","appliedAt":"2026-08-01T04:35:22.000Z","cancelledAt":null,"createdByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","appliedByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","cancelledByUserId":null,"createdAt":"2026-08-01T02:00:00.000Z","updatedAt":"2026-08-01T04:35:22.000Z"}},"purchase_orders":{"type":"object","description":"Las ÓRDENES DE COMPRA al proveedor: lo pedido, antes de que llegue. Sus recepciones se ligan por `inventory_receipts.purchaseOrderId`, y `receivedAt` se sella solo cuando NINGUNA línea quedó pendiente; cancelar una recepción ligada devuelve la orden a `pendiente`.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"supplierId":{"type":"string","description":"Proveedor del documento. Resuélvelo contra el recurso `suppliers`."},"folio":{"type":"integer","description":"Consecutivo del documento dentro de su sucursal: es como el taller lo cita de viva voz."},"series":{"type":"string","description":"Serie del folio (por omisión \"A\"). El par serie + folio es el nombre humano del documento."},"status":{"type":"string","description":"Estado de la orden: `pendiente`, `parcial`, `recibida` o `cancelada`."},"notes":{"type":"string","description":"Nota libre que capturó el taller en el documento."},"receivedAt":{"type":"string","format":"date-time","description":"Sello de recepción TOTAL: ninguna línea quedó pendiente."},"cancelledAt":{"type":"string","format":"date-time","description":"Fecha de cancelación, en UTC. Nula mientras el documento esté vivo: un documento cancelado NO desaparece de esta API, se marca."},"cancelledByUserId":{"type":"string","description":"Usuario que canceló el documento."},"cancelReason":{"type":"string","description":"Motivo escrito de la cancelación."},"createdByUserId":{"type":"string","description":"Usuario que creó la fila."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"7d4a2f60-3c95-4e18-b072-8a6f1c53d029","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","supplierId":"8e2c6a04-9f71-4b35-8d60-3a15c7e94b28","folio":205,"series":"A","status":"recibida","notes":"Surtir antes del viernes.","receivedAt":"2026-08-11T18:40:12.000Z","cancelledAt":null,"cancelledByUserId":null,"cancelReason":null,"createdByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-08T16:22:40.000Z","updatedAt":"2026-08-11T18:40:12.000Z"}},"supplier_returns":{"type":"object","description":"Las DEVOLUCIONES A PROVEEDOR: la mercancía que se regresa y qué se acordó a cambio. El importe del reembolso es el ACORDADO con el proveedor, no el costo de la capa — la diferencia queda visible, jamás como asiento escondido. Las piezas que salieron están en `inventory_movements` filtrando por `supplierReturnId`.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"supplierId":{"type":"string","description":"Proveedor del documento. Resuélvelo contra el recurso `suppliers`."},"folio":{"type":"integer","description":"Consecutivo del documento dentro de su sucursal: es como el taller lo cita de viva voz."},"series":{"type":"string","description":"Serie del folio (por omisión \"A\"). El par serie + folio es el nombre humano del documento."},"receiptId":{"type":"string","description":"Recepción de ORIGEN de la mercancía devuelta; opcional. La base impide citar la recepción de otro proveedor."},"reason":{"type":"string","description":"Motivo de la devolución: `defectuosa`, `equivocada`, `sobrante` o `garantia`."},"refundMethod":{"type":"string","description":"Cómo se resarce: `pendiente`, `nota_credito`, `reposicion` o `efectivo`."},"refundAmountCents":{"type":"integer","description":"Importe acordado del reembolso, en centavos. Nace en 0 cuando el método es `pendiente` y se documenta al llegar."},"notes":{"type":"string","description":"Nota libre que capturó el taller en el documento."},"cancelledAt":{"type":"string","format":"date-time","description":"Fecha de cancelación, en UTC. Nula mientras el documento esté vivo: un documento cancelado NO desaparece de esta API, se marca."},"cancelledByUserId":{"type":"string","description":"Usuario que canceló el documento."},"cancelReason":{"type":"string","description":"Motivo escrito de la cancelación."},"createdByUserId":{"type":"string","description":"Usuario que creó la fila."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"4e0c8a35-7f16-4b29-9d54-2c31e6b7f08a","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","supplierId":"8e2c6a04-9f71-4b35-8d60-3a15c7e94b28","folio":7,"series":"A","receiptId":"f19d4c73-0b28-4e61-9a35-6c80d2f5a417","reason":"defectuosa","refundMethod":"nota_credito","refundAmountCents":112000,"notes":"Una pieza con el empaque roto y el disco rayado.","cancelledAt":null,"cancelledByUserId":null,"cancelReason":null,"createdByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-12T15:18:36.000Z","updatedAt":"2026-08-13T19:04:51.000Z"}},"service_templates":{"type":"object","description":"El CATÁLOGO DE SERVICIOS del taller: las plantillas con las que se arma una orden en dos clics, con su precio, su mano de obra y su garantía por defecto. Todos los importes van SIN IVA. La fila con `workshopId` nulo es la plantilla corporativa que aplica a toda sucursal.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal dueña de la plantilla; NULO = plantilla corporativa, válida en toda la empresa."},"name":{"type":"string","description":"Nombre del servicio como lo lee el cliente."},"canonicalKey":{"type":"string","description":"Llave canónica del servicio (`afinacion_mayor`, `cambio_aceite`…): es la que permite comparar el mismo servicio entre talleres."},"description":{"type":"string","description":"Qué incluye el servicio, en palabras para el cliente."},"salePriceCents":{"type":"integer","description":"Precio de venta de la plantilla, en centavos y SIN IVA. El catálogo se guarda neto: si el taller captura con el impuesto dentro, la conversión ocurre al capturar el precio, nunca al cotizar."},"laborHours":{"type":"number","description":"Horas de mano de obra que la plantilla sugiere."},"pricingType":{"type":"string","description":"Cómo precia la plantilla: `fijo` o `por_hora`."},"laborMultiplier":{"type":"number","description":"Multiplicador de la mano de obra sobre la tarifa de la sucursal."},"satProductKey":{"type":"string","description":"Clave de producto/servicio del SAT de la plantilla."},"satUnitKey":{"type":"string","description":"Clave de unidad del SAT de la plantilla."},"warrantyMonths":{"type":"integer","description":"Meses de garantía por defecto del servicio."},"warrantyKm":{"type":"integer","description":"Kilómetros de garantía por defecto del servicio."},"isActive":{"type":"boolean","description":"false = la plantilla ya no se ofrece, pero las órdenes viejas la conservan."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"2c8b4f96-1d73-4a05-9e68-7b3f0c5a9d12","workshopId":null,"name":"Afinación mayor 4 cilindros","canonicalKey":"afinacion_mayor","description":"Bujías, filtros de aire, aceite y gasolina, y limpieza de cuerpo de aceleración.","salePriceCents":285000,"laborHours":2.5,"pricingType":"fijo","laborMultiplier":1,"satProductKey":"78181500","satUnitKey":"E48","warrantyMonths":3,"warrantyKm":5000,"isActive":true,"createdAt":"2025-09-19T16:12:38.000Z","updatedAt":"2026-07-02T20:45:13.000Z"}},"suppliers":{"type":"object","description":"Los PROVEEDORES del taller, con su RFC —el que la DIOT necesita junto al IVA de cada recepción— y sus días de crédito. La fila con `workshopId` nulo es el proveedor corporativo, válido en toda sucursal.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal dueña del proveedor; NULO = proveedor corporativo de toda la empresa."},"name":{"type":"string","description":"Nombre comercial del proveedor."},"taxId":{"type":"string","description":"RFC del proveedor: con el IVA de cada recepción es el insumo de la DIOT."},"legalName":{"type":"string","description":"Razón social del proveedor."},"email":{"type":"string","description":"Correo del proveedor."},"phone":{"type":"string","description":"Teléfono del proveedor."},"mobile":{"type":"string","description":"Móvil del proveedor."},"addressLine":{"type":"string","description":"Calle y número."},"addressNeighborhood":{"type":"string","description":"Colonia."},"addressCity":{"type":"string","description":"Municipio o ciudad."},"addressState":{"type":"string","description":"Estado."},"addressPostalCode":{"type":"string","description":"Código postal."},"creditDays":{"type":"integer","description":"Días de crédito que el proveedor le da al taller."},"notes":{"type":"string","description":"Nota libre que capturó el taller en el documento."},"isActive":{"type":"boolean","description":"false = ya no se le compra, pero su historia se conserva."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"8e2c6a04-9f71-4b35-8d60-3a15c7e94b28","workshopId":null,"name":"Refaccionaria del Norte","taxId":"RNO980412K73","legalName":"Refaccionaria del Norte SA de CV","email":"ventas@refanorte.example.mx","phone":"+528183120044","mobile":"+528111223344","addressLine":"Av. Colón 1450","addressNeighborhood":"Industrial","addressCity":"Monterrey","addressState":"Nuevo León","addressPostalCode":"64440","creditDays":30,"notes":"Entrega dos veces al día en zona Cumbres.","isActive":true,"createdAt":"2025-05-27T15:41:07.000Z","updatedAt":"2026-06-18T22:09:33.000Z"}},"service_payments":{"type":"object","description":"EL LIBRO DE COBROS de la orden: cada vez que entró dinero, con su forma de pago y su destino. Append-only —pagina por `createdAt`—: un cobro no se edita ni se borra, se REVERSA con otra fila que apunta a él (`reversesPaymentId`). El desglose fiscal de qué factura saldó cada peso está en `payment_applications`.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"serviceId":{"type":"string","description":"Orden de servicio a la que pertenece la fila. Resuélvela contra el recurso `services`."},"coversDocumentId":{"type":"string","description":"Comprobante fiscal que este cobro salda, cuando se cobró contra una factura ya emitida."},"method":{"type":"string","description":"Forma de pago: `efectivo`, `tarjeta`, `transferencia`, `cheque` o `otro`."},"amountCents":{"type":"integer","description":"Importe cobrado, en centavos. En una reversa es negativo."},"satPaymentForm":{"type":"string","description":"Clave de forma de pago del SAT (01 efectivo, 03 transferencia, 04 tarjeta…)."},"reference":{"type":"string","description":"Referencia del cobro: autorización de la terminal, folio de la transferencia…"},"notes":{"type":"string","description":"Nota libre del cobro."},"cashSessionId":{"type":"string","description":"Corte de caja (sesión) en el que cayó la operación. Resuélvelo contra `cash_sessions`."},"bankAccountId":{"type":"string","description":"Cuenta bancaria del propio taller involucrada en la operación."},"reversesPaymentId":{"type":"string","description":"Cobro que esta fila reversa. Nulo en un cobro normal."},"reversalReason":{"type":"string","description":"Motivo escrito de la reversa."},"receivedAt":{"type":"string","format":"date-time","description":"Cuándo se recibió el dinero."},"receivedByUserId":{"type":"string","description":"Usuario que recibió el cobro."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."}},"example":{"id":"6c2f8e05-4a91-437d-b168-0d9a5c3e7f42","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","serviceId":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29","coversDocumentId":null,"method":"tarjeta","amountCents":860720,"satPaymentForm":"04","reference":"AUTH 448210","notes":null,"cashSessionId":"ae13c749-08b6-4d52-9371-2f5c8a0e6b34","bankAccountId":"d83b6f14-5c07-4e92-8a36-1b70e2c9d548","reversesPaymentId":null,"reversalReason":null,"receivedAt":"2026-08-15T21:12:44.000Z","receivedByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-15T21:12:44.000Z"}},"payment_applications":{"type":"object","description":"EL COMPLEMENTO DE PAGO desglosado: qué pago saldó qué factura, con la base gravable, el impuesto y el saldo antes y después de cada aplicación. Es lo que la contabilidad no puede deducir de `service_payments` sola, y el insumo del CFDI de pagos (REP).","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"paymentId":{"type":"string","description":"Cobro que se está aplicando. Resuélvelo contra `service_payments`."},"serviceId":{"type":"string","description":"Orden de servicio a la que pertenece la fila. Resuélvela contra el recurso `services`."},"invoiceDocumentId":{"type":"string","description":"Factura que este renglón salda. Resuélvela contra `fiscal_documents`."},"repDocumentId":{"type":"string","description":"CFDI de pagos (REP) que amparó esta aplicación, una vez timbrado."},"amountCents":{"type":"integer","description":"Importe aplicado a esa factura, en centavos."},"installment":{"type":"integer","description":"Número de parcialidad: 1 el primer abono, 2 el segundo…"},"previousBalanceCents":{"type":"integer","description":"Saldo de la factura ANTES de esta aplicación, en centavos."},"outstandingCents":{"type":"integer","description":"Saldo de la factura DESPUÉS de esta aplicación, en centavos."},"taxBaseCents":{"type":"integer","description":"Base gravable del importe aplicado, en centavos."},"taxCents":{"type":"integer","description":"Impuesto trasladado del importe aplicado, en centavos."},"taxRate":{"type":"number","description":"Tasa de impuesto de la aplicación (0.16 = 16 %)."},"objectImpDr":{"type":"string","description":"Clave del SAT «objeto de impuesto» del documento relacionado (01 no objeto, 02 sí objeto…)."},"paidAt":{"type":"string","format":"date-time","description":"Fecha del pago que se está aplicando."},"satPaymentForm":{"type":"string","description":"Clave de forma de pago del SAT con la que se timbra la aplicación."},"currency":{"type":"string","description":"Moneda del pago (ISO 4217)."},"exchangeRate":{"type":"number","description":"Tipo de cambio del pago cuando la moneda no es la de la factura."},"operationNumber":{"type":"string","description":"Número de operación bancaria del pago."},"receiverBankTaxId":{"type":"string","description":"RFC del banco receptor: el del propio taller."},"receiverAccount":{"type":"string","description":"Cuenta del taller donde cayó el pago."},"repRequired":{"type":"boolean","description":"true = esta aplicación EXIGE timbrar un CFDI de pagos."},"repDueAt":{"type":"string","format":"date-time","description":"Fecha límite para timbrar ese CFDI de pagos."},"cancelledAt":{"type":"string","format":"date-time","description":"Cuándo se canceló la aplicación."},"cancelledReason":{"type":"string","description":"Motivo de la cancelación de la aplicación."},"createdByUserId":{"type":"string","description":"Usuario que creó la fila."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."},"payerBankTaxId":{"type":"string","description":"RFC del banco ORDENANTE: el del cliente que pagó. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"payerBankName":{"type":"string","description":"Nombre del banco ordenante del cliente. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"payerAccount":{"type":"string","description":"Cuenta del cliente desde la que salió el pago. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true}},"example":{"id":"2f7b0c96-8d34-4a15-9e28-6c0f3a51b7d4","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","paymentId":"6c2f8e05-4a91-437d-b168-0d9a5c3e7f42","serviceId":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29","invoiceDocumentId":"b40e9c27-1a58-4d63-8072-5f9c3e6a1408","repDocumentId":null,"amountCents":860720,"installment":1,"previousBalanceCents":860720,"outstandingCents":0,"taxBaseCents":742000,"taxCents":118720,"taxRate":0.16,"objectImpDr":"02","paidAt":"2026-08-15T21:12:44.000Z","satPaymentForm":"04","currency":"MXN","exchangeRate":1,"operationNumber":"448210","receiverBankTaxId":"BBA830831LJ2","receiverAccount":"0182","repRequired":true,"repDueAt":"2026-09-05T05:59:59.000Z","cancelledAt":null,"cancelledReason":null,"createdByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-15T21:12:45.000Z","updatedAt":"2026-08-15T21:12:45.000Z","payerBankTaxId":"BNM840515VB1","payerBankName":"Banco Nacional de México","payerAccount":"7734"}},"opening_receivables":{"type":"object","description":"La CARTERA IMPORTADA del arranque: lo que los clientes ya le debían al taller cuando entró a Pitflo. Sin ella, la cobranza que ve tu sistema empieza el día de la migración y la contabilidad no cuadra. El saldo vivo es `totalCents − collectedCents`; los abonos en efectivo se ven en `cash_movements.openingReceivableId`.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"customerId":{"type":"string","description":"Cliente al que pertenece la fila. Resuélvelo contra el recurso `customers`."},"reference":{"type":"string","description":"Folio o referencia del documento de ORIGEN, en el sistema anterior. Es la llave natural del import: volver a subir el archivo no duplica deudas."},"issuedOn":{"type":"string","format":"date-time","description":"Fecha del documento original."},"dueOn":{"type":"string","format":"date-time","description":"Fecha de vencimiento de la deuda."},"totalCents":{"type":"integer","description":"Lo que el documento de origen decía, en centavos."},"collectedCents":{"type":"integer","description":"Lo ya cobrado de esa deuda, en centavos. Solo sube, y admite abonos parciales."},"importBatchId":{"type":"string","description":"Lote de importación que trajo la fila: sirve para agrupar una migración entera."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"c58a2e70-9147-4b03-8d65-3f0c7a91e2b6","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","customerId":"9d4b1e77-2c85-4a13-8f60-5b2e7a9c0d41","reference":"FAC-2025-4471","issuedOn":"2025-12-18T00:00:00.000Z","dueOn":"2026-01-17T00:00:00.000Z","totalCents":1200000,"collectedCents":400000,"importBatchId":"1e4d7b90-3a26-4c58-b071-9f6c2e8a5d13","createdAt":"2026-01-05T18:00:11.000Z","updatedAt":"2026-07-22T17:45:38.000Z"}},"cash_sessions":{"type":"object","description":"LOS CORTES DE CAJA: una sesión por turno de caja registradora, del fondo inicial al arqueo. Mientras la sesión está abierta, todo el sellado del cierre es nulo — no existe el corte a medias. `differenceCents` es `countedCashCents − expectedCashCents` y NO se puede «ajustar» para que cuadre: un faltante se explica, no se borra.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"cashRegisterId":{"type":"string","description":"Caja registradora física de la sucursal a la que pertenece el turno."},"folio":{"type":"integer","description":"Consecutivo del documento dentro de su sucursal: es como el taller lo cita de viva voz."},"openingFloatCents":{"type":"integer","description":"Fondo con el que abrió la caja, en centavos."},"openedAt":{"type":"string","format":"date-time","description":"Cuándo se abrió la caja."},"openedByUserId":{"type":"string","description":"Usuario que abrió la caja."},"openingNotes":{"type":"string","description":"Nota de la apertura."},"closedAt":{"type":"string","format":"date-time","description":"Cuándo se cerró la caja; nulo mientras el turno siga abierto."},"closedByUserId":{"type":"string","description":"Usuario que cerró la caja."},"expectedCashCents":{"type":"integer","description":"Efectivo que el sistema afirmaba tener al cerrar, en centavos: la suma de los movimientos de efectivo del turno."},"countedCashCents":{"type":"integer","description":"Efectivo contado físicamente en el arqueo, en centavos."},"differenceCents":{"type":"integer","description":"Contado menos esperado, en centavos: negativo es faltante, positivo sobrante."},"differenceReason":{"type":"string","description":"Explicación de la diferencia. Obligatoria a partir de $1.00 en valor absoluto."},"closingNotes":{"type":"string","description":"Nota del cierre."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"ae13c749-08b6-4d52-9371-2f5c8a0e6b34","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","cashRegisterId":"e70c4a18-6b93-4d25-8f01-2a5c9e3b7d64","folio":412,"openingFloatCents":150000,"openedAt":"2026-08-15T13:55:02.000Z","openedByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","openingNotes":"Fondo verificado con la gerente.","closedAt":"2026-08-16T01:18:47.000Z","closedByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","expectedCashCents":984300,"countedCashCents":984200,"differenceCents":-100,"differenceReason":"Redondeo de un cobro en efectivo.","closingNotes":"Depósito programado para mañana temprano.","createdAt":"2026-08-15T13:55:02.000Z","updatedAt":"2026-08-16T01:18:47.000Z"}},"cash_movements":{"type":"object","description":"EL LIBRO DE LA CAJA: cada peso que entró o salió del cajón, atado a su turno. El importe va SIGNADO según el `kind`, así que la suma del turno ES el efectivo esperado, sin casos especiales. Append-only —pagina por `createdAt`—: un movimiento no se edita, se CONTRA-ASIENTA con otra fila (`reversesMovementId`). Cada movimiento cita EXACTAMENTE UN origen, o ninguno cuando es un retiro o un ingreso manual.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"cashSessionId":{"type":"string","description":"Corte de caja (sesión) en el que cayó la operación. Resuélvelo contra `cash_sessions`."},"kind":{"type":"string","description":"Qué clase de movimiento es: `fondo_inicial`, `cobro`, `pago_gasto`, `devolucion`, `retiro`, `ingreso` o `reverso_devolucion`."},"amountCents":{"type":"integer","description":"Importe SIGNADO del movimiento, en centavos, atado al `kind`: la suma del turno es el efectivo esperado."},"servicePaymentId":{"type":"string","description":"Cobro de orden que originó el movimiento."},"expensePaymentId":{"type":"string","description":"Pago de gasto que originó el movimiento."},"fiscalDocumentId":{"type":"string","description":"Nota de crédito cuyo dinero se le regresó al cliente."},"openingReceivableId":{"type":"string","description":"Saldo inicial importado que este cobro abona. Admite abonos parciales: una deuda se paga en varias visitas."},"reversesMovementId":{"type":"string","description":"Movimiento que esta fila contra-asienta."},"reversedAt":{"type":"string","format":"date-time","description":"Sello en el movimiento ORIGINAL cuando alguien lo contra-asentó."},"reason":{"type":"string","description":"Motivo de catálogo, obligatorio en `retiro` e `ingreso`: `deposito_bancario`, `compra_menor`, `prestamo`, `otro`…"},"concept":{"type":"string","description":"Concepto escrito del retiro o del ingreso."},"depositReference":{"type":"string","description":"Folio de la ficha de depósito bancario del retiro."},"bankAccountId":{"type":"string","description":"Cuenta bancaria del propio taller involucrada en la operación."},"occurredAt":{"type":"string","format":"date-time","description":"Fecha del hecho según el documento, en UTC. Puede ser anterior a `createdAt` cuando se capturó después."},"createdByUserId":{"type":"string","description":"Usuario que registró el movimiento."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."}},"example":{"id":"9e5b1a34-7c08-4f26-8b93-0d61c2a7e548","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","cashSessionId":"ae13c749-08b6-4d52-9371-2f5c8a0e6b34","kind":"retiro","amountCents":-800000,"servicePaymentId":null,"expensePaymentId":null,"fiscalDocumentId":null,"openingReceivableId":null,"reversesMovementId":null,"reversedAt":null,"reason":"deposito_bancario","concept":"Depósito del corte de la tarde.","depositReference":"DEP-77120","bankAccountId":"d83b6f14-5c07-4e92-8a36-1b70e2c9d548","occurredAt":"2026-08-16T01:05:30.000Z","createdByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-16T01:05:30.000Z"}},"fiscal_documents":{"type":"object","description":"EL COMPROBANTE FISCAL (CFDI) del taller: facturas, notas de crédito, facturas globales y comprobantes de pago, con el emisor y el receptor CONGELADOS tal como se timbraron. El XML y el PDF no viajan por esta API —se descargan desde la pantalla—; lo que viaja son los datos del negocio. Los saldos (`collectedCents`, `outstandingCents`) se mueven con cada `payment_applications`.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"serviceId":{"type":"string","description":"Orden de servicio a la que pertenece la fila. Resuélvela contra el recurso `services`."},"customerId":{"type":"string","description":"Cliente al que pertenece la fila. Resuélvelo contra el recurso `customers`."},"type":{"type":"string","description":"Tipo de comprobante: `ingreso` (factura), `egreso` (nota de crédito), `pago` (REP) o `global`."},"status":{"type":"string","description":"Estado del comprobante: `borrador`, `timbrado`, `cancelado` o `error`."},"series":{"type":"string","description":"Serie fiscal del comprobante."},"folio":{"type":"integer","description":"Folio fiscal consecutivo dentro de su serie."},"issuedAt":{"type":"string","format":"date-time","description":"Fecha de emisión del comprobante."},"uuid":{"type":"string","description":"Folio fiscal (UUID) que asignó el SAT al timbrar. Nulo mientras sea borrador."},"stampedAt":{"type":"string","format":"date-time","description":"Cuándo se timbró."},"cancelableUntil":{"type":"string","format":"date-time","description":"Hasta cuándo se puede cancelar sin aceptación del receptor."},"verificationUrl":{"type":"string","description":"URL de verificación del SAT del comprobante."},"issuerLegalName":{"type":"string","description":"Razón social del EMISOR, congelada al timbrar."},"issuerTaxId":{"type":"string","description":"RFC del emisor."},"issuerTaxRegime":{"type":"string","description":"Régimen fiscal del emisor."},"issuerZipCode":{"type":"string","description":"Código postal del lugar de expedición."},"issuerPersonType":{"type":"string","description":"Persona `fisica` o `moral` del emisor."},"currency":{"type":"string","description":"Moneda del comprobante (ISO 4217)."},"exchangeRate":{"type":"number","description":"Tipo de cambio cuando la moneda no es la nacional."},"paymentMethod":{"type":"string","description":"Método de pago del SAT: `PUE` (una exhibición) o `PPD` (parcialidades o diferido)."},"paymentForm":{"type":"string","description":"Clave de forma de pago del SAT (01 efectivo, 03 transferencia…)."},"paymentTerms":{"type":"string","description":"Condición de pago del comprobante: `contado` o `credito`."},"creditDueDate":{"type":"string","format":"date-time","description":"Vencimiento del crédito del comprobante."},"grossSubtotalCents":{"type":"integer","description":"Subtotal antes de descuentos e impuestos, en centavos."},"discountCents":{"type":"integer","description":"Descuento del comprobante, en centavos."},"taxTransferredCents":{"type":"integer","description":"Impuestos TRASLADADOS (IVA cobrado), en centavos."},"taxWithheldCents":{"type":"integer","description":"Impuestos RETENIDOS, en centavos."},"totalCents":{"type":"integer","description":"Total del comprobante, en centavos."},"creditedCents":{"type":"integer","description":"Cuánto de este comprobante se acreditó con notas de crédito, en centavos."},"collectedCents":{"type":"integer","description":"Cuánto se ha cobrado del comprobante, en centavos."},"outstandingCents":{"type":"integer","description":"Saldo pendiente del comprobante, en centavos."},"refundableCents":{"type":"integer","description":"Cuánto se le puede devolver al cliente, en centavos."},"replacesDocumentId":{"type":"string","description":"Comprobante que este sustituye tras una cancelación."},"satRelationType":{"type":"string","description":"Tipo de relación del SAT con el comprobante citado (01 nota de crédito, 04 sustitución…)."},"supersededAt":{"type":"string","format":"date-time","description":"Cuándo este comprobante quedó sustituido por otro."},"creditsDocumentId":{"type":"string","description":"Factura a la que esta nota de crédito le resta."},"creditNoteKind":{"type":"string","description":"Clase de nota de crédito: `descuento`, `devolucion` o `cancelacion`."},"creditReason":{"type":"string","description":"Motivo escrito de la nota de crédito."},"globalPeriodicity":{"type":"string","description":"Periodicidad de la factura global: `dia`, `semana`, `quincena` o `mes`."},"globalPeriodStart":{"type":"string","format":"date-time","description":"Inicio del período que ampara la factura global."},"globalPeriodEnd":{"type":"string","format":"date-time","description":"Fin del período que ampara la factura global."},"coversRemissions":{"type":"boolean","description":"Ids de las remisiones que la factura global ampara."},"cancellationStatus":{"type":"string","description":"Estado de la cancelación ante el SAT: `solicitada`, `aceptada`, `rechazada` o `en_proceso`."},"cancelMotive":{"type":"string","description":"Clave de motivo de cancelación del SAT (01 a 04)."},"cancelledAt":{"type":"string","format":"date-time","description":"Fecha de cancelación, en UTC. Nula mientras el documento esté vivo: un documento cancelado NO desaparece de esta API, se marca."},"createdByUserId":{"type":"string","description":"Usuario que creó la fila."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."},"receiverLegalName":{"type":"string","description":"Razón social del RECEPTOR, congelada al timbrar. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"receiverTaxId":{"type":"string","description":"RFC del receptor. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"receiverTaxRegime":{"type":"string","description":"Régimen fiscal del receptor. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"receiverZipCode":{"type":"string","description":"Código postal fiscal del receptor. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"receiverCfdiUse":{"type":"string","description":"Uso del CFDI que declaró el receptor (G03 gastos en general…). Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"receiverEmail":{"type":"string","description":"Correo al que se le envió el comprobante al receptor. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true}},"example":{"id":"b40e9c27-1a58-4d63-8072-5f9c3e6a1408","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","serviceId":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29","customerId":"9d4b1e77-2c85-4a13-8f60-5b2e7a9c0d41","type":"ingreso","status":"timbrado","series":"F","folio":1907,"issuedAt":"2026-08-15T21:30:02.000Z","uuid":"A7F3C2E1-58B4-4D09-9E62-0C1A7B35D846","stampedAt":"2026-08-15T21:30:07.000Z","cancelableUntil":"2026-08-16T21:30:07.000Z","verificationUrl":"https://verificacfdi.facturaelectronica.sat.gob.mx/default.aspx?id=A7F3C2E1-58B4-4D09-9E62-0C1A7B35D846","issuerLegalName":"Servicio Automotriz Cumbres SA de CV","issuerTaxId":"SAC120315M42","issuerTaxRegime":"601","issuerZipCode":"64610","issuerPersonType":"moral","currency":"MXN","exchangeRate":1,"paymentMethod":"PUE","paymentForm":"04","paymentTerms":"contado","creditDueDate":null,"grossSubtotalCents":742000,"discountCents":0,"taxTransferredCents":118720,"taxWithheldCents":0,"totalCents":860720,"creditedCents":0,"collectedCents":860720,"outstandingCents":0,"refundableCents":0,"replacesDocumentId":null,"satRelationType":null,"supersededAt":null,"creditsDocumentId":null,"creditNoteKind":null,"creditReason":null,"globalPeriodicity":null,"globalPeriodStart":null,"globalPeriodEnd":null,"coversRemissions":[],"cancellationStatus":null,"cancelMotive":null,"cancelledAt":null,"createdByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-15T21:29:48.000Z","updatedAt":"2026-08-15T21:30:07.000Z","receiverLegalName":"JOSE RAMON VILLARREAL","receiverTaxId":"VIRJ850612QX3","receiverTaxRegime":"612","receiverZipCode":"66220","receiverCfdiUse":"G03","receiverEmail":"jr.villarreal@example.mx"}},"expenses":{"type":"object","description":"LOS GASTOS del taller: lo que se debe y por qué. La fila con `workshopId` nulo es el gasto corporativo, que aplica a toda sucursal. Un gasto no se borra: se CANCELA, y la cancelación viaja. Lo que efectivamente salió de la caja o del banco está en `expense_payments`.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que se carga el gasto; NULO = gasto corporativo de toda la empresa."},"concept":{"type":"string","description":"Concepto del gasto, en palabras del taller."},"categoryId":{"type":"string","description":"Categoría contable del gasto."},"supplierId":{"type":"string","description":"Proveedor a quien se le debe el gasto."},"amountCents":{"type":"integer","description":"Importe TOTAL del gasto, en centavos, con el IVA dentro."},"taxCents":{"type":"integer","description":"IVA acreditable contenido en el importe, en centavos: con el RFC del proveedor es el insumo de la DIOT."},"incurredAt":{"type":"string","format":"date-time","description":"Fecha en la que se incurrió el gasto."},"dueDate":{"type":"string","format":"date-time","description":"Fecha en la que hay que pagarlo."},"supplierDocument":{"type":"string","description":"Folio del comprobante del proveedor."},"notes":{"type":"string","description":"Nota libre que capturó el taller en el documento."},"settledAt":{"type":"string","format":"date-time","description":"Cuándo quedó saldado el gasto por completo; nulo mientras deba algo."},"receiptId":{"type":"string","description":"Recepción de mercancía que generó el gasto, si nació de una compra a almacén."},"expenseScheduleId":{"type":"string","description":"Plantilla de gasto RECURRENTE de la que nació esta fila: distingue la renta generada de la capturada a mano."},"scheduleDueOn":{"type":"string","format":"date-time","description":"Período que ampara el gasto recurrente."},"cancelledAt":{"type":"string","format":"date-time","description":"Fecha de cancelación, en UTC. Nula mientras el documento esté vivo: un documento cancelado NO desaparece de esta API, se marca."},"cancelledByUserId":{"type":"string","description":"Usuario que canceló el documento."},"cancellationReason":{"type":"string","description":"Motivo de la cancelación del gasto."},"createdByUserId":{"type":"string","description":"Usuario que creó la fila."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"48f2c7b1-0956-4e30-a8d7-6c15b920e4d7","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","concept":"Renta del local · agosto 2026","categoryId":"61a9d3f5-2c47-4b80-9e15-8d0f3a6c7b29","supplierId":"5c9e0b73-1d48-4a26-8f50-3b7a2c6e9014","amountCents":5800000,"taxCents":800000,"incurredAt":"2026-08-01T00:00:00.000Z","dueDate":"2026-08-05T00:00:00.000Z","supplierDocument":"A-1183","notes":"Incluye mantenimiento del estacionamiento.","settledAt":"2026-08-04T19:22:08.000Z","receiptId":null,"expenseScheduleId":"f30b8d51-4e72-4c96-a015-7d2c6b9e3a80","scheduleDueOn":"2026-08-05T00:00:00.000Z","cancelledAt":null,"cancelledByUserId":null,"cancellationReason":null,"createdByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-01T14:02:19.000Z","updatedAt":"2026-08-04T19:22:08.000Z"}},"expense_payments":{"type":"object","description":"LOS PAGOS DEL GASTO: la otra mitad del flujo de efectivo. `expenses` dice lo que se debe; esto dice lo que salió, cuándo y por dónde. Append-only —pagina por `createdAt`—: un pago se REVERSA con otra fila, jamás se edita.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"expenseId":{"type":"string","description":"Gasto que este pago abona. Resuélvelo contra `expenses`."},"method":{"type":"string","description":"Forma de pago: `efectivo`, `tarjeta`, `transferencia`, `cheque` o `otro`."},"amountCents":{"type":"integer","description":"Importe pagado, en centavos. En una reversa es negativo."},"reference":{"type":"string","description":"Referencia del pago: folio de la transferencia, número de cheque…"},"notes":{"type":"string","description":"Nota libre del pago."},"cashSessionId":{"type":"string","description":"Corte de caja (sesión) en el que cayó la operación. Resuélvelo contra `cash_sessions`."},"fromRegister":{"type":"boolean","description":"true = el dinero salió del CAJÓN de la caja registradora, y por eso hay un `cash_movements` gemelo."},"bankAccountId":{"type":"string","description":"Cuenta bancaria del propio taller involucrada en la operación."},"reversesPaymentId":{"type":"string","description":"Pago que esta fila reversa."},"reversalReason":{"type":"string","description":"Motivo escrito de la reversa."},"paidAt":{"type":"string","format":"date-time","description":"Cuándo se pagó."},"paidByUserId":{"type":"string","description":"Usuario que registró el pago."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."}},"example":{"id":"b7c4e903-5a18-4d62-8f30-1e9c5a20d763","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","expenseId":"48f2c7b1-0956-4e30-a8d7-6c15b920e4d7","method":"transferencia","amountCents":5800000,"reference":"SPEI 20260804-118342","notes":null,"cashSessionId":null,"fromRegister":false,"bankAccountId":"d83b6f14-5c07-4e92-8a36-1b70e2c9d548","reversesPaymentId":null,"reversalReason":null,"paidAt":"2026-08-04T19:22:08.000Z","paidByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-04T19:22:08.000Z"}},"remissions":{"type":"object","description":"LAS REMISIONES: el papel con el que se entrega el trabajo cuando el cliente no pidió factura. Congela al cliente y al vehículo tal como se imprimieron. Si después el ticket se factura —solo o dentro de una global—, `invoiceDocumentId` y `coveredAt` lo dicen.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal a la que pertenece la fila. Resuélvela contra el recurso `workshops`."},"serviceId":{"type":"string","description":"Orden de servicio a la que pertenece la fila. Resuélvela contra el recurso `services`."},"series":{"type":"string","description":"Serie del folio (por omisión \"A\"). El par serie + folio es el nombre humano del documento."},"folio":{"type":"integer","description":"Consecutivo del documento dentro de su sucursal: es como el taller lo cita de viva voz."},"status":{"type":"string","description":"Estado de la remisión: `emitida`, `amparada` (ya facturada) o `cancelada`."},"issuedAt":{"type":"string","format":"date-time","description":"Cuándo se emitió."},"issuerName":{"type":"string","description":"Nombre del taller congelado en el papel."},"issuerAddress":{"type":"string","description":"Domicilio del taller congelado en el papel."},"issuerPhone":{"type":"string","description":"Teléfono del taller congelado en el papel."},"customerId":{"type":"string","description":"Cliente al que pertenece la fila. Resuélvelo contra el recurso `customers`."},"vehicleLabel":{"type":"string","description":"Vehículo tal como se imprimió («Nissan Versa 2021»)."},"odometer":{"type":"integer","description":"Kilometraje impreso en la remisión."},"currency":{"type":"string","description":"Moneda de la remisión."},"grossSubtotalCents":{"type":"integer","description":"Subtotal antes de descuentos e impuestos, en centavos."},"discountCents":{"type":"integer","description":"Descuento, en centavos."},"taxRate":{"type":"number","description":"Tasa de impuesto aplicada."},"taxCents":{"type":"integer","description":"Impuesto, en centavos."},"totalCents":{"type":"integer","description":"Total de la remisión, en centavos."},"paymentCondition":{"type":"string","description":"Condición de pago: `contado` o `credito`."},"creditDueDate":{"type":"string","format":"date-time","description":"Vencimiento del crédito de la remisión."},"customerMessage":{"type":"string","description":"Mensaje al cliente impreso en la remisión."},"invoiceDocumentId":{"type":"string","description":"Comprobante que terminó amparando esta remisión."},"coveredAt":{"type":"string","format":"date-time","description":"Cuándo quedó amparada por una factura (columna `amparada_at`)."},"cancelledAt":{"type":"string","format":"date-time","description":"Fecha de cancelación, en UTC. Nula mientras el documento esté vivo: un documento cancelado NO desaparece de esta API, se marca."},"cancelledByUserId":{"type":"string","description":"Usuario que canceló el documento."},"cancellationReason":{"type":"string","description":"Motivo de la cancelación."},"issuedByUserId":{"type":"string","description":"Usuario que emitió la remisión."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."},"customerName":{"type":"string","description":"Nombre del cliente congelado en el papel. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"customerPhone":{"type":"string","description":"Teléfono del cliente congelado en el papel. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"vehiclePlate":{"type":"string","description":"Placa congelada en el papel. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"vehicleVin":{"type":"string","description":"VIN congelado en el papel. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true}},"example":{"id":"d91f5c04-2b76-4a38-8e50-7c3a1d69b025","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","serviceId":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29","series":"R","folio":3348,"status":"emitida","issuedAt":"2026-08-15T21:40:19.000Z","issuerName":"Pitflo Cumbres","issuerAddress":"Av. Paseo de los Leones 3120, Cumbres 3er Sector, Monterrey, NL","issuerPhone":"+528183451200","customerId":"9d4b1e77-2c85-4a13-8f60-5b2e7a9c0d41","vehicleLabel":"Nissan Versa 2021 · Gris Oxford","odometer":87450,"currency":"MXN","grossSubtotalCents":742000,"discountCents":0,"taxRate":0.16,"taxCents":118720,"totalCents":860720,"paymentCondition":"contado","creditDueDate":null,"customerMessage":"Se recomienda cambiar balatas traseras en la próxima visita.","invoiceDocumentId":null,"coveredAt":null,"cancelledAt":null,"cancelledByUserId":null,"cancellationReason":null,"issuedByUserId":"0a3c7e91-5d26-4b80-9f13-8c4e6a2b7d05","createdAt":"2026-08-15T21:40:19.000Z","updatedAt":"2026-08-15T21:40:19.000Z","customerName":"José Ramón Villarreal","customerPhone":"+528118765432","vehiclePlate":"SXH-482-C","vehicleVin":"3N1CN8AD5ML830472"}},"fixed_assets":{"type":"object","description":"EL ACTIVO FIJO del taller: la rampa, el escáner, la compresora. La depreciación NO viaja y no se almacena en ninguna parte — se DERIVA al leer (lineal, mensual) con `originalCostCents`, `annualRatePct` e `inUseFrom`, exactamente igual que la pantalla. La fila con `workshopId` nulo es un activo de la empresa, no de una sucursal.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal dueña del activo; NULO = activo de la empresa."},"name":{"type":"string","description":"Nombre del activo."},"assetType":{"type":"string","description":"Tipo de activo del catálogo (`equipo_taller`, `computo`, `mobiliario`, `vehiculo`…), que sugiere la tasa."},"annualRatePct":{"type":"number","description":"Tasa anual de depreciación en porcentaje (10 = 10 % al año). Vale por sí misma, aunque el tipo sugiera otra."},"originalCostCents":{"type":"integer","description":"Monto original de la inversión (MOI), en centavos."},"inUseFrom":{"type":"string","format":"date-time","description":"Desde cuándo está en uso: es la fecha en la que arranca la depreciación."},"disposedOn":{"type":"string","format":"date-time","description":"Fecha de baja del activo; nula mientras siga en uso."},"disposalReason":{"type":"string","description":"Motivo de la baja (venta, robo, obsolescencia…)."},"notes":{"type":"string","description":"Nota libre del activo."},"createdAt":{"type":"string","format":"date-time","description":"Fecha y hora de alta de la fila, en UTC (ISO 8601)."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"6b03e5c9-4a71-4283-9d06-1f5c8a2e7b40","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","name":"Rampa de dos postes 4 toneladas","assetType":"equipo_taller","annualRatePct":10,"originalCostCents":8500000,"inUseFrom":"2025-03-01T00:00:00.000Z","disposedOn":null,"disposalReason":null,"notes":"Bahía 3. Mantenimiento anual en marzo.","createdAt":"2025-03-04T16:48:52.000Z","updatedAt":"2025-03-04T16:48:52.000Z"}},"ledger_account_maps":{"type":"object","description":"EL MAPEO concepto→cuenta del contador: con qué cuenta contable se registra cada concepto del taller. La fila con `workshopId` nulo es el default de la empresa y la de la sucursal lo sobrescribe — pide las dos, o tu póliza saldrá incompleta. No tiene `createdAt`: `updatedAt` es el único sello, y es el que pagina.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"workshopId":{"type":"string","description":"Sucursal con override; NULO = default de la empresa."},"conceptKey":{"type":"string","description":"Concepto del taller (`venta_refaccion`, `iva_trasladado`, `banco:<bankAccountId>`, `gasto:<categoryId>`…). El sufijo tras los dos puntos apunta a la entidad concreta."},"accountCode":{"type":"string","description":"Código de la cuenta contable a la que se registra el concepto."},"accountName":{"type":"string","description":"Nombre de esa cuenta, como lo escribió el contador."},"updatedAt":{"type":"string","format":"date-time","description":"Fecha y hora del último cambio, en UTC. Es el campo que filtra `updated_since` y el que ordena la página."}},"example":{"id":"3c8f0b64-5d19-4a27-8e05-9b1a6c37e2d4","workshopId":null,"conceptKey":"venta_refaccion","accountCode":"401-01-000","accountName":"Ventas de refacciones","updatedAt":"2026-02-17T18:35:41.000Z"}},"employees":{"type":"object","description":"EL PADRÓN DE PERSONAS de tu empresa, en vista MÍNIMA: quién es, qué rol tiene, en qué estado está y a qué sucursales alcanza. Existe para que puedas resolver a un nombre los `…ByUserId` que esta API devuelve por todos lados (quién recibió el pago, quién movió la refacción, quién cerró la caja); sin él, cada actor es un uuid ciego. NO trae credenciales ni su reloj (contraseña, intentos fallidos, bloqueos), ni sesiones, ni preferencias personales (tema, zona horaria, idioma, avisos), ni la última conexión: eso es la cerradura de la cuenta, no dato del negocio. Una persona dada de baja deja de aparecer el mismo día.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"roleId":{"type":"string","description":"Rol que decide QUÉ puede hacer la persona. Es un id de rol de tu propia empresa (los roles son configurables por taller), así que su nombre se administra dentro de Pitflo y aquí solo viaja la llave: dos personas con el mismo `roleId` tienen exactamente los mismos permisos."},"status":{"type":"string","description":"Estado de la cuenta: `pending_activation` (invitada, todavía no entra), `active`, `suspended` (bloqueada temporalmente) o `disabled` (dada de baja del sistema, conserva su historial)."},"workshopIds":{"type":"array","items":{"type":"string"},"description":"Las sucursales a las que la persona alcanza HOY, resueltas en cada consulta desde sus accesos VIVOS: un acceso revocado desaparece de esta lista al instante. Arreglo vacío = todavía no le han dado ninguna. Resuélvelas contra el recurso `workshops`."},"createdAt":{"type":"string","format":"date-time","description":"Cuándo se dio de alta la persona en el sistema, en UTC."},"updatedAt":{"type":"string","format":"date-time","description":"Último cambio de su ficha, en UTC: es el sello por el que este recurso pagina y el que responde a `updated_since`."},"name":{"type":"string","description":"Nombre de la persona, tal como lo firma en el taller. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true},"email":{"type":"string","description":"Correo con el que la persona entra a Pitflo. Cuando el taller le dio una cuenta SIN buzón propio, es un correo técnico del dominio `no-mail.pitflo.local`: sirve como identificador, no como buzón — no le escribas ahí. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true}},"example":{"id":"9c4b7e21-5d38-4a60-8f19-2b7e6c05d34a","name":"María Fernanda Robles","email":"mrobles@tallercumbres.mx","roleId":"b8d1f4a7-2c39-4e05-9a68-7f3b1d24c6e0","status":"active","workshopIds":["3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73"],"createdAt":"2026-01-14T16:22:08.000Z","updatedAt":"2026-08-11T19:03:44.000Z"}},"audit_log":{"type":"object","description":"LA BITÁCORA de tu empresa: qué pasó, cuándo, quién lo hizo y sobre qué. Es el libro con el que un auditor reconstruye una jornada sin pedirle capturas de pantalla a nadie, y el mismo que alimenta la pantalla de Auditoría. Cuatro datos y ni uno más: el DETALLE de cada hecho (`metadata`) NO se sirve por decisión expresa —es un campo libre donde cabe cualquier cosa, incluido dato personal de un tercero, y servirlo sería un agujero que nadie vuelve a revisar—. Sus filas son INMUTABLES: se escriben una vez y jamás se editan ni se borran, así que paginar por el hecho no te hace perder nada.","properties":{"id":{"type":"string","description":"Identificador único e inmutable de la fila (UUID). Es la llave con la que deduplicas al sincronizar."},"occurredAt":{"type":"string","format":"date-time","description":"Cuándo ocurrió el hecho, en UTC. Es el sello por el que este recurso ordena y pagina, y el que responde a `updated_since`: como la fila nunca cambia después de escrita, el sello del hecho y el de su última versión son el mismo."},"workshopId":{"type":"string","description":"Sucursal donde ocurrió el hecho, cuando el hecho tiene una. Es NULO para los actos de empresa que no pasan en un taller (dar de alta una sucursal, cambiar la configuración corporativa). Resuélvelo contra el recurso `workshops`."},"actorType":{"type":"string","description":"Qué clase de actor lo hizo: `user` (una persona de tu empresa), `owner` (personal de Pitflo actuando con autorización), `system` (un proceso automático de la casa, como el cierre de jornadas) o `integracion` (una integración empujando datos, como un conector de telemática)."},"action":{"type":"string","description":"Qué se hizo, como llave estable en punto (`servicio.creado`, `pago.registrado`, `usuario.desactivado`). Es el vocabulario interno de Pitflo y crece con el producto: trátalo como texto abierto y no como enumeración cerrada."},"resourceType":{"type":"string","description":"Sobre qué clase de cosa se actuó (`service`, `payment`, `user`…). Igual que `action`, es vocabulario abierto."},"resourceId":{"type":"string","description":"La fila concreta sobre la que se actuó. Es NULO en los hechos que no apuntan a una fila (un inicio de sesión, una exportación)."},"actorId":{"type":"string","description":"Quién lo hizo. Con `actorType = user` es el id de una persona: resuélvelo contra el recurso `employees`. Con `integracion` es el id de la FUENTE, no de una persona. Es NULO cuando el actor es el sistema. Dato personal (marca pii de la allowlist): al marcarlo en tu integración estás sacando datos personales de tus clientes a un sistema externo.","x-pii":true}},"example":{"id":"a1f6c803-9b2e-4d47-85c0-3e9a17d5b268","occurredAt":"2026-08-14T15:41:02.000Z","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","actorType":"user","actorId":"9c4b7e21-5d38-4a60-8f19-2b7e6c05d34a","action":"servicio.etapa_cambiada","resourceType":"service","resourceId":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29"}},"network_workshops":{"type":"object","description":"LOS TALLERES VINCULADOS a tu red, re-resueltos en cada consulta: un taller que se desvincula desaparece de esta lista al instante. Trae su identidad comercial y su ubicación; la identidad FISCAL del franquiciatario no se le vende a la red.","properties":{"id":{"type":"string","description":"Identificador de la sucursal vinculada. Es el mismo `workshopId` que traen las órdenes de `network_services`."},"name":{"type":"string","description":"Nombre comercial de la sucursal."},"brand":{"type":"string","description":"Marca bajo la que opera la sucursal."},"city":{"type":"string","description":"Municipio o ciudad de la sucursal."},"state":{"type":"string","description":"Estado de la sucursal."},"timezone":{"type":"string","description":"Zona horaria IANA de la sucursal: la que hay que usar para leer las fechas UTC de sus órdenes."},"currency":{"type":"string","description":"Moneda de la sucursal (ISO 4217). Todo importe de la red va en centavos de la moneda de SU sucursal."},"active":{"type":"boolean","description":"true = la sucursal está operando; false = está suspendida."},"organization":{"type":"object","description":"La empresa dueña de la sucursal, con su `id` y su `name`: en una red, cada taller puede ser de un franquiciatario distinto."},"linkedSince":{"type":"string","format":"date-time","description":"Desde cuándo la sucursal está vinculada a tu red."}},"example":{"id":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","name":"Pitflo Cumbres","brand":"Pitflo","city":"Monterrey","state":"Nuevo León","timezone":"America/Monterrey","currency":"MXN","active":true,"organization":{"id":"7a15c3e8-0d92-4b47-8f36-2c908e5b1d74","name":"Grupo Automotriz Cumbres"},"linkedSince":"2026-02-01T00:00:00.000Z"}},"network_services":{"type":"object","description":"LAS ÓRDENES de los talleres vinculados, con las MISMAS columnas que el panel de red: lo aprobado y lo facturado, la etapa y el vehículo en grueso. Nunca el cliente, nunca la placa, nunca el VIN, nunca el costo ni el margen. Cada consulta re-resuelve vínculos y permisos: el taller sin permiso de detalle vigente simplemente NO aparece.","properties":{"id":{"type":"string","description":"Identificador de la orden."},"workshopId":{"type":"string","description":"Sucursal dueña de la orden. La API pagina la unión de la red entera, así que sin este campo no sabrías de qué taller es cada orden."},"folio":{"type":"string","description":"Folio de la orden ya armado como lo cita el taller (serie y consecutivo pegados, p. ej. \"A-2841\")."},"createdAt":{"type":"string","format":"date-time","description":"Cuándo se abrió la orden, en UTC."},"stage":{"type":"string","description":"Llave CANÓNICA de la etapa: es lo que hace comparables dos tableros que nadie nombró igual."},"stageName":{"type":"string","description":"Nombre que ESE taller le puso a su columna."},"quoteStatus":{"type":"string","description":"Estado del presupuesto de la orden."},"decidedAt":{"type":"string","format":"date-time","description":"Cuándo el cliente decidió el presupuesto."},"approvedCents":{"type":"integer","description":"Total APROBADO de la orden, en centavos."},"invoicedCents":{"type":"integer","description":"Total FACTURADO de la orden, en centavos; nulo si todavía no se factura."},"vehicle":{"type":"object","description":"El vehículo en grueso: `make`, `model` y `year`. Sin placa y sin VIN, a propósito."}},"example":{"id":"5b8f2d31-6a07-4c92-ae14-73d0b6c85f29","workshopId":"3f2a9c10-7b41-4e58-9a02-1c6d5e8f4b73","folio":"A-2841","createdAt":"2026-08-14T13:47:09.000Z","stage":"reparacion","stageName":"En reparación","quoteStatus":"aprobado","decidedAt":"2026-08-14T14:05:31.000Z","approvedCents":860720,"invoicedCents":null,"vehicle":{"make":"Nissan","model":"Versa","year":2021}}}},"responses":{"error_400":{"description":"Petición inválida (p. ej. INVALID_CURSOR).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"error_401":{"description":"Sin token, token inválido, revocado o vencido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"error_403":{"description":"RESOURCE_NOT_ENABLED: el recurso no está habilitado en tu integración (o una cookie de sesión tocó /v1).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"error_404":{"description":"No existe — o no existe para tu empresa: es el mismo 404.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"error_409":{"description":"WEBHOOK_DISABLED: el webhook está apagado; reactívalo antes de mandarle un ping.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}},"error_429":{"description":"Límite de peticiones; respeta Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/error"}}}}}}}