Ir al contenido

Permisos

Cada clave lleva una lista de permisos, elegida en la creación. El formato es modulo:accion, siempre en inglés, y las acciones son read, create, update y delete.

clients:read
clients:create
service_orders:update

GET /public/v1/me devuelve en key.permissions exactamente lo que la clave tiene.

Módulo Permisos
Clientes
clients
clients:read clients:create clients:update clients:delete
Leads
leads
leads:read leads:create
Órdenes de servicio
service_orders
service_orders:read service_orders:create service_orders:update service_orders:delete
Presupuestos
quotes
quotes:read quotes:create quotes:update quotes:delete
Ventas
sales
sales:read sales:create sales:update
Finanzas
finance
finance:read finance:create finance:update finance:delete
Agenda
appointments
appointments:read appointments:create appointments:update appointments:delete
Productos y stock
products
products:read products:create products:update products:delete
Servicios
services
services:read services:create services:update services:delete
Tareas
tasks
tasks:read tasks:create tasks:update tasks:delete
Equipo
team
team:read
Webhooks
webhooks
webhooks:read webhooks:create webhooks:update webhooks:delete
Eventos
events
events:read

No todos los módulos aceptan las cuatro acciones. sales no tiene delete porque el panel no elimina una venta, y team y events son solo de lectura.

Esta tabla sale del contrato de la API, del mismo archivo que leen el panel y el servidor. Un módulo nuevo aparece aquí solo. Tener el permiso marcado en la clave no quiere decir que la ruta de ese módulo ya exista: la Referencia es la lista de lo que responde hoy.

GET /public/v1/me responde a cualquier clave válida, sin exigir un permiso específico.

Las claves creadas en la primera versión de la API siguen valiendo: leads:write vale como leads:create, y leads:read y team:read siguen iguales.

Lo que la clave puede hacer de verdad es el encuentro de cuatro cosas:

permiso de la clave ∩ rol del miembro ∩ permisos del miembro ∩ plan de la empresa

Basta con que una de ellas diga que no para que la llamada sea rechazada. Por eso una clave con clients:create todavía puede recibir un 403.

La clave necesita el permiso que la ruta exige. Sin él, la respuesta es 403 con el código permission_missing. Una ruta pública sin permiso declarado también se rechaza: lo predeterminado es negar, nunca permitir.

La captación de lead toca dos módulos, y la clave necesita los permisos de los dos:

  • POST /public/v1/leads exige leads:create. Cuando la llamada abre la tarea de contacto, sea la predeterminada (cuerpo sin task), sea la que usted describe en task, exige también tasks:create. Sin él, la respuesta es 403 permission_missing y no se guarda nada, ni el lead. Con task: null no se abre ninguna tarea, y leads:create alcanza.
  • Si el teléfono o el correo ya estaba registrado, la respuesta trae deduplicated: true. En ese caso, una clave sin leads:read ni clients:read recibe solo el object y el id, tanto del lead como de la task, y no el nombre, el correo, la situación y el origen de quien ya estaba en la base. La tarea viene reducida porque su título predeterminado es “Entrar em contato com” seguido del nombre del registro existente; en el panel, el título sigue completo. Una clave que solo registra no lee clientes.

La orden de servicio también escribe en otros módulos en dos opciones, y la regla es la misma:

  • POST /public/v1/service_orders con create_appointment: true y scheduled_start crea una cita en la agenda, y exige también appointments:create. Sin scheduled_start no se crea ninguna cita, como en el panel, y el permiso no se exige.
  • POST /public/v1/service_orders/{id}/complete con link_financial_transaction: true registra el ingreso en finanzas, y exige también finance:create.

Sin el segundo permiso, la respuesta es 403 permission_missing y no se guarda nada: ni la orden, ni la conclusión. Sin esas dos opciones, el permiso de órdenes de servicio alcanza.

Las conversiones de presupuesto siguen la misma regla, y cada permiso extra solo se exige cuando el efecto ocurre de verdad:

  • POST /public/v1/quotes/{id}/convert_to_sale siempre crea una venta, y exige también sales:create.
  • Cuando la solicitud PIDE el registro en finanzas (track_in_finance: true o una cuenta en payment_plan.account_id), exige también finance:create. Cuando el campo no viene y la venta va a finanzas porque la configuración de la empresa registra las ventas (lo estándar), el permiso NO se exige: ese registro es una automatización de la empresa, como el contrato de la aprobación (vea abajo), y la pantalla del panel también deja convertir así a quien no ve finanzas.
  • Enviar track_in_finance o la cuenta exige el permiso de finanzas en el miembro: la ventana de conversión del panel esconde los dos a quien no ve finanzas (403 member_permission_denied).
  • decrement_stock: true descuenta el stock de los productos del catálogo y NO exige products:update: descontar stock es parte de vender, y los caminos de venta del panel (incluido el portal del vendedor) lo descuentan sin el permiso de productos. products:* solo se exige en las rutas del propio catálogo (productos, variaciones, categorías y stock).
  • POST /public/v1/service_orders/from_quote exige service_orders:create y quotes:read en la clave, y el permiso de presupuestos en el miembro (la orden nace con el contenido del presupuesto, y convertir lo que la clave no puede leer sería hacer más de lo que haría la persona) y, con create_appointment: true y scheduled_start, también appointments:create, como la creación de orden.

Sin el permiso extra, la respuesta es 403 permission_missing y no se guarda nada: ni la venta, ni la orden.

Las ventas siguen la misma regla. La clave necesita el permiso de ventas de la ruta y, cuando el efecto ocurre de verdad, el del módulo en el que escribe:

  • Crear una venta que PIDE el registro en finanzas (track_in_finance: true o una cuenta en payment_plan.account_id) exige finance:create. Sin el campo, la venta va a finanzas por la configuración de la empresa (lo estándar), y eso NO exige finance:create: es una automatización de la empresa, la misma que la pantalla aplica a quien no ve finanzas. El descuento de stock (decrement_stock, true por defecto) no exige products:update: es parte de vender, como en el panel.
  • Cobrar, anular un cobro, reprogramar cuotas, crear versión nueva y cancelar la venta son acciones de VENTAS, como la pestaña Cobros del panel, que el miembro sin finanzas opera: piden solo sales:update en la clave y el permiso de ventas en el miembro, incluso en una venta que está en finanzas o que descontó stock. Lo que esas acciones cambian en finanzas (movimientos) y en el stock (devolución) es automatización de la venta.
  • El permiso de finanzas solo entra cuando la solicitud ELIGE algo de finanzas: una cuenta (account_id en el cobro o payment_plan.account_id en la venta y en la versión) exige finance:read y finance:create (en la versión, finance:update) en la clave, y el permiso de finanzas en el miembro. Decidir track_in_finance en la creación exige el permiso de finanzas en el miembro. La pantalla del panel esconde los dos a quien no ve finanzas.

Sin el permiso extra, la respuesta es 403 y no se guarda nada.

Finanzas usa finance:read, finance:create, finance:update y finance:delete, y el permiso de finanzas del miembro, en sus tres áreas (movimientos, cuentas y categorías):

  • Leer es finance:read. Crear movimiento, transferencia, recurrencia, cuotas, cuenta y categoría es finance:create. Editar y mark_paid son finance:update. Eliminar es finance:delete.
  • Los roles son los del panel: solo el dueño y los administradores crean, editan y eliminan categorías, y solo ellos eliminan cuentas. Otro rol recibe 403 member_permission_denied.
  • mark_paid en una cuota de venta registra el cobro de la VENTA, y pide solo el permiso de finanzas: el cobro es consecuencia de la acción, como en el panel, y no exige sales:*. Es el principio de las ventas en el sentido contrario: cobrar por la venta no exige finance:*.
  • Toda lectura de finanzas agenda la sincronización de las ventas con finanzas, como la pantalla. Es automatización de la empresa y no exige sales:read.

Aprobar un presupuesto puede crear un contrato, cuando la empresa activó el contrato automático de la mensualidad. Esa creación es una automatización de la empresa, que corre igual cuando el cliente aprueba por el portal, y no pide permiso extra en la clave: quotes:update alcanza para aprobar.

La clave actúa como un miembro de la empresa, elegido en la creación. Su rol vale en la API igual que en el panel:

  • OWNER y ADMIN pasan por todas las áreas.
  • MEMBER depende de los permisos marcados en su perfil, tanto para leer como para escribir.
  • VIEWER nunca escribe. Lee las áreas que tenga marcadas.

Una clave nunca representa a un miembro con un rol superior al de quien la creó. Un ADMIN no emite una clave que actúe como OWNER.

Dentro de los roles MEMBER y VIEWER, el panel marca área por área lo que la persona alcanza. La API respeta las mismas marcas. Si la persona no ve las finanzas en la pantalla, la clave que la representa no lee las finanzas por la API. El rechazo es 403 con el código member_permission_denied.

Las mismas marcas recortan listas que juntan áreas distintas. GET /public/v1/team devuelve el equipo que el panel le mostraría al miembro: técnicos solo con el permiso de técnicos, vendedores solo con el de ventas, y miembros del panel y auxiliares con el de tareas. OWNER y ADMIN ven a todos. Quien no tiene uno de esos permisos recibe la lista sin esas personas, y no un error.

Esto vale al instante. Si cambian las marcas en el panel, la llamada siguiente ya lo siente, sin esperar nada. Y si el miembro es suspendido o eliminado de la empresa, la clave deja de autenticar: la respuesta ya no es 403, pasa a ser el 401 de Autenticación.

El plan necesita incluir la API, y las cuotas de registro del plan valen igual por la API. Los rechazos son 403 con plan_feature_unavailable (el plan no incluye el recurso) y plan_limit_reached (la cuota se agotó). Una suscripción que no está al día responde 403 con subscription_inactive.

Cómo descubrir cuál de las cuatro rechazó

Sección titulada «Cómo descubrir cuál de las cuatro rechazó»

El code del error dice qué capa rechazó:

  • permission_missing: rechazó la clave. Cree una clave con el permiso que falta, o apunte la integración a una clave que ya lo tenga.
  • member_permission_denied: rechazó el rol o los permisos del miembro. Ajuste el acceso de ese miembro en el panel, o emita la clave para otro miembro.
  • plan_feature_unavailable: rechazó el plan. El plan de la empresa no incluye ese recurso.
  • plan_limit_reached: rechazó la cuota del plan. La cuota del período se agotó.
  • subscription_inactive: rechazó la suscripción. Regularice la suscripción en el panel.
  • access_denied: un 403 sin causa mapeada. La API rechazó y ninguno de los códigos de arriba se aplica. Si recibe este, envíe el request_id al soporte.

Las tres respuestas quieren decir cosas distintas, y la diferencia importa a la hora de depurar.

401, authentication_error. La API no sabe quién está llamando. Todo rechazo de autenticación devuelve la misma respuesta, con el código api_key_invalid. Vea Autenticación.

403, permission_error. La API sabe quién está llamando y no lo permite. Es una de las cuatro capas de arriba.

404, invalid_request_error. El recurso no existe, o existe y es de otra empresa. Un id de otra empresa responde 404, nunca 403: responder 403 confirmaría que ese id existe en algún lugar. Una ruta que no es de la API también responde 404, con el código route_not_found.

Vale la lectura directa: 401 es problema de clave, 403 es problema de derecho, 404 es problema de dirección o de id.

  • Marque solo lo que la integración usa. Una integración que lee clientes no necesita clients:delete.
  • Una clave por integración. Revocar una no afecta a las demás, y el registro de uso muestra quién llamó a qué.
  • Emita la clave para el miembro correcto. Si la integración solo necesita leer, emítala para un miembro que solo lee: así ni un error de marcado en la clave abre la escritura.