Pular para o conteúdo

Permissões

Cada chave carrega uma lista de permissões, escolhida na criação. O formato é modulo:acao, sempre em inglês, e as ações são read, create, update e delete.

clients:read
clients:create
service_orders:update

GET /public/v1/me devolve em key.permissions exatamente o que a chave tem.

Módulo Permissões
Clientes
clients
clients:read clients:create clients:update clients:delete
Leads
leads
leads:read leads:create
Ordens de serviço
service_orders
service_orders:read service_orders:create service_orders:update service_orders:delete
Orçamentos
quotes
quotes:read quotes:create quotes:update quotes:delete
Vendas
sales
sales:read sales:create sales:update
Financeiro
finance
finance:read finance:create finance:update finance:delete
Agenda
appointments
appointments:read appointments:create appointments:update appointments:delete
Produtos e estoque
products
products:read products:create products:update products:delete
Serviços
services
services:read services:create services:update services:delete
Tarefas
tasks
tasks:read tasks:create tasks:update tasks:delete
Equipe
team
team:read
Webhooks
webhooks
webhooks:read webhooks:create webhooks:update webhooks:delete
Eventos
events
events:read

Nem todo módulo aceita as quatro ações. sales não tem delete porque o painel não exclui venda, e team e events são só de leitura.

Esta tabela sai do contrato da API, no mesmo arquivo que o painel e o servidor leem. Módulo novo aparece aqui sozinho. Ter a permissão marcada na chave não quer dizer que a rota daquele módulo já exista: a Referência é a lista do que responde hoje.

GET /public/v1/me responde a qualquer chave válida, sem exigir permissão específica.

Chaves criadas na primeira versão da API continuam valendo: leads:write vale como leads:create, e leads:read e team:read seguem iguais.

O que a chave pode de fato é o encontro de quatro coisas:

permissão da chave ∩ papel do membro ∩ permissões do membro ∩ plano da empresa

Basta uma delas dizer não para a chamada ser recusada. Por isso uma chave com clients:create ainda pode receber 403.

A chave precisa ter a permissão que a rota exige. Sem ela, a resposta é 403 com o código permission_missing. Rota pública sem permissão declarada também é negada: o padrão é negar, nunca liberar.

A captação de lead toca dois módulos, e a chave precisa das permissões dos dois:

  • POST /public/v1/leads exige leads:create. Quando a chamada abre a tarefa de contato, seja a padrão (corpo sem task), seja a que você descreve em task, exige também tasks:create. Sem ela, a resposta é 403 permission_missing e nada é gravado, nem o lead. Com task: null nenhuma tarefa é aberta, e leads:create basta.
  • Se o telefone ou o e-mail já estava cadastrado, a resposta traz deduplicated: true. Nesse caso, uma chave sem leads:read nem clients:read recebe só o object e o id, tanto do lead quanto da task, e não o nome, o e-mail, a situação e a origem de quem já estava na base. A tarefa vem reduzida porque o título padrão dela é “Entrar em contato com” seguido do nome do cadastro existente; no painel, o título continua completo. Uma chave que só cadastra não lê clientes.

A ordem de serviço também escreve em outros módulos em duas opções, e a regra é a mesma:

  • POST /public/v1/service_orders com create_appointment: true e scheduled_start cria um compromisso na agenda, e exige também appointments:create. Sem scheduled_start nenhum compromisso é criado, como no painel, e a permissão não é cobrada.
  • POST /public/v1/service_orders/{id}/complete com link_financial_transaction: true lança a receita no financeiro, e exige também finance:create.

Sem a segunda permissão, a resposta é 403 permission_missing e nada é gravado: nem a OS, nem a conclusão. Sem as duas opções, a permissão de ordens de serviço basta.

As conversões de orçamento seguem a mesma regra, e cada permissão extra só é cobrada quando o efeito acontece de verdade:

  • POST /public/v1/quotes/{id}/convert_to_sale sempre cria uma venda, e exige também sales:create.
  • Quando a requisição PEDE o lançamento no financeiro (track_in_finance: true ou uma conta em payment_plan.account_id), exige também finance:create. Quando o campo não vem e a venda vai para o financeiro porque a configuração da empresa lança as vendas (o padrão), a permissão NÃO é cobrada: esse lançamento é uma automação da empresa, como o contrato da aprovação (veja abaixo), e a tela do painel também deixa quem não vê o financeiro converter assim.
  • Mandar track_in_finance ou a conta exige a permissão de financeiro no membro: a janela de conversão do painel esconde os dois de quem não vê o financeiro (403 member_permission_denied).
  • decrement_stock: true baixa o estoque dos produtos do catálogo e NÃO exige products:update: baixar estoque é parte de vender, e os caminhos de venda do painel (inclusive o portal do vendedor) baixam sem a permissão de produtos. products:* só é exigido nas rotas do próprio catálogo (produtos, variações, categorias e estoque).
  • POST /public/v1/service_orders/from_quote exige service_orders:create e quotes:read na chave, e a permissão de orçamentos no membro (a OS nasce com o conteúdo do orçamento, e converter o que a chave não pode ler seria fazer mais do que a pessoa faria) e, com create_appointment: true e scheduled_start, também appointments:create, como a criação de OS.

Sem a permissão extra, a resposta é 403 permission_missing e nada é gravado: nem a venda, nem a OS.

As vendas seguem a mesma regra. A chave precisa da permissão de vendas da rota e, quando o efeito acontece de verdade, da do módulo em que ele escreve:

  • Criar venda que PEDE o lançamento no financeiro (track_in_finance: true ou uma conta em payment_plan.account_id) exige finance:create. Sem o campo, a venda vai para o financeiro pela configuração da empresa (o padrão), e isso NÃO cobra finance:create: é automação da empresa, a mesma que a tela aplica a quem não vê o financeiro. A baixa de estoque (decrement_stock, true por padrão) não exige products:update: é parte de vender, como no painel.
  • Receber, estornar recebimento, reagendar parcelas, criar versão nova e cancelar a venda são ações de VENDAS, como na aba Recebimentos do painel, que o membro sem o financeiro opera: pedem só sales:update na chave e a permissão de vendas no membro, mesmo numa venda que está no financeiro ou que baixou estoque. O que essas ações mudam no financeiro (lançamentos) e no estoque (devolução) é automação da venda.
  • A permissão de financeiro só entra quando a requisição ESCOLHE algo do financeiro: uma conta (account_id no recebimento ou payment_plan.account_id na venda e na versão) exige finance:read e finance:create (na versão, finance:update) na chave, e a permissão de financeiro no membro. Decidir track_in_finance na criação exige a permissão de financeiro no membro. A tela do painel esconde os dois de quem não vê o financeiro.

Sem a permissão extra, a resposta é 403 e nada é gravado.

O financeiro usa finance:read, finance:create, finance:update e finance:delete, e a permissão de financeiro no membro, nas três áreas (lançamentos, contas e categorias):

  • Ler é finance:read. Criar lançamento, transferência, recorrência, parcelamento, conta e categoria é finance:create. Editar e mark_paid são finance:update. Excluir é finance:delete.
  • Os papéis são os do painel: categorias só o dono e os administradores criam, editam e excluem, e conta só eles excluem. Outro papel recebe 403 member_permission_denied.
  • mark_paid numa parcela de venda registra o recebimento da VENDA, e pede só a permissão de financeiro: o recebimento é consequência da ação, como no painel, e não cobra sales:*. É o mesmo princípio das vendas no sentido contrário: receber pela venda não cobra finance:*.
  • Toda leitura do financeiro agenda a sincronização das vendas com o financeiro, como a tela. É automação da empresa e não cobra sales:read.

Aprovar um orçamento pode criar um contrato, quando a empresa ligou o contrato automático da mensalidade. Essa criação é uma automação da empresa, que roda igual quando o cliente aprova pelo portal, e não pede permissão extra na chave: quotes:update basta para aprovar.

A chave age como um membro da empresa, escolhido na criação. O papel dele vale na API igual ao que vale no painel:

  • OWNER e ADMIN passam por todas as áreas.
  • MEMBER depende das permissões marcadas no perfil dele, tanto para ler quanto para escrever.
  • VIEWER nunca escreve. Lê as áreas que tiver marcadas.

Uma chave nunca representa um membro de papel acima de quem a criou. Um ADMIN não emite chave que age como OWNER.

Dentro do papel MEMBER e VIEWER, o painel marca área por área o que a pessoa acessa. A API respeita a mesma marcação. Se a pessoa não vê o financeiro na tela, a chave que a representa não lê o financeiro pela API. A recusa é 403 com o código member_permission_denied.

A mesma marcação recorta listas que juntam áreas diferentes. GET /public/v1/team devolve a equipe que o painel mostraria ao membro: técnicos só com a permissão de técnicos, vendedores só com a de vendas, e membros do painel e auxiliares com a de tarefas. OWNER e ADMIN veem todos. Quem não tem uma dessas permissões recebe a lista sem aquelas pessoas, e não um erro.

Isso vale no instante. Mudou a marcação no painel, a chamada seguinte já sente, sem esperar nada expirar. E se o membro for suspenso ou removido da empresa, a chave para de autenticar: a resposta deixa de ser 403 e vira o 401 de Autenticação.

O plano precisa incluir a API, e as cotas de cadastro do plano valem igual pela API. As recusas são 403 com plan_feature_unavailable (o plano não inclui o recurso) e plan_limit_reached (a cota acabou). Assinatura fora de dia responde 403 com subscription_inactive.

O code do erro diz qual camada recusou:

  • permission_missing: recusou a chave. Crie uma chave com a permissão que falta, ou aponte a integração para uma chave que já a tenha.
  • member_permission_denied: recusou o papel ou as permissões do membro. Ajuste o acesso desse membro no painel, ou emita a chave para outro membro.
  • plan_feature_unavailable: recusou o plano. O plano da empresa não inclui esse recurso.
  • plan_limit_reached: recusou a cota do plano. A cota do período acabou.
  • subscription_inactive: recusou a assinatura. Regularize a assinatura no painel.
  • access_denied: 403 sem causa mapeada. A API recusou e nenhum dos códigos acima se aplica. Se você receber este, mande o request_id para o suporte.

As três respostas querem dizer coisas diferentes, e a diferença importa na hora de depurar.

401, authentication_error. A API não sabe quem está chamando. Toda recusa de autenticação devolve a mesma resposta, com o código api_key_invalid. Veja Autenticação.

403, permission_error. A API sabe quem está chamando e não deixa. É uma das quatro camadas acima.

404, invalid_request_error. O recurso não existe, ou existe e é de outra empresa. Id de outra empresa responde 404, nunca 403: responder 403 confirmaria que aquele id existe em algum lugar. Caminho que não é rota da API também responde 404, com o código route_not_found.

Vale a leitura direta: 401 é problema de chave, 403 é problema de direito, 404 é problema de endereço ou de id.

  • Marque só o que a integração usa. Uma integração que lê clientes não precisa de clients:delete.
  • Uma chave por integração. Revogar uma não derruba as outras, e o registro de uso mostra quem chamou o quê.
  • Emita a chave para o membro certo. Se a integração só precisa ler, emita para um membro que só lê: aí nem um erro de marcação na chave abre escrita.