API · bookings

bookings operations in the OpenAPI contract that accept an API key.

Updated:

GET /bookings

Listar reservas

Por defecto from/to filtran por el inicio de la reserva (from <= inicio < to). Con overlap=true devuelven toda reserva que ocupa parte del rango (inicio < to y fin > from): una tarea que empezó antes y sigue dentro también entra. El orden y el cursor siguen siendo por inicio.

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
fromqueryNostring (date-time)
toqueryNostring (date-time)
overlapqueryNoboolean default falsetrue: from/to filtran por solapamiento (inicio < to y fin > from) en vez de por inicio. Por defecto false.
include_carriedqueryNoboolean default falseF3-573 (arrastre): con from/to, suma las tareas iniciadas que pasaron su fin y siguen ocupando su lugar en el rango (servicio de día completo o que no pide una persona, hasta 7 días después de su fin), aunque hayan empezado antes. Vienen con carried_over=true. Misma regla que GET /slots. Las reglas de visibilidad (staff) no cambian. Por defecto false: el listado no cambia.
feesqueryNostring all, due default allFIU-510 · Tareas-cuota de las suscripciones. all (por defecto) las lista todas. due deja solo las por cobrar vencidas o dentro de su aviso (notice_days del plan) y saca las pagadas, las perdonadas y las de cobro automático al día (vuelven si el cobro falla o no llega). Es la vista de Hoy y Trabajo del panel, de Wagy y del MCP (list_tasks). Solo filtra: no cambia montos ni estados.
scheduling_statequeryNopending, scheduled, queue
statusqueryNoBookingStatus
resource_idqueryNostring
customer_idqueryNostring
limitqueryNointeger default 25
cursorqueryNostring

Responses

StatusDescription
200OK (BookingPage)
defaultError (RFC 9457) (Problem)

POST /bookings

Crear una tarea programada, de cola o pendiente de horario

schedule_later=true sólo para servicios scheduled: owner/manager o API key bookings:write. Crea pending_schedule sin allocations, vencimiento ni cobro; resource_ids son asignaciones operativas. false mantiene el alta legacy: scheduled requiere start, queue permite ventana opcional. Tenant desde credencial; misma clave y cuerpo devuelve el mismo ID aun tras programarlo; cuerpo distinto devuelve 422 idempotency_conflict. 409 membership_blocked si el cliente tiene la membresía que cubre ese servicio bloqueada por impago (plan en el cuerpo; se libera al pagar la cuota). Con flow_id en lugar de service_id (F3-457, ADR 0040 §4.3) crea una tarea sin servicio de un flujo por intención (reclamo, presupuesto): subject.type=intent, confirmada, en cola, sin horario, sin capacidad y sin monto (POST /v1/payments/bookings/{id}/due lo fija; se cobra como cualquier tarea). Mismo permiso (tasks:create, API key bookings:write). 404 si el flujo no existe; 422 flow_not_intent si no es por intención, está desactivado o no es de cola. Emite booking.created (no booking.confirmed: no avisa confirmación al cliente).

  • Authentication: API key or dashboard session
  • Scopes: bookings:write

Parameters

NameInRequiredTypeDescription
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Request body (required)

  • application/json: BookingCreate

Responses

StatusDescription
201Creado (Booking)
409Error (RFC 9457) (Problem)
defaultError (RFC 9457) (Problem)

GET /bookings/{id}

Detalle de reserva

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
idpathYesstring

Responses

StatusDescription
200OK (Booking)
defaultError (RFC 9457) (Problem)

PATCH /bookings/{id}

Editar una reserva (cliente, servicio, recurso, personas, seña, notas, intake)

Edición con las mismas garantías del motor (F1-27): una sola transacción con la reserva bloqueada, todo o nada, auditada con diff sin datos personales y con el evento booking.updated. Todos los campos son opcionales y null equivale a "no tocar".

  • Permisos. owner, manager y API key con bookings:write: todos los campos. staff: solo notes e intake_data de reservas de SUS recursos (otro campo ⇒ 403 edit_forbidden; reserva ajena ⇒ 404). viewer ⇒ 403. El workspace sale siempre de la credencial.
  • Idempotency-Key. Opcional si el cuerpo trae solo notes/intake_data (compatibilidad con v1); obligatorio para cualquier otro campo (422 idempotency_key_required). Misma clave + mismo cuerpo devuelve la reserva sin volver a ejecutar; con otro cuerpo, 422 idempotency_conflict.
  • Estados terminales (completada, cancelada, ausencia, fallida, vencida): solo notes y datos de contacto; el resto ⇒ 409 booking_not_editable.
  • Servicio, recurso y personas se revalidan con el motor de slots sobre el MISMO horario (grilla, horario del recurso, requisitos, capacidad pooled y EXCLUDE); no se re-aplican lead time, anticipación máxima ni cutoff. Sin lugar ⇒ 409 slot_taken (con reasons) y no queda ningún cambio. Cambiar de servicio conserva al mismo recurso si sigue sirviendo. No se cambia entre servicios con horario y de cola (422 service_mode_mismatch). En cola, el recurso nuevo respeta el tope de tareas activas (409 queue_full; 422 resource_not_eligible si no es del grupo del servicio).
  • Seña. Ya paga: no se admite un cambio de servicio o de personas que altere su monto (409 deposit_locked). Pendiente: rige el monto nuevo y, si pasa a 0, la reserva se confirma. La seña se cobra con POST /payments/manual (cobro unificado, F3-492: forma de pago y auditoría); editar la reserva ya no la marca pagada.
  • Paquete o club (F3-261). Una tarea que usa una unidad de un plan no cambia de cliente ni de servicio (409 booking_covered_by_plan): primero se le quita el plan (POST /prepaid/bookings/{booking_id}/release).
  • Cliente. customer_id reasigna a otro cliente existente del workspace (404 si no existe o es de prueba; las reservas de prueba no cambian de cliente: 422 test_booking_locked). customer corrige nombre, teléfono, email e idioma de la ficha (un teléfono de otro cliente ⇒ 409 customer_phone_taken). La auditoría enmascara teléfono, email y nombre.
  • Revisar dirección (F3-151). destination_review_action: confirm es una declaración explícita del editor autorizado, también si los valores son los mismos. Requiere expected_version (omitida ⇒ 422) y destino con texto más par de coordenadas finito/en rango (422 intake_invalid, reason destination_review_requires_point). Resuelve sólo revisar-direccion, atómicamente y con auditoría, sin validar geocodificación externa; notas, coordenadas o PATCH tags sin esa acción no la resuelven. Se gobierna por la misma autorización de intake_data, incluido staff de SUS recursos y API key bookings:write; no habilita acciones autónomas del LLM. No exige Idempotency-Key por sí sola.
  • Concurrencia. expected_version opcional salvo esa acción: si cambió ⇒ 409 version_conflict.
  • Authentication: API key or dashboard session
  • Scopes: bookings:write

Parameters

NameInRequiredTypeDescription
idpathYesstring
Idempotency-KeyheaderNostringObligatorio salvo que el cuerpo traiga solo notes/intake_data.

Request body (optional)

  • application/json: BookingPatch

Responses

StatusDescription
200Reserva tras la edición (sin cambios si el cuerpo no modifica nada) (Booking)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
410Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)
defaultError (RFC 9457) (Problem)

POST /bookings/{id}/cancel

Cancelar

Cancelar nunca se bloquea (F3-181, decisión del dueño 2026-10-09). Con etapas v2 no exige los datos de la etapa cancelada ni los ítems de la salida «cancelar»; para registrarlos, usar POST /work-items/{id}/transitions/{stage}, donde son opcionales. Cancelar una tarea principal cancela también sus tareas vinculadas que todavía no empezaron (motivo case_cancelled, F3-585); las que están en curso siguen. Para avisar antes, leer pending_ids de GET /bookings/{id}/case. Reprogramar no cancela el caso.

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
idpathYesstring

Request body (optional)

  • application/json: object

Responses

StatusDescription
200OK (Booking)
defaultError (RFC 9457) (Problem)

GET /bookings/{id}/case

Caso de una tarea: sus tareas vinculadas y qué las traba

F3-585 (versión liviana): la tarea que reservó el cliente es el caso y sus tareas vinculadas son tareas normales. Se pide por la principal o por cualquier vinculada y devuelve lo mismo. Una tarea sin vinculadas es un caso vacío (tasks: []). Una vinculada está trabada (blocked) mientras la que la bloquea no se completó. Mismo alcance que GET /bookings/{id}: staff solo pide el caso de una tarea suya (si no, 404), y entonces ve todas las vinculadas del caso.

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
idpathYesstring

Responses

StatusDescription
200OK (Case)
defaultError (RFC 9457) (Problem)

PUT /bookings/{id}/items

Editar los productos del pedido de una tarea (cantidades, agregar, quitar)

Campo «Productos» del formulario (F3-148): reemplazo completo e idempotente de las líneas de intake_data.items. Cada línea del cuerpo es solo {product_id, qty}; nombre, unidad y precio unitario (unit_price_minor, unidad menor de la moneda) los copia el servidor del catálogo AL CREAR la línea y una línea existente los conserva (cambiar el catálogo no altera pedidos). qty_requested (lo pedido) no cambia: una línea agregada después de crear la tarea queda con qty_requested: 0. El servidor recalcula items_total y el total del formulario con la definición cotizada de la tarea.

  • Permisos (matriz única): order:edit (owner, manager; API key con bookings:write) agrega, quita y cambia cantidades en tareas no cerradas (no en cancelled, expired, failed, no_show ⇒ 409 order_not_editable). order:edit_own (staff): solo en tareas asignadas a sus recursos (si no, 404) y en curso (checked_in; si no, 409 order_not_editable con reason: not_in_progress), solo cantidades (incluso 0) y productos que el servicio ofrece, sin quitar líneas (403 order_forbidden), y solo si el dueño encendió staff_can_edit_order (apagado por defecto; si no, 403). viewer ⇒ 403.
  • Catálogo. Una línea nueva exige un producto activo, del negocio y ofrecido por el servicio (422 order_invalid con reason: product_unavailable) y al menos una unidad; sin productos repetidos (duplicate_product); rangos del campo (qty_range, lines_range, units_range); sin campo «Productos» en la tarea ⇒ no_items_field.
  • Valor a cobrar. Si ya estaba congelado (due_minor), el servidor lo ajusta en la MISMA transacción por la diferencia del cálculo (respeta un ajuste manual previo), con motivo «cambio de pedido» y autor = quien editó, auditado (payment.due_adjusted); no exige payments:adjust. Si lo ya cobrado supera el nuevo valor ⇒ 409 order_below_paid (con paid_minor y due_minor) y no cambia nada: lo resuelve un manager. Con API key, leer lo cobrado exige además payments:read_balance.
  • Auditoría booking.order_changed con ids de producto, cantidades y totales (sin datos personales).
  • Concurrencia. expected_version opcional: si la reserva cambió ⇒ 409 version_conflict.

PATCH /bookings/{id} no cambia los ítems (salvo en un hold): un intake_data con otras cantidades ⇒ 422 order_invalid con reason: use_items_endpoint; si no trae items, se conservan los guardados.

  • Authentication: API key or dashboard session
  • Scopes: bookings:write

Parameters

NameInRequiredTypeDescription
idpathYesstring

Request body (required)

  • application/json: OrderItemsIn

Responses

StatusDescription
200La tarea con el pedido actualizado (sin cambios si las líneas son las mismas) (Booking)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)
defaultError (RFC 9457) (Problem)

POST /bookings/{id}/message

Mensaje operativo al cliente de la tarea

F3-115. Resuelve conversación y canal en el servidor y reutiliza el envío manual del Inbox: persiste una salida pending (outbox durable), deja la conversación en modo humano, crea la conversación si no existía y registra booking.message_sent en la línea de tiempo de la tarea (solo el canal, nunca el texto). Es un mensaje OPERATIVO: el opt-out de promociones no lo bloquea; la pausa de canal sí (409 no_message_channel). Idempotency-Key obligatoria (24 h para sesión de panel; una clave con otro texto u opción devuelve 422). Rate limit por usuario (429 rate_limited, por defecto 6 por minuto). Roles owner, manager y staff (solo sus tareas, si no 404); viewer 403. Scope de API key messages:write.

  • Authentication: API key or dashboard session
  • Scopes: messages:write

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
Idempotency-KeyheaderYesstring

Request body (required)

  • application/json: TaskMessageIn

Responses

StatusDescription
201Persistido, pendiente de despacho durable (TaskMessageOut)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
429Error (RFC 9457) (Problem)

GET /bookings/{id}/message-options

Canales por los que escribirle al cliente de la tarea

F3-115. El servidor elige: la conversación vinculada a la tarea (origin), si no la última conversación del cliente, si no un canal nuevo con sus datos de contacto (WhatsApp si tiene teléfono y hay uno conectado, email si tiene email y Resend está activo). default_key es la primera opción sin bloqueo; blocked explica por qué una opción no se puede usar (pausa de canal, conexión caída, cliente de prueba). Sin opciones, unavailable_reason dice por qué. Roles owner, manager y staff (solo tareas asignadas a sus recursos, si no 404); viewer 403. Scope de API key messages:read.

  • Authentication: API key or dashboard session
  • Scopes: messages:read

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)

Responses

StatusDescription
200Opciones de envío (TaskMessageOptions)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)

POST /bookings/{id}/reschedule

Reprogramar (crea una reserva nueva y cancela la anterior)

La pausa del servicio no bloquea reprogramar una tarea existente. Conserva validaciones de horario, recursos y capacidad; si falla, la tarea original queda intacta. No habilita reservas nuevas ni oferta de slots. Con etapas v2 no se bloquea por los datos de la etapa cancelada, porque cancelar nunca se bloquea (F3-181). Staff solo mueve sus turnos y solo a sus propias agendas (403 reschedule_forbidden si no).

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
idpathYesstring
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Request body (required)

  • application/json: object

Responses

StatusDescription
200Nueva reserva (Booking)
defaultError (RFC 9457) (Problem)

POST /bookings/{id}/schedule

Programar o cambiar ventana sin cambiar el ID

Owner/manager o API key bookings:write, CSRF para sesión. Tenant desde credencial. Clave obligatoria, operación+cuerpo con huella durable; replay no incrementa versión. expected_version stale devuelve 409 version_conflict. Tareas terminales y holds técnicos con expires_at devuelven 409 booking_not_editable. Pending scheduled no consume capacidad: schedule valida horario/requisitos/recursos/zona con el motor y sus constraints. Unschedule mantiene estado operativo, pagos/vencimientos y asignación real sin capacidad. Queue conserva contador/is_queue aunque tenga ventana; no ocupa capacidad horaria. Pausar el servicio no bloquea operaciones sobre tareas existentes; se mantienen validaciones de horario, recursos y capacidad.

  • Authentication: API key or dashboard session
  • Scopes: bookings:write

Parameters

NameInRequiredTypeDescription
idpathYesstring
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Request body (required)

  • application/json: ScheduleBookingIn

Responses

StatusDescription
200Tarea actualizada o replay (Booking)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)
defaultError (RFC 9457) (Problem)

POST /bookings/{id}/unschedule

Quitar horario conservando ejecución, pago y asignación

Owner/manager o API key bookings:write, CSRF para sesión. Tenant desde credencial. Clave obligatoria, operación+cuerpo con huella durable; replay no incrementa versión. expected_version stale devuelve 409 version_conflict. Tareas terminales y holds técnicos con expires_at devuelven 409 booking_not_editable. Pending scheduled no consume capacidad: schedule valida horario/requisitos/recursos/zona con el motor y sus constraints. Unschedule mantiene estado operativo, pagos/vencimientos y asignación real sin capacidad. Queue conserva contador/is_queue aunque tenga ventana; no ocupa capacidad horaria.

  • Authentication: API key or dashboard session
  • Scopes: bookings:write

Parameters

NameInRequiredTypeDescription
idpathYesstring
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Request body (required)

  • application/json: UnscheduleBookingIn

Responses

StatusDescription
200Tarea actualizada o replay (Booking)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)
defaultError (RFC 9457) (Problem)

GET /conversations/{id}/work-items

Tareas (reservas) creadas desde una conversación

Las reservas cuyo origen es esta conversación (0, 1 o N), de la más reciente a la más antigua. No incluye holds sin confirmar ni vencidos. Un usuario staff solo ve las de sus recursos. Conversación inexistente, de otro workspace o (rol staff, F2-52) de un cliente sin tareas suyas: 404. API keys: scope bookings:read, solo GET (ADR 0017).

  • Authentication: API key or dashboard session
  • Scopes: bookings:read

Parameters

NameInRequiredTypeDescription
idpathYesstring

Responses

StatusDescription
200OK (object)
defaultError (RFC 9457) (Problem)

GET /task-lines/suggestions

Buscador de «+ Agregar»: catálogo y ítems usados antes

Un solo buscador (decisión 3): productos y servicios activos del catálogo y, después, los ítems libres usados antes en el negocio (source: memory, último precio y, a quien tiene tasks:costs (por defecto el dueño), último costo; suggest_save desde el tercer uso). Permiso tasks:edit_lines; API key bookings:read.

  • Authentication: API key or dashboard session
  • Scopes: bookings:read

Parameters

NameInRequiredTypeDescription
qqueryYesstring

Responses

StatusDescription
200Successful Response (LineSuggestions)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

GET /work-items

Consulta unificada de tareas

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
feesqueryNostring all, due default allFIU-510 · Tareas-cuota de las suscripciones. all (por defecto) las lista todas. due deja solo las por cobrar vencidas o dentro de su aviso (notice_days del plan) y saca las pagadas, las perdonadas y las de cobro automático al día (vuelven si el cobro falla o no llega). Es la vista de Hoy y Trabajo del panel, de Wagy y del MCP (list_tasks). Solo filtra: no cambia montos ni estados.
scheduling_statequeryNopending, scheduled, queue
min_latqueryNonumber / null (null)Inclusive geographic box; supply all four bounds together, minimum <= maximum.
max_latqueryNonumber / null (null)Inclusive geographic box; supply all four bounds together, minimum <= maximum.
min_lngqueryNonumber / null (null)Inclusive geographic box; supply all four bounds together, minimum <= maximum.
max_lngqueryNonumber / null (null)Inclusive geographic box; supply all four bounds together, minimum <= maximum.
statusqueryNostring / null (null)
base_statequeryNostring / null (null)Estado base fijo (ADR 0023, F1-37), el que usan filtros, estadísticas y mapa: pending (held, pending_payment, confirmed), in_progress (checked_in), completed, cancelled (cancelled, expired), absent (no_show), refused (failed). No depende del nombre de la etapa.
stagequeryNostring / null (null)
resource_idqueryNostring / null (null)
customer_idqueryNostring / null (null)
fromqueryNostring / null (null)
toqueryNostring / null (null)
created_fromqueryNostring / null (null)Desde cuándo entró la tarea (created_at, incluido; F3-729). Independiente de from/to, que miran la fecha de trabajo. Con created_from o created_to no entran los holds sin concretar (held, expired), como en el conteo del uso del flujo.
created_toqueryNostring / null (null)Hasta cuándo entró la tarea (created_at, excluido; F3-729). Debe ser posterior a created_from (422).
minequeryNoboolean / null (null)Solo lo asignado a los recursos propios de quien consulta, sin lo que solo podría tomar (F3-729): el alcance own del uso de un flujo, para que el link de un día traiga lo mismo que se contó.
priorityqueryNostring / null (null)
tagqueryNostring / null (null)
typequeryNostring / null (null)
flow_idqueryNostring / null (null)Tareas de ese flujo (F3-33). Con group_by=stage, groups trae todas las etapas activas del flujo en su orden (también las vacías, con count 0), con label y position.
statusesqueryNoarray<BookingStatus> / null (null)Tareas en alguno de esos estados (repetí el parámetro). Se combina con status; los conteos de groups, total y la paginación salen de la misma selección (F3-33).
resource_idsqueryNoarray / null (null)Tareas de alguno de esos recursos (repetí el parámetro), p. ej. «Mías» con varias agendas. Se combina con resource_id; un staff solo alcanza sus recursos (F3-33).
fulfillment_modequeryNostring / null (null)
unassignedqueryNoboolean / null (null)true lista solo el trabajo que nadie tiene (sin recurso asignado): el conjunto "Sin asignar". Un staff ve ahí lo que puede tomar (servicios cuyo grupo incluye alguno de sus recursos); false lista lo ya asignado.
attentionqueryNostring / null (null)Selección compartida con los contadores de Control. Sin fechas implícitas; se combina con los demás filtros mediante AND. Avisos, dirección y cobertura solo incluyen abiertas; sin zona excluye fuera de cobertura; sin asignar excluye holds asignados. Evaluaciones incluye finales.
overlapqueryNoboolean default falseIncluye tareas cuya ventana cruza el rango aunque empiecen antes de from. Sin ventana usa el plazo.
include_carriedqueryNoboolean default falseF3-573 (arrastre): con from/to, suma las tareas iniciadas que pasaron su fin y siguen ocupando su lugar en el rango (servicio de día completo o que no pide una persona, hasta 7 días después de su fin), aunque hayan empezado antes. Vienen con carried_over=true. Misma regla que GET /slots. Las reglas de visibilidad (staff) no cambian. Por defecto false: el listado no cambia.
latequeryNoboolean / null (null)true lista solo las tareas atrasadas (fechadas, en etapa no final, con el fin de la ventana más late_grace_min vencidos); false lista el resto. Combinable con las vistas, p. ej. view=today.
zone_idqueryNostring / null (null)Tareas de esa zona (F1-38).
no_zonequeryNoboolean / null (null)true lista las tareas vivas con destino que ninguna zona cubre (solo si el negocio tiene zonas): lo que aparece en "Necesita tu atención". false las excluye.
originqueryNostring / null (null)
sourcequeryNostring / null (null)
openqueryNoboolean / null (null)true lista solo las tareas abiertas (ni completed, cancelled, expired, no_show ni failed) de cualquier día; false lista solo las terminadas o canceladas. Es lo que usa «Pendientes» de Hoy.
untilqueryNostring / null (null)Solo con open=true: deja las tareas fechadas antes de este instante (exclusivo) y también las que no tienen fecha (por agendar, cola sin plazo). A diferencia de to, no excluye lo que no tiene fecha.
qqueryNostring / null (null)Búsqueda literal sin comodines sobre nombre canónico y traducciones persistidas del servicio, notas, etiquetas e ID de tareas visibles; independiente del idioma del panel.
matchqueryNostring / null (null)FIU-560 · Lo que filtra una vista de Trabajo (criterios v2), como JSON (WorkItemMatch, hasta 4096 caracteres): estado de selección múltiple, fecha con su campo y rango relativo, y grupos «o» (any_of). La fecha relativa se resuelve en la zona horaria del proyecto al pedir la primera página (el cursor conserva ese instante). Se combina con AND con el resto de los parámetros. Inválido: 422.
order_byqueryNostring start, due, priority, created, completed default start
directionqueryNostring asc, desc default asc
group_byqueryNostring / null (null)Conteos por grupo en groups. flow agrupa por flujo (con el nombre del flujo en label).
viewqueryNostring all, inbox, today, upcoming default all
limitqueryNointeger default 25
cursorqueryNostring / null (null)

Responses

StatusDescription
200Successful Response (WorkItemPage)
422Error (RFC 9457) (Problem)
defaultError (RFC 9457) (Problem)

GET /work-items/{id}

Tarea y próximos pasos autorizados

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
idpathYesstring

Responses

StatusDescription
200OK (Booking)
defaultError (RFC 9457) (Problem)

PATCH /work-items/{id}

Editar metadata de tarea

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)

Request body (required)

  • application/json: WorkItemPatch

Responses

StatusDescription
200Successful Response (Booking)
422Error (RFC 9457) (Problem)
defaultError (RFC 9457) (Problem)

GET /work-items/{id}/journey

Recorrido completo de una tarea con sus versiones históricas

Solo equipo autorizado (tasks:read y alcance de tareas, incluido historial terminado); API key bookings:read. Eventos cronológicos sin el límite de 100 del stage-history, y mapas v2 de las versiones registradas. Nunca sustituye una versión ausente por la vigente. Sin mutaciones ni acceso mediante enlaces de cliente.

  • Authentication: API key or dashboard session
  • Scopes: tasks:read, bookings:read

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)

Responses

StatusDescription
200Eventos y versiones del flujo de esta tarea, bajo RLS del proyecto (WorkItemJourney)
401Error (RFC 9457) (Problem)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

GET /work-items/{id}/lines

Ítems de la tarea, bloques del presupuesto y margen

Permiso tasks:edit_lines (el viewer y el colaborador no ven dinero; staff: solo tareas asignadas a sus recursos, si no 404); API key bookings:read. margin y cost_minor solo para quien tiene tasks:costs (FIU-597: por defecto solo el dueño; API key con payments:read_balance); los demás ven solo el costo de las líneas que cargaron. awaiting_approval_minor: lo que no se cobra hasta que el cliente apruebe el presupuesto actualizado.

  • Authentication: API key or dashboard session
  • Scopes: bookings:read

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)

Responses

StatusDescription
200Successful Response (TaskLinesOut)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

POST /work-items/{id}/lines

Agregar un ítem a la tarea (del catálogo o libre)

Del catálogo (product_id o service_id): nombre y precio se copian del catálogo. Libre (name, kind, unit_price_minor): sin producto oculto; save_to_catalog lo guarda como producto o servicio. Sube el valor a cobrar exactamente el subtotal de la línea. Idempotente por tarea; auditado.

FIU-472 (ADR 0040 §3, decisiones 1A a 5A). Exactamente uno de product_id, service_id (catálogo: nombre y precio copiados, un precio enviado se ignora) o name (ítem libre: kind y unit_price_minor obligatorios; sin producto oculto). cost_minor opcional: el cliente nunca lo ve y no entra en Dinero (solo el margen). save_to_catalog: crea el producto (o el servicio, como el de la tarea) sin que el bot lo ofrezca; exige products:write/catalog:write (403 line_forbidden, reason: catalog_forbidden). Sube el valor a cobrar exactamente el subtotal de la línea (motivo «ítems de la tarea»); con un presupuesto aceptado, nace el actualizado para aprobar. Permiso tasks:edit_lines; API key bookings:write. Idempotente por tarea (Idempotency-Key; otro cuerpo con la misma clave: 422 idempotency_conflict; un reintento responde 200 con replay: true). Auditada (booking.line_added). Errores: 404; 409 line_not_editable (reason: status, completed, covered_by_plan, due_negative), version_conflict; 422 line_invalid (reason: product_unavailable, service_unavailable, lines_range, catalog_duplicate, catalog_service_needs_team).

  • Authentication: API key or dashboard session
  • Scopes: products:write, catalog:write, bookings:write

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Request body (required)

  • application/json: LineAddIn

Responses

StatusDescription
201Successful Response (TaskLinesOut)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

PATCH /work-items/{id}/lines/{line_id}

Editar un ítem de la tarea

De una línea del catálogo cambian la cantidad y el costo (otro campo: 422 line_invalid, reason: catalog_line_fixed); de una libre, también nombre, tipo, unidad y precio. cost_minor: null borra el costo. Mueve el valor a cobrar la diferencia; si lo ya cobrado quedaría por encima, 409 order_below_paid y nada cambia. Staff: solo las líneas que cargó (403 line_forbidden, reason: not_own_line). Idempotente; auditada (booking.line_changed).

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
line_idpathYesstring (uuid)
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Request body (required)

  • application/json: LineEditIn

Responses

StatusDescription
200Successful Response (TaskLinesOut)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

DELETE /work-items/{id}/lines/{line_id}

Quitar un ítem de la tarea

Baja el valor a cobrar el subtotal de la línea; si lo ya cobrado quedaría por encima, 409.

Baja el valor a cobrar el subtotal de la línea; si lo ya cobrado quedaría por encima, 409 order_below_paid (paid_minor, due_minor) y nada cambia. Idempotente; auditada (booking.line_removed).

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
line_idpathYesstring (uuid)
expected_versionqueryNointeger / null (null)
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Responses

StatusDescription
200Successful Response (TaskLinesOut)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

POST /work-items/{id}/offers/{key}

Aceptar una oferta de la etapa actual («Ofrecer…»)

F3-461 (ADR 0040 §3). Acepta el ítem offer key de la etapa actual de la tarea (visible al equipo). product: suma la línea a esta tarea con nombre, unidad y precio copiados del catálogo (un precio enviado se ignora) y sube su valor a cobrar (ajuste con motivo «oferta aceptada»; en una tarea con «Productos» en el formulario, el mismo recálculo que PUT /bookings/{id}/items). service: crea otra tarea independiente de ese servicio para el mismo cliente (origin.type: offer, origin.booking_id); si el servicio tiene horario, nace por agendar (pending_schedule) y se agenda con POST /bookings/{id}/schedule. plan (F3-463): vende el plan al cliente de la tarea con la venta del panel (además prepaid:sell; sin él, 403 offer_forbidden con reason: prepaid_sell): nace la suscripción con su cuota 1, el alta, Por cobrar (created_work_item, origin.type: offer); un plan que ya no se puede vender da 422 offer_item_unavailable con el motivo de prepaid. Sin cascadas: cancelar una de las dos tareas no toca la otra. Idempotente por tarea con Idempotency-Key (otro cuerpo con la misma clave: 422 idempotency_conflict). Auditada (booking.offer_accepted, con la etapa y la clave del ítem). Permiso tasks:accept_offer (owner, manager y staff en tareas asignadas a sus recursos, solo con «El repartidor puede modificar el pedido» encendido: si no, 403 offer_forbidden); API key con bookings:write. Un servicio con ficha obligatoria pide intake_data (si falta, 422 invalid_intake). Errores: 404 offer_not_found; 409 offer_not_available (reason: status, kind, covered_by_plan); 422 offer_item_unavailable (reason: item_required, not_offered, qty_range, intake_not_allowed, qty_not_allowed).

  • Authentication: API key or dashboard session
  • Scopes: bookings:write

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
keypathYesstring
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Request body (required)

  • application/json: OfferAcceptIn

Responses

StatusDescription
200La tarea (con lo sumado), la tarea que nació (si el ítem ofrece un servicio o un plan) y el registro (OfferAcceptResult)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

GET /work-items/{id}/quote

Presupuesto de la tarea: lo de ahora y sus revisiones

Los bloques de ahora (Mano de obra / Servicios y Materiales / Productos, con subtotales que suman el valor a cobrar) y las revisiones del presupuesto. Permiso tasks:edit_lines; API key bookings:read.

  • Authentication: API key or dashboard session
  • Scopes: bookings:read

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)

Responses

StatusDescription
200Successful Response (QuotesOut)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

POST /work-items/{id}/quote

Mandar el presupuesto al cliente

El cliente lo recibe por su canal, en dos bloques con subtotales. Queda esperando su respuesta.

Decisión 5A. Crea una revisión pending con los bloques de ahora (sin costos) y la manda al cliente por su canal (aviso transaccional task_quote, que se relee antes de salir). La pendiente anterior queda superseded. Idempotente con Idempotency-Key (200 replay: true). 409 quote_not_sendable (reason: status, internal_task). Permiso tasks:edit_lines; API key bookings:write. Auditada (booking.quote_sent).

  • Authentication: API key or dashboard session
  • Scopes: bookings:write

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Responses

StatusDescription
201Successful Response (QuoteResult)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

POST /work-items/{id}/quote/{revision}/decision

Registrar la respuesta del cliente al presupuesto

Aprobado: lo aprobado pasa a ser este total y la diferencia ya se puede cobrar. Rechazado: la diferencia sigue sin cobrarse hasta que el equipo corrija y el cliente apruebe otra revisión.

Registra la respuesta del cliente a la revisión pendiente (via: customer_channel, in_person, phone). Aprobada: su total pasa a ser lo aprobado y la diferencia ya se cobra; mientras haya una revisión pendiente después de una aprobada, la parte del valor a cobrar por encima de lo aprobado no sale por link, QR, Point ni tarjeta, ni se imputa un cobro manual (409 payment.quote_pending); rechazar la revisión tampoco libera la diferencia. Repetir la misma respuesta no cambia nada; 409 quote_not_pending si la revisión ya no espera respuesta. Auditada (booking.quote_accepted / booking.quote_rejected).

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
revisionpathYesinteger

Request body (required)

  • application/json: QuoteDecisionIn

Responses

StatusDescription
200Successful Response (QuoteResult)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

POST /work-items/{id}/reopen

Reabrir una tarea completada, cancelada o ausente

Permiso tasks:reopen (owner/manager); API key con bookings:write y creador que siga siendo miembro activo owner/manager con tasks:reopen en cada llamada y replay (también MCP); degradado o sin membership responde 403. Motivo obligatorio. Restaura la última etapa activa anterior o su canónica; conserva cobros y seña retenida; no_show no es deuda, al reabrir vuelve a la clasificación de deuda según estado activo/fecha. Recupera capacidad en la misma transacción (exclusive, pooled, queue). Si ya no hay lugar responde 409 reopen_requires_reschedule: elegir un nuevo start y, opcionalmente, resource_ids y confirmar nuevamente con otra clave; se conserva la misma tarea y sus cobros. Una cancelada con reemplazo por reprogramación no se puede reabrir (409 invalid_transition). Idempotency-Key obligatorio: replay idéntico, otra operación con la misma clave da 409. Historial y auditoría incluyen motivo; workitem.reopened no incluye texto libre. No ejecuta efectos de cierre ni notificaciones de etapa.

  • Authentication: API key or dashboard session
  • Scopes: bookings:write

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Request body (required)

  • application/json: ReopenIn

Responses

StatusDescription
200Tarea reabierta (o respuesta original en replay) (WorkItemActionResult)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

GET /work-items/{id}/stage-history

Historial inmutable de etapas

  • Authentication: API key or dashboard session
  • Scopes: None

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
orderqueryNostring asc, desc default descOrden de los últimos 100 eventos; asc los presenta cronológicamente.

Responses

StatusDescription
200Últimos 100 eventos con actor, origen, clase e ítems históricos; orden elegido (object)
404Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

POST /work-items/{id}/tools/{tool}

Usar herramienta de la etapa actual

F3-183. Lock, idempotencia, autorización y requisitos. Mutación, cobro, historial y outbox atómicos. Scope bookings:write; payments:record si cobra. Falta de requisitos: 422 stage_requirements_missing. Un dato que vino pero no vale (tipo, formato, rango, opción, campo calculado o ya cargado): 422 stage_field_invalid con invalid (F3-482). No ejecuta charge electrónico (F4-29 pendiente).

  • Authentication: API key or dashboard session
  • Scopes: bookings:write

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
toolpathYesstring
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Request body (required)

  • application/json: WorkItemOperationInput

Responses

StatusDescription
200La tarea tras los efectos de servidor y los efectos de cliente (WorkItemActionResult)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

POST /work-items/{id}/transitions/{to_stage}

Entrar a una etapa del servicio

F3-183. Lock, idempotencia, autorización y requisitos. Mutación, cobro, historial y outbox atómicos. Scope bookings:write; payments:record si cobra. Falta de requisitos: 422 stage_requirements_missing. Un dato que vino pero no vale (tipo, formato, rango, opción, campo calculado o ya cargado): 422 stage_field_invalid con invalid (F3-482). No ejecuta charge electrónico (F4-29 pendiente).

  • Authentication: API key or dashboard session
  • Scopes: bookings:write

Parameters

NameInRequiredTypeDescription
idpathYesstring (uuid)
to_stagepathYesstring
Idempotency-KeyheaderYesstringUUID único por operación; reintentos con la misma key devuelven el mismo resultado durante 24 h.

Request body (required)

  • application/json: WorkItemOperationInput

Responses

StatusDescription
200La tarea tras los efectos de servidor y los efectos de cliente (WorkItemActionResult)
403Error (RFC 9457) (Problem)
404Error (RFC 9457) (Problem)
409Error (RFC 9457) (Problem)
422Error (RFC 9457) (Problem)

GET /work-items/stream

Avisos en vivo de tareas (SSE)

text/event-stream del proyecto. Eventos work_item.created, work_item.updated (etapa, estado, asignación, horario, pago u otro dato) y work_item.deleted con {event, id}, sin contenido: el cliente vuelve a pedir GET /v1/work-items. Se publican después del commit. Un comentario : cada 15 s mantiene la conexión. Mismos roles que GET /v1/work-items (API key: bookings:read; Mini App: header X-Miniapp-Session). Un cobro, su anulación o una devolución avisan work_item.updated de las tareas que pagan. Rol staff sin gestión: solo recibe avisos de las tareas que ve en ese momento o que ya vio en este stream (al abrirlo o por un aviso anterior); cuando deja de verla (reasignada, tomada por otro) o se borra, recibe un aviso con ese id y nunca el id de una tarea que no pudo ver, tampoco en las bajas. El stream se corta si la sesión se cierra o cambia el rol.

  • Authentication: API key or dashboard session
  • Scopes: bookings:read

Responses

StatusDescription
200Stream abierto
403Error (RFC 9457) (Problem)
defaultError (RFC 9457) (Problem)