Docs

API: pedidos y pagos

Listar pedidos, cambiarles el estado, asignar repartidor y cotizar el envío; ver pagos, aprobarlos o rechazarlos, registrar cobros y reportar movimientos del banco.

Revisado el 28 de octubre de 2026

El pedido

Un pedido nace cuando el cliente lo cierra en WhatsApp o cuando alguien lo crea en el panel. Mientras el bot lo va armando es un carrito (draft) y la API no lo enseña: no aparece en las listas, GET /orders/{id} responde 404 y no dispara webhooks. La única excepción es el carrito de delivery que espera que el negocio le ponga el costo del envío: ese ya se ve (ver Cotizar el envío).

json
{
  "id": "5b7c2e1a-3f4d-4c8e-9a6b-1d2e3f4a1b2c",
  "object": "order",
  "number": "4A1B2C",
  "status": "confirmed",
  "currency": "USD",
  "subtotal": "18.00",
  "delivery_fee": "2.00",
  "loyalty_discount": "0.00",
  "pay_method": "Pago móvil",
  "pay_adjustment": "0.00",
  "collect_on_delivery": null,
  "total": "20.00",
  "fully_paid": true,
  "amount_paid": "20.00",
  "balance_due": "0.00",
  "exchange_rate": "36.5000",
  "is_credit": false,
  "fulfillment": "delivery",
  "delivery": {
    "address": "Av. Principal de Los Palos Grandes, casa 12",
    "lat": 10.4969,
    "lng": -66.8446,
    "km": "3.20",
    "eta_minutes": null,
    "phone": null,
    "assigned_to": null,
    "assigned_to_id": null
  },
  "note": "Llamar al llegar",
  "customer": {
    "id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
    "name": "María Pérez",
    "phone": "584140000000"
  },
  "conversation_id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
  "items": [
    {
      "name": "Hamburguesa clásica",
      "quantity": 2,
      "unit_price": "7.00",
      "notes": "sin cebolla",
      "product_id": "e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8a9b",
      "external_id": "SKU-0101",
      "barcode": "7591234500017",
      "charge": null,
      "options": [
        { "group": "Contorno", "name": "Papas fritas", "price": 0, "group_external_id": "GRP-CONT", "external_id": "OPT-PAPAS" }
      ],
      "variant": null
    },
    {
      "name": "Queso blanco (kg)",
      "quantity": 0.5,
      "unit_price": "8.00",
      "notes": null,
      "product_id": "f6a7b8c9-d0e1-4f2a-9b3c-5d6e7f8a9b0c",
      "external_id": "SKU-0230",
      "barcode": null,
      "charge": null,
      "options": [],
      "variant": null
    },
    {
      "name": "Envases",
      "quantity": 2,
      "unit_price": "0.50",
      "notes": null,
      "product_id": null,
      "external_id": null,
      "barcode": null,
      "charge": "packaging",
      "options": [],
      "variant": null
    }
  ],
  "payments": [
    {
      "id": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
      "method": "pago_movil",
      "method_label": "Pago móvil",
      "status": "approved",
      "amount": "730.00",
      "currency": "VES",
      "amount_ves": "730.00",
      "reference": "012345",
      "group_id": null,
      "received_at": "2026-09-29T18:01:05.000Z",
      "resolved_by": "system",
      "created_at": "2026-09-29T18:00:51.907Z"
    }
  ],
  "created_at": "2026-09-29T17:58:12.412Z",
  "updated_at": "2026-09-29T18:03:40.118Z"
}
CampoTipoQué es
iduuidId del pedido.
objecttextoSiempre order.
numbertextoEl número corto que ven el cliente y el panel (#4A1B2C): los 6 últimos caracteres del id, en mayúsculas. No es único garantizado; para identificar un pedido usa el id.
statustextoVer estados.
currencytextoMoneda del pedido: USD, EUR o VES.
subtotalimporteSuma de los productos.
delivery_feeimporteCosto del envío ("0.00" si no hay).
loyalty_discountimporteLo que rebajó el premio de la tarjeta de sellos ("0.00" si el pedido no lleva premio). Ya va restado de total.
pay_methodtexto | nullEl método de pago del negocio con que paga el cliente, por su nombre tal como está en métodos de pago (Pago móvil, Zelle…). Se fija cuando el cliente lo dice en el chat, cuando llega su comprobante o cuando tu equipo lo elige en el pedido. null = todavía no se sabe.
pay_adjustmentimporteEl descuento (negativo, "-1.00") o recargo (positivo) que ese método tiene configurado, ya sumado a total. "0.00" si el método no tiene ajuste o aún no se sabe. Deja de moverse en cuanto el pedido tiene un pago aprobado.
collect_on_deliverytexto | nullResto contra entrega: el método en persona (punto de venta, biopago, efectivo) con que se cobra lo que falta al entregar o retirar el pedido. Con un abono aprobado, el pedido sale confirmado y balance_due es lo que se cobra en la entrega. null = no aplica.
totalimporteLo que paga el cliente: subtotal + delivery_fee − loyalty_discount + pay_adjustment.
fully_paidbooleanoSi sus pagos aprobados ya cubren el total. Es la misma cuenta que pasa el pedido a confirmed; un pedido a crédito puede estar confirmed con fully_paid en false.
amount_paidimporteLo que llevan aprobado sus pagos, en la moneda del pedido y sin pasar del total. Los pagos en bolívares se convierten con la tasa del pedido; en un pedido a crédito en dólares o euros, cada uno con la tasa del día en que entró.
balance_dueimporteLo que falta por pagar, en la moneda del pedido. 0.00 si ya está pagado.
exchange_rateimporte | nullBolívares por unidad de la moneda del pedido que quedaron guardados en él, con 4 decimales. Un cambio de tasa posterior no lo mueve.
is_creditbooleanotrue si el pedido va a crédito (a la cuenta del cliente).
fulfillmenttexto | nulldelivery (envío), pickup (retiro en tienda) o null si no se eligió.
delivery.addresstexto | nullDirección de entrega.
delivery.latnúmero | nullLatitud del punto de entrega, si el cliente compartió ubicación. Igual con lng.
delivery.kmimporte | nullDistancia desde el negocio, en km.
delivery.eta_minutesentero | nullMinutos estimados que se le dijeron al cliente al salir.
delivery.phonetexto | nullOtro teléfono para la entrega (el portero, un familiar). null = el del cliente.
delivery.assigned_totexto | nullNombre del repartidor asignado.
delivery.assigned_to_iduuid | nullId del repartidor asignado (el mismo de GET /team).
notetexto | nullNota del pedido.
customerobjetoResumen del cliente: id, name (puede ser null) y phone.
conversation_iduuid | nullEl chat del que salió.
items[]listaUna línea por producto: name, quantity (número), unit_price (importe) y notes («sin cebolla»), más qué producto del catálogo era: product_id (su id en AtiendeFácil), external_id y barcode. quantity es entero salvo en los productos que se venden fraccionados (fractional), donde puede llevar hasta 3 decimales (0.5). El nombre, el precio y los tres datos del producto son los del momento en que se anotó la línea: si el producto se renombra o se borra después, la línea no cambia. Los tres son null si la línea no casó con ningún producto del catálogo. En los pedidos anteriores a que existieran estos campos, product_id y external_id se rellenaron por nombre cuando el producto seguía en el catálogo, y barcode viene null. charge dice si la línea la puso el sistema: packaging (envases) o delivery_surcharge (recargo por delivery), que solo aparecen en el delivery y van sin producto; null = un producto pedido. options trae lo que el cliente eligió de sus grupos de opciones, [{ group, name, price, group_external_id, external_id }], con el precio de cada una ya sumado en unit_price; [] = ninguna. Los dos external_id son los que tenían el grupo y la opción en el catálogo al pedir (null si no tenían). variant es el tamaño pedido, { name, price, external_id }, o null si el producto no tiene tamaños. Las líneas anteriores al 26 de octubre de 2026 traen null en estos campos nuevos.
payments[]listaResumen de cada pago del pedido: id, method, method_label, status, amount, currency, amount_ves, reference, group_id, received_at, resolved_by y created_at, con el mismo significado que en el pago. El detalle completo, en GET /payments/{id}.
created_atfechaCuándo se creó.
updated_atfechaÚltimo cambio.

Estados

statusEn el panelQué significa
pending_paymentEsperando pagoEl cliente cerró el pedido y falta que pague (o que pague el resto).
pending_reviewRevisar pagoLlegó un comprobante que el sistema no pudo confirmar solo: alguien tiene que revisarlo.
confirmedConfirmadoPagado (o a crédito) y aceptado.
preparingPreparandoEn preparación.
readyListoListo para entregar o para que lo retiren.
out_for_deliveryEn caminoSalió con el repartidor.
completedEntregadoEntregado. Es final.
cancelledCanceladoCancelado. Es final.

Listar y ver pedidos

GET/orders

Del más nuevo al más viejo, paginado. Filtros (todos opcionales, se combinan):

ParámetroQué filtra
statusUn estado de la tabla de arriba. Otro valor: 400 invalid_status.
customer_idLos pedidos de un cliente. Si no es un uuid: 400 invalid_id.
created_afterCreados en esa fecha o después (ISO 8601). Mal escrita: 400 invalid_date.
created_beforeCreados en esa fecha o antes.
fulfillmentdelivery (con envío) o pickup (retiro en tienda). Otro valor se ignora.
qBusca por el cliente (nombre, sin importar acentos, o teléfono) o por el número del pedido.
activetrue: solo los que siguen en curso (ni entregados ni cancelados).
is_credittrue (solo los que van a crédito) o false (solo los de contado).
delivery_user_idLos asignados a ese repartidor. Si no es un uuid: 400 invalid_id.
updated_afterLos que cambiaron en esa fecha o después, del cambio más viejo al más nuevo. Ver sincronización incremental.
page, limitVer paginación.
curl
curl "https://api.atiendefacil.com/api/v1/orders?status=pending_review&limit=50" \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI"
GET/orders/{id}

Un pedido. 404 not_found si no existe, es de otro negocio o es un carrito (salvo el que espera la cotización del envío, que sí sale, en draft).

Crear un pedido

POST/orders

Carga un pedido que no entró por WhatsApp (el del teléfono, el del mostrador, el de tu punto de venta). Hace lo mismo que Nuevo pedido en el panel y la app: aparta existencias, avisa al dueño y queda en pending_payment. Responde 201 con el pedido.

CampoObligatorioQué es
customer_id / customer_phoneUno de los dosEl cliente. Con customer_phone, si no existe se crea; customer_name es opcional.
itemsSíDe 1 a 50 líneas { product_id, variant?, options?, quantity, notes? }. El product_id es el id del producto (ver catálogo); el precio es el de hoy, no se manda. Si el producto tiene tamaños, variant es el nombre exacto de uno («Familiar»): sin él responde 400 con los tamaños que hay. options es lo elegido de sus grupos, [{ group?, name }]: se exigen los obligatorios, no más del máximo de cada grupo, y cada opción suma su precio.
fulfillmentSípickup o delivery. Tiene que ser una modalidad que el negocio ofrezca.
delivery_addressEn deliveryDirección escrita.
delivery_feeNoCosto del envío, en la moneda base. Por defecto 0; aquí no se calcula solo.
noteNoNota del pedido.
notify_customerNoPor defecto true: al cliente le llega el pedido con el total y cómo pagar. En el canal oficial, fuera de la ventana de 24 h sale dentro de una plantilla; si no se le puede escribir, el pedido se crea igual.
curl
curl -X POST https://api.atiendefacil.com/api/v1/orders \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_phone": "04140000000",
    "customer_name": "María González",
    "items": [
      { "product_id": "0c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f", "quantity": 2, "notes": "Sin cebolla",
        "options": [{ "group": "Contorno", "name": "Papas fritas" }] },
      { "product_id": "1d2e3f4a-5b6c-4d7e-8f9a-0b1c2d3e4f5a", "variant": "Familiar", "quantity": 1 }
    ],
    "fulfillment": "delivery",
    "delivery_address": "Av. Bolívar, edificio Roma, piso 2",
    "delivery_fee": 3,
    "notify_customer": true
  }'

Errores: 400 invalid_body (falta algo o tiene mal formato), 400 order_rejected (un producto no existe, no está disponible, no hay existencias o la modalidad no se ofrece; el mensaje dice cuál) y 404 not_found (ese customer_id no existe).

Cambiar el estado de un pedido

POST/orders/{id}/status

Hace lo mismo que los botones de estado del panel y de la app. Cuerpo:

CampoObligatorioQué es
statusSíUno de confirmed, preparing, ready, out_for_delivery, completed, cancelled.
eta_minutesNoEntero de 1 a 300. Solo se usa con out_for_delivery: se guarda en el pedido y va en el aviso al cliente («Tiempo estimado de entrega: 25 min»). Con otro estado se ignora.
delivery_user_idNoId del repartidor que lleva el pedido. Solo va con out_for_delivery (con otro estado: 400 invalid_body). Tiene que ser alguien del equipo con rol de repartidor y activo; si no, 400 delivery_user_invalid. Los ids salen de GET /team.
curl
curl -X POST https://api.atiendefacil.com/api/v1/orders/5b7c2e1a-3f4d-4c8e-9a6b-1d2e3f4a1b2c/status \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{ "status": "out_for_delivery", "eta_minutes": 25 }'

Asignar el repartidor

Como en el panel, el repartidor se asigna al mandar el pedido en camino: añade delivery_user_id al pasar a out_for_delivery. Si no lo mandas, se queda el que ya tuviera el pedido. El repartidor ve el pedido en «Mis entregas» de su app y, conectado por QR, le llega el aviso por WhatsApp.

curl
# Mandarlo en camino con un repartidor (su id sale de GET /team)
curl -X POST https://api.atiendefacil.com/api/v1/orders/5b7c2e1a-3f4d-4c8e-9a6b-1d2e3f4a1b2c/status \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "out_for_delivery",
    "eta_minutes": 25,
    "delivery_user_id": "b3c4d5e6-f7a8-4b9c-8d0e-1f2a3b4c5d6e"
  }'

Sale order.status_changed y, si el repartidor es nuevo, order.assigned (con assigned_to y previous_assigned_to). Cambiar el repartidor de un pedido que ya va en camino no se puede por la API: se hace desde el panel o la app.

Responde 200 con el pedido ya actualizado. Mira su status: al confirmar con un pago parcial, el pedido se queda en pending_payment (ver abajo).

Transiciones permitidas

Desde cada estado solo se puede ir a algunos. Lo demás responde 409 invalid_transition. Ojo: de confirmed o preparing sí se puede saltar a completed, pero es un cierre interno y al cliente no se le avisa. Para que reciba «¡Pedido entregado!», pasa antes por ready (retiro en tienda) o out_for_delivery (envío).

DesdePuede pasar a
pending_paymentconfirmedcancelled
pending_reviewconfirmedcancelled
confirmedpreparingreadyout_for_deliverycompletedcancelled
preparingreadyout_for_deliverycompletedcancelled
readyout_for_deliverycompletedcancelled
out_for_deliverycompletedcancelled
completedNinguno: es un estado final.
cancelledNinguno: es un estado final.

Qué pasa al cambiarlo

AEfectos
confirmedSi el pedido esperaba pago, aprueba todos sus comprobantes pendientes o por revisar (quedan firmados por tu clave). Si con eso lo pagado cubre el total, el pedido se confirma y al cliente le llega «¡Pago confirmado! Tu pedido fue aceptado…». Si no lo cubre, el pedido sigue en pending_payment y al cliente se le cobra el resto. Sin ningún pago que aprobar y sin el total cubierto: 409 no_payment (el mensaje dice cuánto falta) y no se toca nada.
preparingAviso al cliente: «Estamos preparando tu pedido».
readyAviso al cliente: listo para retirar en la tienda (retiro) o listo (envío).
out_for_deliveryAviso «Tu pedido va en camino», con los minutos si mandaste eta_minutes. En pedidos con envío, además: el enlace de seguimiento si el negocio lo tiene encendido, lo que falte por pagar contra entrega, y el aviso por WhatsApp al repartidor asignado (en el canal oficial, fuera de las 24 horas sale como plantilla si está aprobada). El repartidor es el de delivery_user_id o, si no lo mandas, el que ya tenía.
completedDesde ready u out_for_delivery, aviso al cliente: «¡Pedido entregado!». Desde confirmed o preparing, sin aviso.
cancelledAviso al cliente: «Tu pedido fue cancelado». Si esperaba pago, el comprobante pendiente se rechaza.

En todos los casos, además:

  • Se imprime la comanda o el ticket si el negocio lo tiene configurado para ese estado (ver Impresión de comandas).
  • El panel y la app lo muestran al instante, y sale el webhook order.status_changed (más order.assigned si cambió el repartidor).
  • Queda en la auditoría: «cambió el pedido a «preparing» desde la API».
  • El aviso al cliente sale por el WhatsApp del negocio y queda en su chat. En el canal oficial, si han pasado más de 24 horas desde su último mensaje, sale dentro de la plantilla de avisos al cliente («Tienes una novedad de (negocio): …»), que Meta cobra a la cuenta de WhatsApp Business del negocio. Si esa plantilla aún no está aprobada, el estado cambia igual, pero el aviso queda en el chat marcado como no enviado. La respuesta de la API no lo dice.

Errores

RespuestaCuándo
400 invalid_bodyFalta status, trae un valor no permitido, eta_minutes está fuera de 1–300 o mandaste delivery_user_id con un estado que no es out_for_delivery.
400 delivery_user_invalidEl delivery_user_id no es un repartidor activo del negocio.
404 not_foundEl pedido no existe, es de otro negocio o es un carrito.
409 invalid_transitionEl pedido no puede pasar de su estado actual al que pides.
409 no_paymentPediste confirmed, el pedido espera pago y no hay nada que aprobar.

Reimprimir un pedido

POST/orders/{id}/print

Manda el pedido otra vez a las impresoras del negocio, como el botón de reimprimir del panel. Sale marcado como copia. Cuerpo opcional { "kind": "kitchen" }: kitchen (la comanda, por defecto) o customer (el ticket del cliente). Responde { "object": "print_request", "order_id", "kind", "printers" }, donde printers es a cuántas impresoras se mandó.

Errores: 404 not_found (el pedido) y 409 no_printers (el negocio no tiene ninguna impresora encendida para ese tipo de ticket; ver Impresión de comandas). La impresión normal, la de cuando el pedido entra, sigue siendo automática: esto es solo para pedir una copia.

Cotizar el envío

Si el negocio cotiza el delivery a mano (ver Delivery y repartidores), cuando un cliente pide con envío el bot le dice que están calculando el costo y espera. En ese momento sale el webhook order.delivery_quote_needed: su data.object es el pedido, todavía en draft y sin costo de envío, y data.conversation_id es el chat del cliente.

POST/orders/{id}/delivery-quote

Le pone el costo del envío. Cuerpo: { fee }, un número de 0 en adelante en la moneda del negocio (base_currency de GET /me); se redondea a dos decimales.

curl
curl -X POST https://api.atiendefacil.com/api/v1/orders/5b7c2e1a-3f4d-4c8e-9a6b-1d2e3f4a1b2c/delivery-quote \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{ "fee": 2.5 }'
Si el pedido…Qué pasa
Esperaba la cotizaciónHace lo mismo que cotizar desde el panel: fija el envío, coloca el pedido (pasa a pending_payment, y sale order.created), le manda al cliente el total con los datos de pago y el bot vuelve a atenderlo.
Ya tenía precio y espera el pago (pending_payment o pending_review)Corrige el envío: recalcula el total, deja el pedido en pending_payment, rechaza los comprobantes pendientes (ya no cuadran con el total nuevo) y le manda al cliente el importe nuevo.
Es un carrito que el cliente todavía está armando (no espera cotización)409 not_quotable. No se toca nada.
No es de delivery, o ya está pagado o en marcha409 not_quotable. No se toca nada.

Responde 200 con el pedido actualizado. Otros errores: 400 invalid_body (falta fee o es negativo) y 404 not_found. Mientras la cotización está pendiente, la conversación trae awaiting_delivery_quote: true y no se puede devolver al bot con /resume: se resuelve cotizando.

El pago

Un pago es un comprobante del cliente (la foto del pago móvil o la transferencia que mandó por WhatsApp), un cobro registrado a mano o por la API, o el pago de una cita (la seña) o de un viaje. Va enlazado a uno de los tres: order_id, appointment_id o ride_id.

json
{
  "id": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
  "object": "payment",
  "status": "approved",
  "method": "pago_movil",
  "method_label": "Pago móvil",
  "amount": "730.00",
  "currency": "VES",
  "amount_ves": "730.00",
  "reference": "012345",
  "group_id": null,
  "received_at": "2026-09-29T18:01:05.000Z",
  "order_id": "5b7c2e1a-3f4d-4c8e-9a6b-1d2e3f4a1b2c",
  "appointment_id": null,
  "ride_id": null,
  "conversation_id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
  "customer": {
    "id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
    "name": "María Pérez",
    "phone": "584140000000"
  },
  "resolved_at": "2026-09-29T18:03:40.101Z",
  "resolved_by": "system",
  "created_at": "2026-09-29T18:00:51.907Z",
  "updated_at": "2026-09-29T18:03:40.118Z"
}
CampoTipoQué es
iduuidId del pago.
objecttextoSiempre payment.
statustextoVer estados.
methodtextopago_movil (pago móvil), transferencia (transferencia), binance (binance), otro (otro método), cash (efectivo). otro es un método digital que no es banco venezolano ni Binance (Zelle, PayPal, Zinli…): su nombre va en method_label. Trata cualquier valor que no conozcas como un método más: la lista puede crecer.
method_labeltexto | nullEl nombre del método del negocio con que pagó el cliente, tal como lo tiene puesto el negocio («Zelle», «Pago móvil»). null en los pagos de antes y cuando no se supo.
amountimporte | nullEl monto, en su moneda. null si el comprobante no lo trae (hay que darlo al aprobar).
currencytextoMoneda del monto.
amount_vesimporte | nullLo que vale en bolívares, calculado al aprobarlo. Si va en la misma moneda que su pedido, con la tasa del pedido (así un pago exacto lo salda); si va en otra moneda, con la tasa del día en que llegó el dinero. Es lo que suma contra el pedido. En un pedido a crédito en dólares o euros todo va con la tasa del día en que llegó el dinero: el fiado se salda a la tasa del día del pago.
referencetexto | nullReferencia bancaria (o la que mandaste al registrar el cobro).
group_idtexto | nullLos pagos que salieron del mismo dinero comparten este valor: un cobro con varios métodos, o un pago repartido entre varios pedidos del cliente (en el panel, la insignia «Pago repartido»). null si el pago va solo.
received_atfecha | nullCuándo llegó el dinero de verdad (del banco, de la caja o lo que indicaste).
order_iduuid | nullEl pedido que paga, si es de un pedido.
appointment_iduuid | nullLa cita que paga (una seña), si es de una cita.
ride_iduuid | nullEl viaje que paga, si es de un viaje.
conversation_iduuid | nullEl chat del pedido o del viaje que paga, para escribirle al cliente sin pedir antes el pedido. En la seña de una cita, el chat del cliente de esa cita. null en lo creado sin chat.
customerobjeto | nullResumen del cliente del pedido, la cita o el viaje.
resolved_atfecha | nullCuándo se aprobó o rechazó.
resolved_bytexto | nullQuién lo resolvió: user (alguien del equipo), bot (la IA validó el comprobante) o system (el cruce con el banco o una integración por la API). Lo que haces con tu clave queda como system, y en el panel se ve «API · nombre de la clave».
created_atfechaCuándo se creó.
updated_atfechaÚltimo cambio.

Estados de un pago

statusEn el panelQué significa
pendingPor revisarLlegó y espera validación: la automática (IA o banco) o la de alguien del equipo.
reviewPor revisarEl sistema no pudo confirmarlo solo (no leyó el monto, pasa de lo que debe el cliente, falta un dato): lo decide una persona.
approvedAprobadoAprobado. Cuenta para el pedido, la cita o el viaje.
rejectedRechazadoRechazado. No cuenta.

Cuando un pago entra en review (porque llegó así o porque pasó a ese estado) sale el webhook payment.needs_review, además del de siempre: es la señal de que alguien tiene que aprobarlo o rechazarlo (abajo). Los pagos en pending no lo disparan: esos todavía los puede confirmar la IA o el cruce con el banco.

Repartidores: asignar y ver la flota en vivo

PeticiónQué hace
POST /orders/{id}/assignPone, cambia o quita el repartidor de un pedido sin moverlo de estado. Cuerpo: delivery_user_id (un id de GET /team?role=delivery) o null para quitarlo. Vale con el pedido confirmado, en preparación, listo o en camino; a uno en camino no se le puede quitar, solo cambiar. Devuelve el pedido. Al repartidor que lo recibe ya en camino le llega el aviso de entrega.
GET /fleetLa flota en vivo: origin (dónde está el negocio) y data, un elemento por repartidor o conductor activo con name, phone, on_shift, location (lat, lng, updated_at; null si nunca reportó), active_orders y stops, las entregas que lleva encima con su dirección y coordenadas.

La posición la reporta la app del repartidor cada pocos segundos mientras tiene el turno abierto. No hay aviso por webhook de cada movimiento: para un mapa en vivo, consulta GET /fleet cada 5 a 10 segundos mientras el mapa esté a la vista, y mira updated_at para saber si una posición es reciente. También se puede asignar al mandar el pedido en camino, con delivery_user_id en POST /orders/{id}/status.

Listar y ver pagos

GET/payments

Del más nuevo al más viejo, paginado. Filtros (todos opcionales, se combinan):

ParámetroQué filtra
statusUno de los cuatro estados de arriba. Otro valor: 400 invalid_status.
order_id, appointment_id, ride_idLos pagos de ese pedido, esa cita o ese viaje. Si no es un uuid: 400 invalid_id.
methodpago_movil, transferencia o cash. Otro valor se ignora.
created_after, created_beforeCreados en esa fecha o después / antes (ISO 8601). Mal escrita: 400 invalid_date.
updated_afterLos que cambiaron desde esa fecha, del más viejo al más nuevo (ver sincronización incremental).
GET/payments/{id}

Un pago, de pedido, cita o viaje.

Aprobar o rechazar un comprobante

POST/payments/{id}/approve
POST/payments/{id}/reject

Hacen lo mismo que Aprobar y Rechazar en la sección Pagos del panel y de la app. Solo sirven con pagos en pending o review; con uno ya aprobado o rechazado responden 409 already_resolved. Responden 200 con el pago actualizado.

Al aprobar puedes mandar lo que de verdad entró, si no coincide con el comprobante (todo opcional; el cuerpo puede ir vacío):

CampoQué es
amountMonto recibido, mayor que 0. Reemplaza el del comprobante.
currencyVES, USD o EUR. Si no va, se mantiene la del pago.
received_atCuándo llegó el dinero (ISO 8601 con zona). Decide con qué tasa del día se pasa a bolívares.
curl
# Aprobar tal cual (el comprobante ya trae el monto)
curl -X POST https://api.atiendefacil.com/api/v1/payments/7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d/approve \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI"

# Aprobar corrigiendo lo que de verdad entró
curl -X POST https://api.atiendefacil.com/api/v1/payments/7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d/approve \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 730, "currency": "VES", "received_at": "2026-09-29T14:01:05-04:00" }'

Qué pasa al aprobar

  • Pago de un pedido: se calcula su valor en bolívares con la tasa del día en que llegó. Si lo aprobado cubre el total, el pedido pasa a confirmed y al cliente le llega «¡Pago confirmado! Tu pedido quedó confirmado»; si no, sigue esperando y se le dice cuánto falta. Un pedido a crédito no cambia de estado, y uno que ya avanzó (preparándose, en camino) no vuelve a esperar el pago. Si el comprobante no trae monto y no mandas amount: 400 needs_amount.
  • Si sobra dinero: igual que en el panel, lo que pasa de lo que debe el pedido se aplica a los otros pedidos del mismo cliente que deben (esperando pago o a crédito con saldo), del más viejo al más nuevo, con pagos nuevos que comparten group_id. Lo que quede después es vuelto del pedido original.
  • Seña de una cita: si lo aprobado cubre la seña, la cita pasa a confirmed y se le avisa al cliente; si no, sigue en requested y se le dice cuánto falta.
  • Pago de un viaje: se despacha la solicitud a los conductores (o, si el viaje ya iba en camino, se le avisa al conductor de que no cobre). Aquí amount, currency y received_at no se usan.

Qué pasa al rechazar

  • Pedido con otros pagos (aprobados o pendientes): no se cancela. Vuelve a pending_payment (o pending_review si le queda otro por revisar; confirmed si con los demás ya está cubierto) y al cliente se le dice cuánto falta.
  • Pedido a crédito o que ya avanzó: no cambia de estado; al cliente le llega «Uno de tus pagos no pudo ser aprobado…».
  • Seña de una cita: la cita queda requested (solicitada) para que el negocio decida, y se le avisa al cliente.
  • Pago de un viaje: el viaje sigue esperando el pago y se le dice al pasajero.

Salen los webhooks payment.status_changed y, si el pedido cambia, order.status_changed, igual que desde el panel.

Registrar un cobro

POST/orders/{id}/payments

Para el dinero que entró por fuera del chat: tu caja, tu punto de venta, tu tienda online. Es el botón Registrar cobro del panel. El pago nace ya aprobado; se permiten abonos parciales.

CampoObligatorioQué es
amountSíMonto, mayor que 0.
currencySíVES, USD o EUR.
methodSícash, pago_movil o transferencia.
referenceNoHasta 120 caracteres: tu número de operación o de ticket.
received_atNoCuándo entró el dinero (ISO 8601 con zona). Si no va, ahora.
notify_customerNoAvisarle al cliente por WhatsApp. Si no lo mandas, se le avisa solo cuando el cobro mueve el pedido (lo confirma o le cobra el resto). false: nunca; true: siempre.
curl
curl -X POST https://api.atiendefacil.com/api/v1/orders/5b7c2e1a-3f4d-4c8e-9a6b-1d2e3f4a1b2c/payments \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 10,
    "currency": "USD",
    "method": "cash",
    "reference": "CAJA-1-000318"
  }'

Responde 201 con el pago creado y cómo quedó el pedido. order_remaining es lo que falta en la moneda del pedido y, a diferencia del resto de importes, viene como número.

json
{
  "payment": {
    "id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
    "object": "payment",
    "status": "approved",
    "method": "cash",
    "amount": "10.00",
    "currency": "USD",
    "amount_ves": "365.00",
    "reference": "CAJA-1-000318",
    "received_at": "2026-09-29T18:10:00.000Z",
    "order_id": "5b7c2e1a-3f4d-4c8e-9a6b-1d2e3f4a1b2c",
    …
  },
  "order_fully_paid": false,
  "order_remaining": 10
}
  • El valor en bolívares se congela con la tasa del día en que entró el dinero, no la de hoy.
  • Si el pedido esperaba el pago (pending_payment o pending_review) y con esto queda cubierto, pasa a confirmed y se imprime la comanda si toca. Si no queda cubierto, sigue esperando el resto.
  • Un pedido que ya avanzó (preparándose, en camino, entregado) o que va a crédito no cambia de estado: el cobro solo baja lo que se debe.
  • Errores: 404 not_found (no existe o es un carrito), 409 order_cancelled, 400 invalid_body.

Cobrar varios pedidos o con varios métodos

POST/payments/collect

Un cobro con varios métodos (una parte en efectivo y otra por pago móvil) y/o sobre varios pedidos del mismo cliente. Es lo que hace Registrar cobro en el panel con varias líneas o con «Cobrar también otros pedidos de este cliente». Los pagos nacen ya aprobados.

CampoObligatorioQué es
order_idsSíDe 1 a 20 ids de pedidos, todos del mismo cliente. El dinero se reparte en el orden de creación de los pedidos, del más viejo al más nuevo.
linesSíDe 1 a 6 líneas, cada una { amount, currency, method, reference? }: amount mayor que 0; currency VES, USD o EUR; method cash, pago_movil o transferencia; reference hasta 120 caracteres.
received_atNoCuándo entró el dinero (ISO 8601 con zona). Si no va, ahora.
notify_customerNoIgual que en POST /orders/{id}/payments.
curl
curl -X POST https://api.atiendefacil.com/api/v1/payments/collect \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{
    "order_ids": [
      "5b7c2e1a-3f4d-4c8e-9a6b-1d2e3f4a1b2c",
      "8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a"
    ],
    "lines": [
      { "amount": 10, "currency": "USD", "method": "cash" },
      { "amount": 500, "currency": "VES", "method": "pago_movil", "reference": "004512" }
    ]
  }'

Responde 201 con payments (los pagos creados, uno por pedido y por método, todos con el mismo group_id), group_id y orders: por cada pedido, order_id, fully_paid, remaining (lo que falta, en su moneda) y change_due (vuelto). remaining y change_due vienen como número.

json
{
  "payments": [
    { "id": "3c4d5e6f-…", "object": "payment", "status": "approved", "method": "cash", "order_id": "5b7c2e1a-…", "group_id": "9f8e7d6c-…", … },
    { "id": "4d5e6f7a-…", "object": "payment", "status": "approved", "method": "pago_movil", "order_id": "8d9e0f1a-…", "group_id": "9f8e7d6c-…", … }
  ],
  "group_id": "9f8e7d6c-5b4a-4392-8a1b-0c9d8e7f6a5b",
  "orders": [
    { "order_id": "5b7c2e1a-3f4d-4c8e-9a6b-1d2e3f4a1b2c", "fully_paid": true, "remaining": 0, "change_due": 0 },
    { "order_id": "8d9e0f1a-2b3c-4d5e-8f6a-7b8c9d0e1f2a", "fully_paid": false, "remaining": 3, "change_due": 0 }
  ]
}
  • Cada pedido se llena hasta lo que debe y se pasa al siguiente; lo que sobre después del último queda como vuelto en ese último.
  • Cada pedido cambia de estado igual que con un cobro normal (ver arriba).
  • Errores: 400 invalid_body, 400 mixed_customers (pedidos de clientes distintos), 404 not_found (algún pedido no existe o es un carrito), 409 order_cancelled (alguno está cancelado). Si falla, no se crea ningún pago.

Reportar un movimiento del banco

POST/bank-movements

Si tu sistema ve los ingresos de la cuenta del banco (tu banca en línea, tu ERP), mándalos aquí. Se concilian igual que los avisos del banco que ya recibe AtiendeFácil (ver Conexión con el banco): si casan con un comprobante pendiente de un cliente, el pedido o la cita se confirman solos.

CampoObligatorioQué es
amountSíMonto en bolívares, mayor que 0.
referenceSíReferencia de la operación (1 a 120 caracteres). Es lo que se cruza con el comprobante.
sender_phoneNoTeléfono de quien pagó (pago móvil). Se quedan solo los dígitos.
bankNoNombre del banco de origen, hasta 80 caracteres.
descriptionNoTexto libre, hasta 1.000 caracteres, para que el dueño lo reconozca.
curl
curl -X POST https://api.atiendefacil.com/api/v1/bank-movements \
  -H "Authorization: Bearer af_live_TU_CLAVE_AQUI" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 730.00,
    "reference": "004512345678",
    "sender_phone": "04140000000",
    "bank": "Banesco"
  }'
json
{
  "id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
  "object": "bank_movement",
  "status": "matched"
}
statusHTTPQué pasó
matched201Casó con un comprobante pendiente: el pago se aprobó y el pedido o la cita se confirmaron.
unmatched201Quedó guardado sin casar. Cuando llegue el comprobante del cliente con esa referencia, se cruzará solo.
duplicate200Ya habías reportado esa referencia: no se hace nada. Puedes reintentar sin miedo a duplicar.

Métodos de pago del negocio

Son las formas de pago que el asistente le ofrece al cliente y los datos que le manda para pagar: los mismos de Ajustes → Pagos, en el panel y en la app.

PeticiónQué hace
GET /payment-methodsTodos los métodos del negocio: id, external_id, kind, label, enabled, fields (cada uno con label y value) y currency (VES o USD: la moneda en que se cobra por ese método).
PUT /payment-methods/syncPublica los métodos de tu sistema. Cuerpo: methods, una lista en la que cada uno lleva external_id, label, fields (de 1 a 8; solo el efectivo puede ir sin ninguno) y, opcionales, kind y currency (VES o USD, solo en los personalizados; sin ella se deduce del nombre: «Zelle» → USD). Devuelve cómo quedaron todos.
  • La lista es completa: un método publicado por tu sistema que deje de venir se quita. Una lista vacía quita todos los tuyos.
  • Los métodos que el dueño cargó a mano (external_id: null) no se tocan.
  • kind: "pago_movil" y kind: "transferencia" son los que el sistema valida solo con el comprobante. No se crean otros: se rellenan esos dos, y sus campos tienen que llamarse exactamente Banco, Teléfono y Cédula/RIF (pago móvil) o Banco, N.º de cuenta, Titular y Cédula/RIF (transferencia). Si dejas de mandarlo, se apaga. Un RIF nuevo o cambiado tiene que traer un dígito verificador que cuadre, y la cuenta tiene que ser de 20 dígitos y empezar por el código del banco; si no, responde invalid_body con el motivo.
  • kind: "binance" rellena el método Binance del negocio, que también existe siempre. Sus campos se llaman Pay ID, Correo de Binance y Usuario; basta mandar el Pay ID o el correo. Se valida solo si el negocio enciende la validación con Binance en el panel. En GET /payment-methods sale con ese kind.
  • Sin kind (o con custom) el método es libre: Zelle, efectivo, tarjeta… con los campos que quieras.
ErrorCuándo
400 invalid_bodyFalta un campo, un external_id viene repetido, hay dos métodos del mismo tipo fijo, a pago móvil o transferencia les falta un campo con su nombre exacto, o Binance no trae ni Pay ID ni correo.
409 duplicate_nameDos métodos quedarían con el mismo nombre (por ejemplo, uno tuyo y uno cargado a mano).
409 payment_methods_fullEl negocio quedaría con más métodos de los que admite.

Preguntas frecuentes

¿Cómo me entero de los pedidos nuevos sin consultar a cada rato?

Con el webhook order.created (y payment.created para los comprobantes). Si además quieres enterarte de lo que espera a alguien, suscríbete a payment.needs_review y order.delivery_quote_needed. Ver Webhooks.

¿Cómo sé qué pedidos lleva cada repartidor?

GET /orders?delivery_user_id=…&status=out_for_delivery. El id de cada repartidor sale de GET /team?role=delivery, y dentro del pedido viene en delivery.assigned_to_id.

¿Confirmar un pedido por la API o aprobar su pago da lo mismo?

Casi. POST /orders/{id}/status con confirmed aprueba todos los comprobantes pendientes del pedido tal como vienen. POST /payments/{id}/approve aprueba ese pago en concreto y te deja corregir monto, moneda y fecha. Si necesitas corregir alguno, usa el segundo.

¿Puedo cambiar los productos o el total de un pedido?

Los productos, no: se cambian desde el panel o la app. El costo del envío sí, mientras el pedido espera el pago, con POST /orders/{id}/delivery-quote, y el total se recalcula solo.

¿No encontraste lo que buscabas?

Escríbenos y te responde una persona del equipo.