Appearance
Clientes (CRM)
O CRM concentra tudo que o estabelecimento sabe sobre quem compra: o cliente em si (pessoa ou empresa), suas preferências e alergias (com severidade que salta na tela do garçom), seus documentos cifrados (passaporte, identidade), as tags que classificam ou bloqueiam e as regras de segmento que aplicam tags automaticamente por job.
O cliente aceita os tipos person e company, telefone em formato E.164 (+5511987654321), nacionalidade e país de residência em ISO 3166-1 alpha-2 e consentimento de marketing (marketingOptIn / marketingOptInAt). O e-mail é único por estabelecimento enquanto o cliente não estiver anonimizado — o campo anonymizedAt marca a anonimização LGPD, que libera o e-mail para reutilização e exclui o registro das buscas por padrão.
Headers obrigatórios
Todas as requisições devem incluir o header X-Tour-Id (UUID) com o identificador da instância e Authorization: Bearer {token} com o token de um usuário staff (guard toursUser).
Permissões
As mutações desta página exigem a permissão customer.write — a API responde 403 com code: FORBIDDEN quando ela falta. A leitura de documentos (index e show de customer-documents) exige customer.document.read, pois o conteúdo é sensível (cifrado com chave do KMS).
Clientes
Listar Clientes
Lista os clientes do estabelecimento, com paginação, busca e filtros.
http
GET /v1/tours/user/customersParâmetros de Query
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
page | number | Não | O número da página (mínimo: 1). |
perPage | number | Não | Itens por página (entre 1 e 100). |
search | string | Não | Busca por nome (máx. 150 caracteres). |
kind | enum | Não | Filtra por tipo: person ou company. |
marketingOptIn | boolean | Não | Filtra por consentimento de marketing. |
anonymized | boolean | Não | true lista só anonimizados; false só não anonimizados. |
orderBy | enum | Não | id, fullName ou createdAt (padrão: id). |
orderDirection | enum | Não | asc ou desc. |
Exemplo de Requisição
bash
curl -X GET "https://api.us.basis.tavares.in/v1/tours/user/customers?kind=person&marketingOptIn=true" \
-H "X-Tour-Id: 123e4567-e89b-12d3-a456-426614174000" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"Respostas
200 OK
Retorna a página de clientes no formato meta/data.
json
{
"meta": {
"currentPage": 1,
"perPage": 10
},
"data": [
{
"id": 1,
"tourId": 1,
"kind": "person",
"fullName": "Mr. Rogelio Cummerata III",
"legalName": null,
"email": "Vada.Corkery93@yahoo.com",
"phoneE164": null,
"birthDate": null,
"nationality": null,
"residenceCountry": null,
"preferredLocale": null,
"marketingOptIn": false,
"marketingOptInAt": null,
"notes": null,
"anonymizedAt": null,
"createdAt": "2026-10-07T11:56:14.487-03:00",
"updatedAt": "2026-10-07T11:56:14.487-03:00"
}
]
}Criar Cliente
Cadastra um cliente (pessoa ou empresa). Exige customer.write.
http
POST /v1/tours/user/customersPayload (Body)
| Atributo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
kind | enum | Não | person ou company (padrão: person). |
fullName | string | Sim | Nome completo / nome fantasia (máx. 200 caracteres). |
legalName | string | Não | Razão social (máx. 200 caracteres). |
email | string | Não | E-mail único no estabelecimento (máx. 250 caracteres). |
phoneE164 | string | Não | Telefone em E.164: + seguido de 8 a 15 dígitos. |
birthDate | date | Não | Data de nascimento (YYYY-MM-DD). |
nationality | string | Não | Nacionalidade, ISO alpha-2 (2 letras, ex.: BR). |
residenceCountry | string | Não | País de residência, ISO alpha-2. |
preferredLocale | string | Não | Locale preferido (máx. 10 caracteres, ex.: pt-BR). |
marketingOptIn | boolean | Não | Consentimento de marketing (padrão: false). |
marketingOptInAt | date | Não | Data/hora do consentimento (ISO 8601). |
notes | string | Não | Observações internas. |
Exemplo de Requisição
bash
curl -X POST "https://api.us.basis.tavares.in/v1/tours/user/customers" \
-H "X-Tour-Id: 123e4567-e89b-12d3-a456-426614174000" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"kind": "person",
"fullName": "Cliente Doc",
"email": "doc.cliente@tours.dev",
"phoneE164": "+5511987654321",
"birthDate": "1990-05-15",
"nationality": "BR",
"residenceCountry": "BR",
"preferredLocale": "pt-BR",
"marketingOptIn": true,
"notes": "Cliente criado pela documentação"
}'Respostas
201 Created
Retorna o ID do cliente criado.
json
{
"id": 4
}422 Unprocessable Entity
Ocorre quando o e-mail já existe no estabelecimento (em cliente não anonimizado) ou o payload é inválido.
json
{
"errors": [
{
"message": "The email has already been taken",
"rule": "database.unique",
"field": "email"
}
]
}403 Forbidden
Ocorre quando o usuário não possui a permissão customer.write (code: FORBIDDEN).
Listar para Dropdown
Retorna os clientes em formato simplificado (value/label), ideal para selects — como o vínculo de cliente a uma reserva.
http
GET /v1/tours/user/customers/listAceita search (máx. 150 caracteres) e limit (1 a 500) como parâmetros opcionais de query.
Exemplo de Requisição
bash
curl -X GET "https://api.us.basis.tavares.in/v1/tours/user/customers/list" \
-H "X-Tour-Id: 123e4567-e89b-12d3-a456-426614174000" \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"Respostas
200 OK
Retorna um array com os clientes.
json
[
{
"value": 4,
"label": "Cliente Doc"
}
]Obter Cliente por ID
Recupera os detalhes do cliente, já com preferências, documentos e tags relacionados.
http
GET /v1/tours/user/customers/:idParâmetros de Rota
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | number | Sim | O ID do cliente. |
Respostas
200 OK
Retorna o cliente com toursCustomerPreferences, toursCustomerDocuments e toursTags.
json
{
"id": 4,
"tourId": 1,
"kind": "person",
"fullName": "Cliente Doc",
"legalName": null,
"email": "doc.cliente@tours.dev",
"phoneE164": "+5511987654321",
"birthDate": "1990-05-15",
"nationality": "BR",
"residenceCountry": "BR",
"preferredLocale": "pt-BR",
"marketingOptIn": true,
"marketingOptInAt": null,
"notes": "Cliente criado pela documentação",
"anonymizedAt": null,
"createdAt": "2026-10-07T15:10:44.190-03:00",
"updatedAt": "2026-10-07T15:10:44.190-03:00",
"toursCustomerPreferences": [],
"toursCustomerDocuments": [],
"toursTags": []
}404 Not Found
Ocorre quando o cliente não existe no estabelecimento.
Atualizar Cliente
Atualiza os dados do cliente. Exige customer.write.
http
PUT /v1/tours/user/customers/:idPayload (Body)
Aceita os mesmos campos da criação, todos opcionais — envie apenas o que deve mudar. O e-mail continua único por estabelecimento, ignorando o próprio cliente.
Exemplo de Requisição
bash
curl -X PUT "https://api.us.basis.tavares.in/v1/tours/user/customers/4" \
-H "X-Tour-Id: 123e4567-e89b-12d3-a456-426614174000" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"fullName": "Cliente Doc Editado",
"notes": "Cliente editado pela documentação"
}'Respostas
204 No Content
Atualização concluída com sucesso.
422 Unprocessable Entity
Ocorre quando o e-mail informado já pertence a outro cliente do estabelecimento.
Sincronizar Tags
Substitui o conjunto de tags do cliente (sync N:N): as tags enviadas passam a ser exatamente as vinculadas. Exige customer.write.
http
PUT /v1/tours/user/customers/:id/tagsPayload (Body)
| Atributo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
tagIds | number[] | Sim | IDs das tags do estabelecimento (devem existir). |
Exemplo de Requisição
bash
curl -X PUT "https://api.us.basis.tavares.in/v1/tours/user/customers/4/tags" \
-H "X-Tour-Id: 123e4567-e89b-12d3-a456-426614174000" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{ "tagIds": [1] }'Respostas
204 No Content
Sincronização concluída com sucesso.
422 Unprocessable Entity
Ocorre quando algum tagId não existe no estabelecimento.
Remover Cliente
Remove um cliente via soft delete. Exige customer.write.
http
DELETE /v1/tours/user/customers/:idRespostas
204 No Content
Remoção concluída com sucesso.
404 Not Found
Ocorre quando o cliente não existe no estabelecimento.
Preferências do Cliente
Preferências e restrições livres por cliente — alergia, dieta, quarto, serviço — classificadas por severidade. Uma alergia critical salta na tela do garçom no POS; warning e info aparecem como alertas discretos. O kind é texto livre (máx. 30 caracteres): convenções comuns são allergy, diet, room e service.
Listar Preferências
http
GET /v1/tours/user/customer-preferencesParâmetros de Query
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
page | number | Não | O número da página (mínimo: 1). |
perPage | number | Não | Itens por página (entre 1 e 100). |
tourCustomerId | number | Não | Filtra por cliente. |
kind | string | Não | Filtra por tipo (máx. 30 caracteres). |
severity | enum | Não | info, warning ou critical. |
orderBy | enum | Não | id, kind, severity ou createdAt. |
orderDirection | enum | Não | asc ou desc. |
Respostas
200 OK
Retorna a página de preferências no formato meta/data.
json
{
"meta": {
"currentPage": 1,
"perPage": 10
},
"data": [
{
"id": 1,
"tourId": 1,
"tourCustomerId": 1,
"kind": "general",
"value": "amita summisse ustilo",
"severity": "info",
"createdAt": "2026-10-07T11:56:14.538-03:00",
"updatedAt": "2026-10-07T11:56:14.538-03:00"
}
]
}Criar Preferência
Exige customer.write.
http
POST /v1/tours/user/customer-preferencesPayload (Body)
| Atributo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
tourCustomerId | number | Sim | ID do cliente (deve existir no estabelecimento). |
kind | string | Sim | Tipo da preferência (máx. 30 caracteres). |
value | string | Sim | Descrição (máx. 255 caracteres). |
severity | enum | Não | info, warning ou critical (padrão: info). |
Exemplo de Requisição
bash
curl -X POST "https://api.us.basis.tavares.in/v1/tours/user/customer-preferences" \
-H "X-Tour-Id: 123e4567-e89b-12d3-a456-426614174000" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"tourCustomerId": 1,
"kind": "allergy",
"value": "frutos do mar",
"severity": "critical"
}'Respostas
201 Created
Retorna o ID da preferência criada.
json
{
"id": 4
}Obter Preferência por ID
http
GET /v1/tours/user/customer-preferences/:idRespostas
200 OK
Retorna a preferência.
json
{
"id": 4,
"tourId": 1,
"tourCustomerId": 1,
"kind": "allergy",
"value": "frutos do mar",
"severity": "critical",
"createdAt": "2026-10-07T15:10:43.507-03:00",
"updatedAt": "2026-10-07T15:10:43.508-03:00"
}404 Not Found
Ocorre quando a preferência não existe no estabelecimento.
Atualizar Preferência
http
PUT /v1/tours/user/customer-preferences/:idAceita os mesmos campos da criação, todos opcionais. Exige customer.write.
Respostas
204 No Content
Atualização concluída com sucesso.
Remover Preferência
http
DELETE /v1/tours/user/customer-preferences/:idRemove via soft delete. Exige customer.write. Responde 204 No Content.
Documentos do Cliente
Documentos de identificação (passaporte, RG, CNH) com número cifrado: numberCiphertext e numberFingerprint são enviados em base64 e gravados como binário — a API nunca trafega o número em claro. O encryptionKeyId referencia a chave do KMS usada na cifragem, permitindo rotação de chaves. O retainUntil marca a data limite de retenção para expurgo (LGPD).
Listar Documentos
Exige customer.document.read. A listagem omite o ciphertext/fingerprint — apenas metadados.
http
GET /v1/tours/user/customer-documentsParâmetros de Query
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
page | number | Não | O número da página (mínimo: 1). |
perPage | number | Não | Itens por página (entre 1 e 100). |
tourCustomerId | number | Não | Filtra por cliente. |
docType | string | Não | Filtra por tipo (máx. 30 caracteres). |
orderBy | enum | Não | id, docType ou createdAt. |
orderDirection | enum | Não | asc ou desc. |
Respostas
200 OK
Retorna a página de documentos no formato meta/data.
json
{
"meta": {
"currentPage": 1,
"perPage": 10
},
"data": [
{
"id": 1,
"tourId": 1,
"tourCustomerId": 1,
"docType": "passport",
"issuingCountry": "BR",
"encryptionKeyId": "key-WbvhTgeo",
"holderName": "Tyrel Barrows",
"issuedOn": null,
"expiresOn": null,
"scanObjectKey": null,
"retainUntil": null,
"createdAt": "2026-10-07T11:56:14.585-03:00",
"updatedAt": "2026-10-07T11:56:14.585-03:00"
}
]
}Criar Documento
Exige customer.write.
http
POST /v1/tours/user/customer-documentsPayload (Body)
| Atributo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
tourCustomerId | number | Sim | ID do cliente (deve existir no estabelecimento). |
docType | string | Sim | Tipo do documento (máx. 30 caracteres, ex.: passport). |
issuingCountry | string | Não | País emissor, ISO alpha-2. |
numberCiphertext | string | Sim | Número cifrado, em base64. |
numberFingerprint | string | Sim | Fingerprint do número, em base64 (para busca sem decifrar). |
encryptionKeyId | string | Sim | ID da chave do KMS (máx. 100 caracteres). |
holderName | string | Não | Nome do titular (máx. 150 caracteres). |
issuedOn | date | Não | Data de emissão (YYYY-MM-DD). |
expiresOn | date | Não | Data de validade (YYYY-MM-DD). |
retainUntil | date | Não | Retenção até (YYYY-MM-DD) — referência para expurgo. |
scanObjectKey | string | Não | Chave do objeto com o scan do documento (máx. 500). |
Exemplo de Requisição
bash
curl -X POST "https://api.us.basis.tavares.in/v1/tours/user/customer-documents" \
-H "X-Tour-Id: 123e4567-e89b-12d3-a456-426614174000" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"tourCustomerId": 1,
"docType": "passport",
"issuingCountry": "BR",
"numberCiphertext": "dGVzdGU=",
"numberFingerprint": "dGVzdGU=",
"encryptionKeyId": "kms-demo-1",
"holderName": "Cliente Doc",
"issuedOn": "2020-01-10",
"expiresOn": "2030-01-09"
}'Respostas
201 Created
Retorna o ID do documento criado.
json
{
"id": 4
}Obter Documento por ID
Exige customer.document.read. Retorna também o ciphertext e o fingerprint (serializados como buffer binário).
http
GET /v1/tours/user/customer-documents/:idRespostas
200 OK
Retorna o documento completo.
json
{
"id": 4,
"tourId": 1,
"tourCustomerId": 1,
"docType": "passport",
"issuingCountry": "BR",
"numberCiphertext": { "type": "Buffer", "data": [116, 101, 115, 116, 101] },
"numberFingerprint": { "type": "Buffer", "data": [116, 101, 115, 116, 101] },
"encryptionKeyId": "kms-demo-1",
"holderName": "Cliente Doc",
"issuedOn": "2020-01-10",
"expiresOn": "2030-01-09",
"scanObjectKey": null,
"retainUntil": null,
"createdAt": "2026-10-07T15:10:42.549-03:00",
"updatedAt": "2026-10-07T15:10:42.549-03:00"
}404 Not Found
Ocorre quando o documento não existe no estabelecimento.
Atualizar Documento
http
PUT /v1/tours/user/customer-documents/:idAceita os mesmos campos da criação, todos opcionais. Exige customer.write. Responde 204 No Content.
Remover Documento
http
DELETE /v1/tours/user/customer-documents/:idRemove via soft delete. Exige customer.write. Responde 204 No Content.
Tags
Tags classificam os clientes — vip, blacklist, corporate — e podem bloquear novas reservas (blocksBooking). Tags com isAutomatic são aplicadas pelo job de segmentação (ver Regras de Segmento) e não devem ser gerenciadas manualmente. O code é único por estabelecimento.
Listar Tags
http
GET /v1/tours/user/tagsParâmetros de Query
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
page | number | Não | O número da página (mínimo: 1). |
perPage | number | Não | Itens por página (entre 1 e 100). |
search | string | Não | Busca por código ou nome. |
blocksBooking | boolean | Não | Filtra por tags que bloqueiam reserva. |
isAutomatic | boolean | Não | Filtra por tags automáticas. |
orderBy | enum | Não | id, code, name ou createdAt. |
orderDirection | enum | Não | asc ou desc. |
Respostas
200 OK
Retorna a página de tags no formato meta/data.
json
{
"meta": {
"currentPage": 1,
"perPage": 10
},
"data": [
{
"id": 1,
"tourId": 1,
"code": "2sxn3bgqzt",
"name": "Movies",
"color": "#3B82F6",
"blocksBooking": false,
"isAutomatic": false,
"createdAt": "2026-10-07T11:56:14.602-03:00",
"updatedAt": "2026-10-07T11:56:14.602-03:00"
}
]
}Criar Tag
Exige customer.write.
http
POST /v1/tours/user/tagsPayload (Body)
| Atributo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
code | string | Sim | Código único no estabelecimento (máx. 50). |
name | string | Sim | Nome de exibição (máx. 150 caracteres). |
color | string | Não | Cor em hexadecimal (máx. 20 caracteres). |
blocksBooking | boolean | Não | Bloqueia novas reservas do cliente (padrão: false). |
isAutomatic | boolean | Não | Gerenciada pelo job de segmentação (padrão: false). |
Exemplo de Requisição
bash
curl -X POST "https://api.us.basis.tavares.in/v1/tours/user/tags" \
-H "X-Tour-Id: 123e4567-e89b-12d3-a456-426614174000" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"code": "DOC-VIP",
"name": "VIP Doc",
"color": "#FFD700",
"blocksBooking": false,
"isAutomatic": false
}'Respostas
201 Created
Retorna o ID da tag criada.
json
{
"id": 4
}422 Unprocessable Entity
Ocorre quando o code já existe no estabelecimento ou o payload é inválido.
Listar para Dropdown
http
GET /v1/tours/user/tags/listAceita search e limit (1 a 500) como parâmetros opcionais de query.
Respostas
200 OK
Retorna um array value/label.
json
[
{
"value": 1,
"label": "Movies"
},
{
"value": 4,
"label": "VIP Doc"
}
]Obter Tag por ID
http
GET /v1/tours/user/tags/:idRespostas
200 OK
Retorna a tag com suas regras de segmento (toursSegmentRules).
json
{
"id": 4,
"tourId": 1,
"code": "DOC-VIP",
"name": "VIP Doc",
"color": "#FFD700",
"blocksBooking": false,
"isAutomatic": false,
"createdAt": "2026-10-07T15:11:15.527-03:00",
"updatedAt": "2026-10-07T15:11:15.527-03:00",
"toursSegmentRules": []
}404 Not Found
Ocorre quando a tag não existe no estabelecimento.
Atualizar Tag
http
PUT /v1/tours/user/tags/:idAceita os mesmos campos da criação, todos opcionais. Exige customer.write. Responde 204 No Content.
Remover Tag
http
DELETE /v1/tours/user/tags/:idRemove via soft delete. Exige customer.write. Responde 204 No Content.
Regras de Segmento
A segmentação é declarativa: cada regra vincula uma tag (tourTagId) a uma definition de forma livre (objeto JSON com propriedades desconhecidas permitidas — ex.: { "lifetime_value_min": 5000 } ou { "field": "email", "operator": "contains", "value": "@test.com" }). Um job executa as regras ativas periodicamente, aplicando/removendo a tag nos clientes que atendem à definição e registrando lastRunAt.
Listar Regras de Segmento
http
GET /v1/tours/user/segment-rulesParâmetros de Query
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
page | number | Não | O número da página (mínimo: 1). |
perPage | number | Não | Itens por página (entre 1 e 100). |
search | string | Não | Busca por nome. |
tourTagId | number | Não | Filtra por tag. |
isActive | boolean | Não | Filtra por regras ativas/inativas. |
orderBy | enum | Não | id, name ou createdAt. |
orderDirection | enum | Não | asc ou desc. |
Respostas
200 OK
Retorna a página de regras no formato meta/data.
json
{
"meta": {
"currentPage": 1,
"perPage": 10
},
"data": [
{
"id": 1,
"tourId": 1,
"tourTagId": 1,
"name": "Sports",
"definition": {
"field": "email",
"value": "@test.com",
"operator": "contains"
},
"isActive": true,
"lastRunAt": null,
"createdAt": "2026-10-07T11:56:14.614-03:00",
"updatedAt": "2026-10-07T11:56:14.614-03:00"
}
]
}Criar Regra de Segmento
Exige customer.write.
http
POST /v1/tours/user/segment-rulesPayload (Body)
| Atributo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
tourTagId | number | Sim | ID da tag aplicada pela regra (deve existir). |
name | string | Sim | Nome da regra (máx. 150 caracteres). |
definition | object | Sim | Definição declarativa (propriedades livres). |
isActive | boolean | Não | Indica se a regra roda no job (padrão: true). |
Exemplo de Requisição
bash
curl -X POST "https://api.us.basis.tavares.in/v1/tours/user/segment-rules" \
-H "X-Tour-Id: 123e4567-e89b-12d3-a456-426614174000" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"tourTagId": 1,
"name": "Alto valor Doc",
"definition": {
"lifetime_value_min": 5000
},
"isActive": true
}'Respostas
201 Created
Retorna o ID da regra criada.
json
{
"id": 4
}Obter Regra por ID
http
GET /v1/tours/user/segment-rules/:idRespostas
200 OK
Retorna a regra com a tag vinculada (toursTag).
json
{
"id": 4,
"tourId": 1,
"tourTagId": 1,
"name": "Alto valor Doc",
"definition": {
"lifetime_value_min": 5000
},
"isActive": true,
"lastRunAt": null,
"createdAt": "2026-10-07T15:11:08.004-03:00",
"updatedAt": "2026-10-07T15:11:08.004-03:00",
"toursTag": {
"id": 1,
"code": "2sxn3bgqzt",
"name": "Movies"
}
}404 Not Found
Ocorre quando a regra não existe no estabelecimento.
Atualizar Regra de Segmento
http
PUT /v1/tours/user/segment-rules/:idAceita os mesmos campos da criação, todos opcionais. Exige customer.write. Responde 204 No Content.
Remover Regra de Segmento
http
DELETE /v1/tours/user/segment-rules/:idRemove via soft delete. Exige customer.write. Responde 204 No Content.