01 · Comece aqui
Início rápido
A integração tem duas URLs e muitos tipos de evento. O Its Time envia a agenda para o webhook do parceiro; o parceiro envia de volta somente o resultado do atendimento.
1. Crie a conexão
O administrador cadastra a URL do webhook e recebe os segredos.
2. Receba eventos
Valide a assinatura e persista os eventos de forma idempotente.
3. Reporte o resultado
Envie done ou no_show referenciando o appointmentId do Its Time.
POST https://api.itstime.pro/api/integrations/agenda/v1/connections/{publicConnectionId}/events02 · Modelo
O Its Time é a autoridade da agenda
Its Time → parceiro
Webhook de sincronização
Envia unidades, profissionais, disponibilidade, pacientes e alterações de agendamento para a URL cadastrada na conexão.
Parceiro → Its Time
Webhook de resultado
Recebe somente appointment.status_reported, com status done ou no_show.
03 · Preparação
Credenciais e ativação
- 01O administrador cria a conexão em Conexões → Integrações → Agenda Universal.
- 02O administrador informa a URL HTTPS pública que receberá os eventos.
- 03O Its Time gera
publicConnectionId,inboundSecreteoutboundSecret. - 04O administrador executa o teste e ativa a conexão; a ativação inicia a ressincronização inicial.
| Segredo | Usado por | Finalidade |
|---|---|---|
inboundSecret | Parceiro | Assinar eventos enviados ao Its Time. |
outboundSecret | Its Time | Assinar webhooks enviados ao parceiro. |
04 · Segurança
Autenticação HMAC
Todas as requisições usam JSON e os mesmos cabeçalhos de segurança. A assinatura deve ser calculada sobre os bytes exatos que serão enviados.
signature = HMAC-SHA256(secret, timestamp + "." + rawBody)
X-Agenda-Signature: sha256=<hash_hexadecimal>| Cabeçalho | Regra |
|---|---|
Content-Type | application/json |
Idempotency-Key | UUID idêntico, byte a byte, ao eventId |
X-Agenda-Timestamp | Unix timestamp em segundos ou milissegundos |
X-Agenda-Signature | sha256= seguido do HMAC-SHA256 hexadecimal |
05 · Its Time → parceiro
Eventos enviados para seu webhook
Todos os eventos usam a mesma URL. O tipo fica no campo eventType.
| eventType | Uso |
|---|---|
integration.test | Teste de conexão. |
unit.upserted | Unidade criada ou atualizada. |
professional.upserted | Profissional criado ou atualizado. |
availability.upserted | Grade ou exceção de disponibilidade atualizada. |
patient.upserted | Paciente criado ou atualizado. |
appointment.created | Novo agendamento. |
appointment.rescheduled | Horário alterado. |
appointment.cancelled | Agendamento cancelado. |
appointment.status_changed | Status alterado no Its Time. |
Cada evento de saída inclui publicConnectionId no corpo, coberto pela assinatura HMAC. Use-o para selecionar a base do grupo; dentro dela, relacione unitId ao CNPJ recebido em unit.upserted. Mais de um ID pode apontar para a mesma base.
{
"schemaVersion": "1.0",
"publicConnectionId": "4f5a39c0d310a4b91f8de2c770a1b2c3d4e5f60718293a4b",
"eventId": "9c0c3d11-1e3e-4a18-8c19-02ab7c4d2001",
"eventType": "appointment.created",
"occurredAt": "2026-09-15T15:00:00Z",
"resourceVersion": 1,
"resource": {
"id": "6f0f3d0f-9db5-4f35-a6e8-0a0c4d0e1001",
"status": "scheduled",
"startTime": "2026-09-20T14:00:00Z",
"endTime": "2026-09-20T14:30:00Z",
"timezone": "America/Sao_Paulo",
"durationMinutes": 30,
"unitId": "7de2e00c-0b1e-4f3a-9c2b-01c8b6d30001",
"locationName": null,
"professionalId": "8de2e00c-0b1e-4f3a-9c2b-01c8b6d30001",
"assignmentId": "9de2e00c-0b1e-4f3a-9c2b-01c8b6d30001",
"patientId": "0de2e00c-0b1e-4f3a-9c2b-01c8b6d30001",
"service": {
"id": "1de2e00c-0b1e-4f3a-9c2b-01c8b6d30001",
"name": "Consulta",
"durationMinutes": 30,
"price": "123.45",
"currency": "BRL"
},
"origin": "api",
"notes": null,
"cancelReason": null,
"metadata": {},
"updatedAt": "2026-09-15T15:00:00Z"
}
}06 · Parceiro → Its Time
Reportar o resultado do atendimento
Envie o evento somente quando o atendimento tiver um resultado final. O baseResourceVersion deve ser a última versão do agendamento que o parceiro conhece.
POST https://api.itstime.pro/api/integrations/agenda/v1/connections/{publicConnectionId}/events{
"schemaVersion": "1.0",
"eventId": "4b1d2d8a-5bd2-4f4c-9a9e-26a9a0e2d001",
"eventType": "appointment.status_reported",
"occurredAt": "2026-09-15T18:00:00Z",
"resource": {
"appointmentId": "6f0f3d0f-9db5-4f35-a6e8-0a0c4d0e1001",
"status": "done",
"reason": null,
"reportedAt": "2026-09-15T18:00:00Z",
"baseResourceVersion": 7,
"metadata": { "source": "reception" }
}
}| Campo | Valores |
|---|---|
eventType | appointment.status_reported |
resource.status | done ou no_show |
resource.appointmentId | UUID do agendamento recebido do Its Time |
resource.baseResourceVersion | Última resourceVersion conhecida pelo parceiro |
07 · Implementação
Exemplos de envio
Os exemplos abaixo usam o inboundSecret e enviam o mesmo corpo que foi assinado.
URL="https://api.itstime.pro/api/integrations/agenda/v1/connections/{publicConnectionId}/events"
SECRET="SEU_INBOUND_SECRET"
TIMESTAMP=$(date +%s)
EVENT_ID="4b1d2d8a-5bd2-4f4c-9a9e-26a9a0e2d001"
BODY='{"schemaVersion":"1.0","eventId":"4b1d2d8a-5bd2-4f4c-9a9e-26a9a0e2d001","eventType":"appointment.status_reported","occurredAt":"2026-09-15T18:00:00Z","resource":{"appointmentId":"6f0f3d0f-9db5-4f35-a6e8-0a0c4d0e1001","status":"done","reason":null,"reportedAt":"2026-09-15T18:00:00Z","baseResourceVersion":7,"metadata":{"source":"reception"}}}'
SIGNATURE=$(printf '%s.%s' "$TIMESTAMP" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl --request POST "$URL" --header "Content-Type: application/json" --header "Idempotency-Key: $EVENT_ID" --header "X-Agenda-Timestamp: $TIMESTAMP" --header "X-Agenda-Signature: sha256=$SIGNATURE" --data-raw "$BODY"import { createHmac, randomUUID } from "node:crypto";
const url = "https://api.itstime.pro/api/integrations/agenda/v1/connections/{publicConnectionId}/events";
const secret = process.env.AGENDA_INBOUND_SECRET;
const eventId = randomUUID();
const timestamp = Math.floor(Date.now() / 1000).toString();
const payload = {
schemaVersion: "1.0",
eventId,
eventType: "appointment.status_reported",
occurredAt: new Date().toISOString(),
resource: {
appointmentId: "6f0f3d0f-9db5-4f35-a6e8-0a0c4d0e1001",
status: "no_show",
reason: "Paciente não compareceu",
reportedAt: new Date().toISOString(),
baseResourceVersion: 7,
metadata: { source: "reception" }
}
};
const rawBody = JSON.stringify(payload);
const signature = createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Idempotency-Key": eventId,
"X-Agenda-Timestamp": timestamp,
"X-Agenda-Signature": `sha256=${signature}`
},
body: rawBody
});
console.log(response.status, await response.text());import hashlib
import hmac
import json
import os
import time
import uuid
import requests
event_id = str(uuid.uuid4())
timestamp = str(int(time.time()))
payload = {
"schemaVersion": "1.0",
"eventId": event_id,
"eventType": "appointment.status_reported",
"occurredAt": "2026-09-15T18:00:00Z",
"resource": {
"appointmentId": "6f0f3d0f-9db5-4f35-a6e8-0a0c4d0e1001",
"status": "done",
"reason": None,
"reportedAt": "2026-09-15T18:00:00Z",
"baseResourceVersion": 7,
"metadata": {"source": "reception"},
},
}
raw_body = json.dumps(payload, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
digest = hmac.new(
os.environ["AGENDA_INBOUND_SECRET"].encode("utf-8"),
timestamp.encode("ascii") + b"." + raw_body,
hashlib.sha256,
).hexdigest()
response = requests.post(
"https://api.itstime.pro/api/integrations/agenda/v1/connections/{publicConnectionId}/events",
data=raw_body,
headers={
"Content-Type": "application/json",
"Idempotency-Key": event_id,
"X-Agenda-Timestamp": timestamp,
"X-Agenda-Signature": f"sha256={digest}",
},
timeout=10,
)
print(response.status_code, response.text)08 · Operação
Respostas, erros e retentativas
{
"accepted": true,
"duplicate": false,
"eventId": "4b1d2d8a-5bd2-4f4c-9a9e-26a9a0e2d001",
"resourceVersion": 8
}| HTTP | Significado | Ação |
|---|---|---|
200 | Evento desconhecido ou recibo já concluído. | Não repetir sem necessidade. |
202 | Evento aceito ou duplicado idempotente. | Marcar como entregue. |
400 | JSON, cabeçalho ou Idempotency-Key inválido. | Corrigir antes de repetir. |
401 | Conexão, timestamp ou assinatura inválidos. | Revisar segredo, relógio e corpo bruto. |
404 | Agendamento não encontrado na conexão. | Não adivinhar por telefone/data. |
409 | Idempotência em conflito ou resourceVersion obsoleta. | Reconciliar o espelho. |
413 | Corpo acima do limite de 64 KiB. | Reduzir o payload antes de repetir. |
415 | Content-Type diferente de application/json. | Enviar como application/json. |
422 | Transição de status inválida. | Corrigir o estado enviado. |
429 | Limite excedido. | Respeitar Retry-After. |
503 | Conexão pausada ou indisponível. | Repetir com backoff. |
Para o webhook do parceiro, o Its Time repete respostas 408, 425, 429 e 5xx, além de falhas de rede. Use o mesmo eventId e o mesmo corpo ao repetir; gere apenas novo timestamp e assinatura.
09 · Referência
Swagger e contratos
Explore os dois sentidos da integração abaixo. O botão de execução está desativado para evitar chamadas reais com credenciais de produção.
10 · Finalização
Checklist de ativação
- URL pública usando HTTPS.
- Segredos guardados em gerenciador seguro.
- HMAC calculado sobre o corpo bruto.
- eventId persistido para idempotência.
- Eventos e campos desconhecidos ignorados.
- resourceVersion controlada.
- Idempotency-Key igual ao eventId no retorno.
- done e no_show enviados somente após o atendimento.
- Backoff configurado para falhas temporárias.
- Teste concluído antes da ativação.