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-signatureEscopos necessários
customer_signature:read- listagem, busca pelo cliente e detalhamentocustomer_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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
page | int >= 1 | query | não | Página (1-based). Omitir para devolver todos os perfis. |
per_page | int 1..500 | query | não | Itens 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
customer_id | int | path | sim | ID 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
signature_id | int | path | sim | ID 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
signature_id | int | path | sim | ID 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
signature_id | int | path | sim | ID 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).
| Campo | Tipo | Obrigatório | Observações |
|---|---|---|---|
id_customer | int | sim | ID do cliente (customer.id) ao qual o perfil pertence. Define a relação 1:1 e é imutável após a criação. |
nome_assinante | str | sim | Nome completo do contador/responsável que assina as declarações. 1 a 120 caracteres. |
cargo | str | não | Cargo do assinante (ex.: Contador, Sócio, Diretor). Máx. 80 caracteres. |
crc | str | não | Número do CRC com sufixo UF (ex.: CRC-SP 123456/O-3). Máx. 40 caracteres. Validação tolerante a formatos regionais. |
imagem_assinatura | str | não | Imagem 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.
| Campo | Tipo | Observações |
|---|---|---|
nome_assinante | str | Novo nome do assinante. 1 a 120 caracteres. |
cargo | str | Novo cargo. Máx. 80 caracteres. |
crc | str | Novo número do CRC. Máx. 40 caracteres. |
imagem_assinatura | str | Nova imagem da assinatura em base64. Limite de ~700 000 caracteres. |
CustomerSignatureRead
Estende os campos base com metadados gerenciados pelo servidor.
| Campo | Tipo | Notas |
|---|---|---|
id | int | Identificador do perfil de assinatura. |
id_customer | int | Cliente vinculado (relação 1:1). |
nome_assinante | str | Nome do assinante. |
cargo | str | Cargo do assinante (pode ser null). |
crc | str | Número do CRC (pode ser null). |
imagem_assinatura | str | Assinatura em base64 (pode ser null). |
id_usuario_created | int | Usuário dono do perfil (isolamento multi-tenant). |
created_at | datetime | Timestamp de criação (server-side). |
updated_at | datetime | Timestamp 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 /upserteGET /by-customer/{customer_id}são declaradas antes das rotas dinâmicasGET/PUT/DELETE /{signature_id}. Isso evita que o router caseupsert/by-customercomo se fossem umsignature_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 paranull. - Limite de
imagem_assinatura. Máximo de ~700 000 caracteres em base64 (≈ 500KB de imagem binária), para não estourar o campoTextdo MySQL nem omax_body_sizedo FastAPI. String vazia é normalizada paranull. - Relaciona-se com Customers via
id_customer(relação 1:1).