Customer Signature

Endpoints da WebApiAlcance para o perfil de assinatura do cliente (customer_signature). Apesar do nome, "signature" aqui não é assinatura comercial/plano de serviços: é o perfil de quem assina as declarações de um cliente - o nome do contador/responsável (nome_assinante), seu cargo, o número do crc e a imagem_assinatura (assinatura digitalizada em base64). O caso de uso primário é o webapp-tributos: ao salvar o formulário de declaração, o front associa a esse cliente o assinante que consta no documento.

A relação com o cliente é 1:1 - cada customer tem no máximo um perfil de assinatura, identificado por id_customer. O perfil pertence ao usuário que o criou (id_usuario_created), e a leitura/escrita é restrita a esse dono (isolamento multi-tenant).

Todos os endpoints exigem autenticação. Veja Autenticação para o fluxo de API Key. O Swagger oficial está em api.contabilidadealcance.com.br/docs.

Base path

/api/v1/customer-signature

Escopos necessários

  • customer_signature:read - listagem, busca pelo cliente e detalhamento
  • customer_signature:write - criação, upsert, atualização e exclusão

O escopo é derivado da rota: o prefixo /customer-signature vira o recurso customer_signature (hífen → underscore), e o método HTTP define a ação (read para GET, write para POST/PUT/DELETE).

Endpoints

POST /customer-signature/

Cria um novo perfil de assinatura (relação 1:1 com customer). Conflitos (cliente já com perfil) retornam 409 Conflict; cliente inexistente retorna 404 Not Found; validações retornam 422 Unprocessable Entity.

Parâmetros

Este endpoint não recebe parâmetros de rota ou query - os dados vão no corpo da requisição (CustomerSignatureCreate): id_customer (obrigatório), nome_assinante (obrigatório), cargo, crc e imagem_assinatura (opcionais).

Request

{
  "id_customer": 1234,
  "nome_assinante": "João da Silva",
  "cargo": "Contador",
  "crc": "CRC-SP 123456/O-3",
  "imagem_assinatura": "data:image/png;base64,iVBORw0KGgo..."
}

Response 201 Created

{
  "success": true,
  "message": "Perfil de assinatura criado com sucesso!",
  "data": {
    "id": 1,
    "id_customer": 1234,
    "nome_assinante": "João da Silva",
    "cargo": "Contador",
    "crc": "CRC-SP 123456/O-3",
    "imagem_assinatura": "data:image/png;base64,iVBORw0KGgo...",
    "id_usuario_created": 42,
    "created_at": "2026-07-03T10:00:00",
    "updated_at": "2026-07-03T10:00:00"
  }
}

Escopo: customer_signature:write

POST /customer-signature/upsert

Cria ou atualiza o perfil pelo id_customer - operação idempotente. Se já existe um perfil para o cliente e ele pertence ao usuário autenticado, atualiza os campos; caso contrário, cria. O front não precisa saber se é insert ou update.

Parâmetros

Este endpoint não recebe parâmetros de rota ou query - os dados vão no corpo da requisição (CustomerSignatureUpsert, idêntico a CustomerSignatureCreate): id_customer (obrigatório), nome_assinante (obrigatório), cargo, crc e imagem_assinatura (opcionais).

Request

{
  "id_customer": 1234,
  "nome_assinante": "João da Silva",
  "cargo": "Contador",
  "crc": "CRC-SP 123456/O-3"
}

Response 200 OK

{
  "success": true,
  "message": "Perfil de assinatura upserted!",
  "data": {
    "id": 1,
    "id_customer": 1234,
    "nome_assinante": "João da Silva",
    "cargo": "Contador",
    "crc": "CRC-SP 123456/O-3",
    "imagem_assinatura": null,
    "id_usuario_created": 42,
    "created_at": "2026-07-03T10:00:00",
    "updated_at": "2026-07-03T11:30:00"
  }
}

Cliente inexistente ou perfil de outro usuário retorna 404 Not Found (proteção contra IDOR); validações retornam 422 Unprocessable Entity. Escopo: customer_signature:write

GET /customer-signature/

Lista todos os perfis de assinatura do usuário autenticado, com paginação opcional. Lista vazia é um estado válido - sempre retorna 200 OK, inclusive com data: [] e a mensagem "Nenhum perfil de assinatura encontrado.".

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
pageint >= 1querynãoPágina (1-based). Omitir para devolver todos os perfis.
per_pageint 1..500querynãoItens por página (máx. 500). Exige page para ativar a paginação.

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Perfis de assinatura recuperados com sucesso!",
  "data": [
    {
      "id": 1,
      "id_customer": 1234,
      "nome_assinante": "João da Silva",
      "cargo": "Contador",
      "crc": "CRC-SP 123456/O-3",
      "imagem_assinatura": "data:image/png;base64,iVBORw0KGgo...",
      "id_usuario_created": 42,
      "created_at": "2026-07-03T10:00:00",
      "updated_at": "2026-07-03T10:00:00"
    }
  ]
}

Escopo: customer_signature:read

GET /customer-signature/by-customer/{customer_id}

Busca o perfil de assinatura pelo ID do cliente (FK 1:1). É o caso de uso primário do front: dado um cliente, devolve seu perfil. Quando não há perfil para o cliente (ou ele pertence a outro usuário), retorna 404 Not Found.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
customer_idintpathsimID do cliente (customer.id)

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Perfil de assinatura encontrado!",
  "data": {
    "id": 1,
    "id_customer": 1234,
    "nome_assinante": "João da Silva",
    "cargo": "Contador",
    "crc": "CRC-SP 123456/O-3",
    "imagem_assinatura": "data:image/png;base64,iVBORw0KGgo...",
    "id_usuario_created": 42,
    "created_at": "2026-07-03T10:00:00",
    "updated_at": "2026-07-03T10:00:00"
  }
}

Escopo: customer_signature:read

GET /customer-signature/{signature_id}

Detalha um perfil pelo ID do próprio perfil. Quando não encontrado (ou pertencente a outro usuário), retorna 404 Not Found.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
signature_idintpathsimID do perfil de assinatura

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Perfil de assinatura encontrado!",
  "data": {
    "id": 1,
    "id_customer": 1234,
    "nome_assinante": "João da Silva",
    "cargo": "Contador",
    "crc": "CRC-SP 123456/O-3",
    "imagem_assinatura": "data:image/png;base64,iVBORw0KGgo...",
    "id_usuario_created": 42,
    "created_at": "2026-07-03T10:00:00",
    "updated_at": "2026-07-03T10:00:00"
  }
}

Escopo: customer_signature:read

PUT /customer-signature/{signature_id}

Atualiza um perfil existente. O body é um CustomerSignatureUpdate com campos parciais - somente o que vier preenchido é alterado. Os campos id_customer, id, id_usuario_created, created_at e updated_at são imutáveis e não são aceitos no payload (proteção contra mass-assignment).

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
signature_idintpathsimID do perfil de assinatura

Request (CustomerSignatureUpdate - todos os campos opcionais)

{
  "nome_assinante": "João da Silva Júnior",
  "cargo": "Sócio"
}

Response 200 OK

{
  "success": true,
  "message": "Perfil de assinatura atualizado!",
  "data": {
    "id": 1,
    "id_customer": 1234,
    "nome_assinante": "João da Silva Júnior",
    "cargo": "Sócio",
    "crc": "CRC-SP 123456/O-3",
    "imagem_assinatura": "data:image/png;base64,iVBORw0KGgo...",
    "id_usuario_created": 42,
    "created_at": "2026-07-03T10:00:00",
    "updated_at": "2026-07-03T11:42:00"
  }
}

Perfil inexistente ou de outro usuário retorna 404 Not Found; validações retornam 422 Unprocessable Entity. Escopo: customer_signature:write

DELETE /customer-signature/{signature_id}

Remove o perfil de assinatura pelo ID - hard delete (exclusão física do registro). Quando o perfil não existe ou pertence a outro usuário, retorna 404 Not Found.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
signature_idintpathsimID do perfil de assinatura

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "Perfil de assinatura removido!",
  "data": null
}

Escopo: customer_signature:write

Schemas

CustomerSignatureCreate

Também usado por POST /upsert (schema CustomerSignatureUpsert, idêntico).

CampoTipoObrigatórioObservações
id_customerintsimID do cliente (customer.id) ao qual o perfil pertence. Define a relação 1:1 e é imutável após a criação.
nome_assinantestrsimNome completo do contador/responsável que assina as declarações. 1 a 120 caracteres.
cargostrnãoCargo do assinante (ex.: Contador, Sócio, Diretor). Máx. 80 caracteres.
crcstrnãoNúmero do CRC com sufixo UF (ex.: CRC-SP 123456/O-3). Máx. 40 caracteres. Validação tolerante a formatos regionais.
imagem_assinaturastrnãoImagem da assinatura digitalizada em base64 (PNG/JPEG). Limite de ~700 000 caracteres (≈ 500KB de imagem binária).

CustomerSignatureUpdate

Todos os campos são opcionais; apenas os enviados são atualizados. id_customer não aparece aqui - a vinculação ao cliente é imutável.

CampoTipoObservações
nome_assinantestrNovo nome do assinante. 1 a 120 caracteres.
cargostrNovo cargo. Máx. 80 caracteres.
crcstrNovo número do CRC. Máx. 40 caracteres.
imagem_assinaturastrNova imagem da assinatura em base64. Limite de ~700 000 caracteres.

CustomerSignatureRead

Estende os campos base com metadados gerenciados pelo servidor.

CampoTipoNotas
idintIdentificador do perfil de assinatura.
id_customerintCliente vinculado (relação 1:1).
nome_assinantestrNome do assinante.
cargostrCargo do assinante (pode ser null).
crcstrNúmero do CRC (pode ser null).
imagem_assinaturastrAssinatura em base64 (pode ser null).
id_usuario_createdintUsuário dono do perfil (isolamento multi-tenant).
created_atdatetimeTimestamp de criação (server-side).
updated_atdatetimeTimestamp da última atualização (server-side).

Notas

  • Toda resposta segue o envelope { success, message, data } (ou { success: false, message, details } em erro).
  • Ordem de rotas (FastAPI). As rotas estáticas POST /upsert e GET /by-customer/{customer_id} são declaradas antes das rotas dinâmicas GET/PUT/DELETE /{signature_id}. Isso evita que o router case upsert/by-customer como se fossem um signature_id.
  • Isolamento multi-tenant. Cada perfil pertence ao usuário que o criou; leituras e escritas de perfis de outros usuários retornam 404 (não vazam a existência do registro alheio - proteção contra IDOR).
  • Validação de crc. Aceita apenas letras, números, espaços, hífens (-), barras (/) e pontos (.), sem impor máscara fixa - cada UF tem formato próprio. String vazia é normalizada para null.
  • Limite de imagem_assinatura. Máximo de ~700 000 caracteres em base64 (≈ 500KB de imagem binária), para não estourar o campo Text do MySQL nem o max_body_size do FastAPI. String vazia é normalizada para null.
  • Relaciona-se com Customers via id_customer (relação 1:1).