Digital Face — Integration Protocol¶
Documento para o dev do ByClinic Data: 2026-04-16 Autor: Gabriel Mowses / Digital Face
Contexto¶
A Digital Face está construindo 3 produtos SaaS independentes que se integram opcionalmente:
| Produto | Função | Stack |
|---|---|---|
| ChatDigi | Atendimento WhatsApp + AI Studio + CRM | Node.js + React |
| Agenda | Agendamento online (novo, em construção) | FastAPI + Next.js |
| ByClinic | Gestão clínica (prontuário, pacientes, financeiro) | FastAPI + Next.js |
Cada produto: - Tem auth, billing e banco de dados próprios - Funciona 100% standalone - Integração com os outros é opcional — ativada pelo admin via API key
Como funciona a integração¶
Fluxo de conexão¶
1. Admin do ByClinic acessa /settings → aba "Integrações"
2. Clica "Conectar ChatDigi" (ou Agenda)
3. Cola a API key gerada no outro produto
4. Sistema valida a key + descobre features disponíveis
5. A partir daí, UI do ByClinic exibe ações do outro produto
6. Webhooks bidirecionais entregam eventos em tempo real
Arquitetura¶
ByClinic ChatDigi / Agenda
──────── ─────────────────
API Key
Settings ─────────────────────► POST /integrations/connect
↓ valida key
↓ retorna features[]
features[]
◄─────────────────────
Evento acontece no ByClinic:
patient.created ───────────────► POST /integrations/webhook (ChatDigi)
↓ processa evento
↓ cria contato se não existe
Evento acontece no ChatDigi:
message.received
◄───────────────────── POST /integrations/webhook (ByClinic)
↓ log na ficha do paciente
O que o ByClinic precisa implementar¶
1. Gerar API Keys¶
O ByClinic precisa ter um sistema de API keys para que outros produtos se conectem.
Endpoint:
POST /api/v1/integrations/api-keys
Authorization: Bearer <admin_jwt>
Response:
{
"api_key": "bc_live_xxxxxxxxxxxxxxxxxxxxxx",
"created_at": "2026-04-16T12:00:00Z"
}
Requisitos:
- Prefixo identificador: bc_live_ (produção) / bc_test_ (sandbox)
- Vinculada à clinic (tenant)
- Admin pode revogar
- Rate limit: 100 req/min por key
2. Validar API Keys de outros produtos¶
Quando o admin cola uma key do ChatDigi ou Agenda no ByClinic:
POST /api/v1/integrations/connect
Authorization: Bearer <admin_jwt>
Body:
{
"product": "chatdigi", // ou "agenda"
"api_key": "cd_live_xxxxx", // key gerada no outro produto
"api_url": "https://api.chatdigi.digitalface.dev.br",
"webhook_url": "https://api.byclinic.xxx/api/v1/integrations/webhook"
}
O que fazer:
1. Chamar GET {api_url}/integrations/status com a key no header
2. Validar resposta (produto correto, key válida)
3. Salvar integração no banco
4. Registrar webhook_url no produto remoto via POST {api_url}/integrations/webhook/register
3. Endpoint de status (pra outros produtos validarem)¶
GET /api/v1/integrations/status
Header: X-Integration-Key: bc_live_xxxxx
Response:
{
"product": "byclinic",
"version": "1.0.0",
"clinic_id": 42,
"clinic_name": "Clínica Exemplo",
"features": [
"patient.search",
"patient.read",
"record.read",
"appointment.read",
"payment.read",
"message.send"
],
"status": "active"
}
4. Receber webhooks¶
POST /api/v1/integrations/webhook
Header: X-Integration-Key: bc_live_xxxxx
Header: X-Webhook-Signature: sha256=xxxxxxx
Body:
{
"id": "evt_xxxxxxxxxxxxx",
"product": "chatdigi",
"event_type": "message.received",
"timestamp": "2026-04-16T12:00:00Z",
"correlation_id": "corr_xxxxx",
"data": {
"contact_phone": "+5582999999999",
"contact_name": "João Silva",
"message_body": "Quero remarcar minha consulta",
"ticket_id": 1234
}
}
Validação do webhook:
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), payload, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
Eventos que ByClinic vai RECEBER:
| Origem | Evento | Quando | Dados |
|---|---|---|---|
| ChatDigi | message.received |
Msg WhatsApp recebida de contato vinculado | phone, name, body, ticket_id |
| ChatDigi | message.sent |
Msg enviada pra contato vinculado | phone, body, ticket_id |
| ChatDigi | ticket.created |
Novo ticket de contato vinculado | phone, name, ticket_id, queue |
| ChatDigi | ticket.closed |
Ticket fechado | ticket_id, resolution |
| ChatDigi | contact.created |
Novo contato criado | phone, name, email |
| Agenda | appointment.created |
Agendamento criado | client_phone, service, professional, datetime |
| Agenda | appointment.cancelled |
Agendamento cancelado | appointment_id, reason |
| Agenda | appointment.reminded |
Lembrete enviado | appointment_id, client_phone |
| Agenda | appointment.noshow |
No-show registrado | appointment_id, client_phone |
5. Enviar webhooks¶
Eventos que ByClinic vai ENVIAR:
| Evento | Quando | Dados |
|---|---|---|
patient.created |
Novo paciente cadastrado | phone, name, email, cpf |
patient.updated |
Paciente atualizado | patient_id, changed_fields |
record.created |
Novo prontuário | patient_id, record_type, summary |
payment.received |
Pagamento recebido | patient_id, amount, description |
payment.overdue |
Pagamento em atraso | patient_id, amount, due_date |
Implementação:
import httpx
import hmac
import hashlib
import json
async def send_webhook(integration, event_type: str, data: dict):
payload = json.dumps({
"id": f"evt_{uuid4().hex[:16]}",
"product": "byclinic",
"event_type": event_type,
"timestamp": datetime.utcnow().isoformat() + "Z",
"data": data
})
signature = "sha256=" + hmac.new(
integration.webhook_secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
async with httpx.AsyncClient() as client:
response = await client.post(
integration.webhook_url,
content=payload,
headers={
"Content-Type": "application/json",
"X-Integration-Key": integration.api_key,
"X-Webhook-Signature": signature,
},
timeout=10.0,
)
# Log delivery
# Se falhar: retry 3x com backoff (1s, 5s, 25s)
6. Search API (pra outros produtos consultarem)¶
Endpoints que ByClinic expõe para ChatDigi/Agenda buscarem dados:
# Buscar paciente por telefone
GET /api/v1/integrations/search/patients?phone=+5582999999999
Header: X-Integration-Key: bc_live_xxxxx
Response:
{
"found": true,
"patient": {
"id": 42,
"name": "João Silva",
"phone": "+5582999999999",
"email": "joao@email.com",
"cpf": "***.***.***-00", // mascarado
"birth_date": "1990-05-15",
"last_appointment": "2026-04-10T14:00:00Z",
"next_appointment": "2026-04-20T14:00:00Z",
"balance_due": 150.00,
"status": "active"
}
}
# Buscar prontuário resumido
GET /api/v1/integrations/search/records?patient_id=42&limit=5
Header: X-Integration-Key: bc_live_xxxxx
Response:
{
"records": [
{
"id": 100,
"date": "2026-04-10",
"type": "consultation",
"professional": "Dra. Maria",
"summary": "Retorno - paciente estável",
"url": "https://byclinic.xxx/patients/42/records/100" // link direto
}
]
}
# Buscar agendamentos do paciente
GET /api/v1/integrations/search/appointments?patient_id=42&status=scheduled
Header: X-Integration-Key: bc_live_xxxxx
Response:
{
"appointments": [
{
"id": 200,
"datetime": "2026-04-20T14:00:00Z",
"service": "Consulta retorno",
"professional": "Dra. Maria",
"status": "confirmed",
"url": "https://byclinic.xxx/agenda/200"
}
]
}
7. Action API (pra outros produtos executarem ações)¶
# ChatDigi pede pra ByClinic registrar interação no prontuário
POST /api/v1/integrations/action/log-interaction
Header: X-Integration-Key: bc_live_xxxxx
Body:
{
"patient_phone": "+5582999999999",
"interaction_type": "whatsapp_message",
"summary": "Paciente confirmou consulta dia 20/04",
"source": "chatdigi",
"source_id": "ticket_1234"
}
Modelo de dados sugerido¶
# models/integration.py
class Integration(Base):
__tablename__ = "integrations"
id: int # PK
clinic_id: int # FK → Clinic (tenant)
product: str # "chatdigi" | "agenda"
api_key_local: str # Key que ByClinic gerou pro outro produto usar
api_key_remote: str # Key do outro produto que ByClinic usa
api_url: str # URL base do outro produto
webhook_url: str # URL onde ByClinic recebe webhooks
webhook_secret: str # HMAC secret
features: list[str] # Features descobertas do outro produto
status: str # "active" | "inactive" | "error"
last_health_check: datetime # Último status check
created_at: datetime
updated_at: datetime
class IntegrationWebhookLog(Base):
__tablename__ = "integration_webhook_logs"
id: int
integration_id: int # FK → Integration
direction: str # "inbound" | "outbound"
event_type: str
payload: dict # JSON
status_code: int # HTTP response code
response_body: str
delivered: bool
retries: int
created_at: datetime
Regras de segurança¶
- API keys nunca no frontend — só backend-to-backend
- HMAC em todo webhook — rejeitar se assinatura inválida
- Rate limit — 100 req/min por integration
- Dados mascarados — CPF, dados médicos sensíveis nunca expostos em full
- Audit log — toda chamada de integração logada
- LGPD — se paciente pede exclusão no ByClinic, webhook
patient.deletedpro ChatDigi/Agenda apagar dados correlatos - Timeout — 10s pra chamadas, não bloquear fluxo principal se outro produto offline
Checklist de implementação pro dev ByClinic¶
- Model
Integration+ migration - Model
IntegrationWebhookLog+ migration - API key generation (
bc_live_xxx) -
POST /integrations/connect(validar key remota) -
GET /integrations/status(expor features) -
POST /integrations/webhook(receber + validar HMAC) - Webhook sender (enviar eventos + retry 3x)
-
GET /integrations/search/patients?phone=X -
GET /integrations/search/records?patient_id=X -
GET /integrations/search/appointments?patient_id=X -
POST /integrations/action/log-interaction - UI: /settings → aba Integrações (colar key, testar, ver status)
- UI: painel no paciente → conversas ChatDigi + agendamentos Agenda
- Rate limiting middleware
- Webhook signature validation
- Audit log de chamadas
Estimativa: ~3-4 sessões de dev.
Contato¶
Dúvidas técnicas: Gabriel Mowses — gabrielmowses@gmail.com