Referência
Todas as rotas
Gerada do mesmo registro de rotas que valida cada requisição. Para usar num gerador de cliente ou no Postman, baixe o openapi.json.
Base: https://platform.torqyn.com. Toda rota aceita o cabeçalho Torqyn-Version.
Começar
GET /v1/ping
Conferir a chave
Responde com a conta, o modo (teste ou produção) e a versão da API. Serve para conferir a chave e a conexão antes de integrar; não pede escopo.
Resposta 200
{
"object": "ping",
"account": "0f6a3c1e-2b4d-4e8a-9c21-7d4e5f6a8b90",
"livemode": false,
"api_version": "2026-10-01"
}
Erros possíveis: invalid_request, invalid_version, unauthorized, rate_limited, internal_error
Contatos
GET /v1/contacts/{external_id}
Ler um contato
O contato pelo id do seu sistema, com as etiquetas e os consentimentos em vigor. Documento (CPF/CNPJ) nunca sai pela API.
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
external_id | caminho | string |
Resposta 200
{
"object": "contact",
"id": "6f1c2a90-5b3e-4d7a-9e12-3c4b5a6d7e8f",
"external_id": "fam-123",
"name": "Ana Martins",
"phone": "+5583999998888",
"email": "ana@exemplo.com.br",
"tags": [
"Plano família"
],
"opted_out": false,
"consents": [
{
"channel": "whatsapp",
"purpose": "utility",
"origin": "api",
"granted_at": "2026-10-01T12:00:00.000Z"
}
],
"created_at": "2026-10-01T12:00:00.000Z",
"updated_at": "2026-10-01T12:00:00.000Z"
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, not_found, rate_limited, internal_error
PUT /v1/contacts/{external_id}
Criar ou atualizar um contato
Cria o contato pelo id do seu sistema, ou atualiza se ele já existe. Campo omitido fica como está; null apaga. O telefone e o e-mail que vêm do seu sistema contam como confirmados, e o contato se liga sozinho à conversa que a mesma pessoa já tem pelo WhatsApp. tags (pelo nome, como cadastradas no painel) passa a ser a lista de etiquetas que a API pôs: as que saírem da lista são tiradas; as que o time pôs à mão, nunca.
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
external_id | caminho | string |
Corpo
| Campo | Tipo | Descrição |
|---|---|---|
name opcional | string | null | |
phone opcional | string | null | |
email opcional | string | null | |
tags opcional | array |
Resposta 200
{
"object": "contact",
"id": "6f1c2a90-5b3e-4d7a-9e12-3c4b5a6d7e8f",
"external_id": "fam-123",
"name": "Ana Martins",
"phone": "+5583999998888",
"email": "ana@exemplo.com.br",
"tags": [
"Plano família"
],
"opted_out": false,
"consents": [
{
"channel": "whatsapp",
"purpose": "utility",
"origin": "api",
"granted_at": "2026-10-01T12:00:00.000Z"
}
],
"created_at": "2026-10-01T12:00:00.000Z",
"updated_at": "2026-10-01T12:00:00.000Z"
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, not_found, payload_too_large, unsupported_media_type, rate_limited, internal_error
POST /v1/contacts/{external_id}/consents
Registrar um consentimento
Registra que a pessoa aceitou receber mensagem num canal e para uma finalidade, com a evidência de como aceitou no seu sistema. marketing é o que permite mandar oferta e novidade; utility é o serviço de algo que ela já contratou. Quem pediu para sair não recebe consentimento pela API (opted_out).
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
external_id | caminho | string |
Corpo
| Campo | Tipo | Descrição |
|---|---|---|
channel | string (whatsapp, email, instagram, messenger, telegram) | |
purpose | string (marketing, utility) | |
evidence | string |
Resposta 201
{
"object": "contact",
"id": "6f1c2a90-5b3e-4d7a-9e12-3c4b5a6d7e8f",
"external_id": "fam-123",
"name": "Ana Martins",
"phone": "+5583999998888",
"email": "ana@exemplo.com.br",
"tags": [
"Plano família"
],
"opted_out": false,
"consents": [
{
"channel": "whatsapp",
"purpose": "utility",
"origin": "api",
"granted_at": "2026-10-01T12:00:00.000Z"
}
],
"created_at": "2026-10-01T12:00:00.000Z",
"updated_at": "2026-10-01T12:00:00.000Z"
}
Erros possíveis: invalid_request, invalid_version, idempotency_key_required, unauthorized, forbidden_scope, not_found, idempotency_key_in_use, payload_too_large, unsupported_media_type, idempotency_key_reused, rate_limited, internal_error
DELETE /v1/contacts/{external_id}/consents/{channel}/{purpose}
Revogar um consentimento
Revoga o consentimento daquele canal e finalidade. O registro não é apagado: fica como prova do período em que valeu.
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
external_id | caminho | string |
channel | caminho | string |
purpose | caminho | string |
Resposta 200
{
"object": "contact",
"id": "6f1c2a90-5b3e-4d7a-9e12-3c4b5a6d7e8f",
"external_id": "fam-123",
"name": "Ana Martins",
"phone": "+5583999998888",
"email": "ana@exemplo.com.br",
"tags": [
"Plano família"
],
"opted_out": false,
"consents": [],
"created_at": "2026-10-01T12:00:00.000Z",
"updated_at": "2026-10-01T12:00:00.000Z"
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, not_found, rate_limited, internal_error
Webhooks
GET /v1/events
Listar os eventos dos últimos 30 dias
Os eventos que saíram (ou sairiam) pelos webhooks nos últimos 30 dias, do mais novo para o mais antigo, no mesmo envelope, sem campo pessoal do contato (só os ids). Serve para conferir o que o seu endpoint perdeu. Filtre por type.
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
limit opcional | query | integer |
starting_after opcional | query | string |
type opcional | query | Só um tipo de evento |
Resposta 200
{
"object": "list",
"data": [
{
"id": "0f6a3c1e-8b2d-4e5f-9a1b-2c3d4e5f6a7b",
"type": "message.delivered",
"api_version": "2026-10-01",
"created": "2026-10-02T10:15:03.000Z",
"livemode": false,
"account": "1a2b3c4d-5e6f-4a8b-9c0d-1e2f3a4b5c6d",
"data": {
"object": {
"object": "message",
"id": "3a5c7e9b-1d2f-4a6b-8c0d-2e4f6a8b0c1d",
"direction": "outbound",
"status": "delivered",
"conversation_id": "9b2f4c1e-7d3a-4e5f-8a6b-1c2d3e4f5a6b",
"channel": "whatsapp",
"contact": {
"id": "5b7c2d1e-8f3a-4b6c-9d2e-1f4a5b6c7d8e",
"external_id": "fam-123"
}
}
}
}
],
"has_more": false
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, rate_limited, internal_error
GET /v1/webhook-endpoints
Listar os endpoints de webhook
Os endpoints desta conta (a de teste ou a de produção, pela chave), sem o segredo. Inclui os desligados.
Resposta 200
{
"object": "list",
"data": [
{
"object": "webhook_endpoint",
"id": "4d2c1b0a-9f8e-4d7c-8b6a-5f4e3d2c1b0a",
"name": "Meu sistema",
"url": "https://api.seusistema.com.br/torqyn",
"events": [
"message.delivered",
"message.failed",
"appointment.created"
],
"customer_fields": [],
"api_version": "2026-10-01",
"status": "active",
"pause_reason": null,
"previous_secret_valid_until": null,
"livemode": false,
"created_at": "2026-10-01T12:00:00.000Z"
}
],
"has_more": false
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, rate_limited, internal_error
POST /v1/webhook-endpoints
Criar um endpoint de webhook
Cria o endpoint e devolve o segredo whsec_… **só nesta resposta**: guarde-o para conferir a assinatura (Torqyn-Signature). A URL precisa ser https e resolver para um endereço público. A versão do corpo fica presa à versão desta requisição.
Corpo
| Campo | Tipo | Descrição |
|---|---|---|
name | string | |
url | string | |
events | array | Os eventos que o endpoint recebe, ou ["*"] para todos (inclusive os que vierem) |
customer_fields opcional | array | Campos do contato que vão no corpo: nome, telefone, email. Vazio: só os ids. Documento nunca sai. |
Resposta 201
{
"object": "webhook_endpoint",
"id": "4d2c1b0a-9f8e-4d7c-8b6a-5f4e3d2c1b0a",
"name": "Meu sistema",
"url": "https://api.seusistema.com.br/torqyn",
"events": [
"message.delivered",
"message.failed",
"appointment.created"
],
"customer_fields": [],
"api_version": "2026-10-01",
"status": "active",
"pause_reason": null,
"previous_secret_valid_until": null,
"livemode": false,
"created_at": "2026-10-01T12:00:00.000Z",
"secret": "whsec_6q2Jb0xVn3cKp9sT1mRw4yZa7uEo5iLd"
}
Erros possíveis: invalid_request, invalid_version, idempotency_key_required, unauthorized, forbidden_scope, idempotency_key_in_use, payload_too_large, unsupported_media_type, idempotency_key_reused, rate_limited, internal_error
GET /v1/webhook-endpoints/{id}
Ler um endpoint de webhook
O endpoint, sem o segredo.
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
id | caminho | string |
Resposta 200
{
"object": "webhook_endpoint",
"id": "4d2c1b0a-9f8e-4d7c-8b6a-5f4e3d2c1b0a",
"name": "Meu sistema",
"url": "https://api.seusistema.com.br/torqyn",
"events": [
"message.delivered",
"message.failed",
"appointment.created"
],
"customer_fields": [],
"api_version": "2026-10-01",
"status": "active",
"pause_reason": null,
"previous_secret_valid_until": null,
"livemode": false,
"created_at": "2026-10-01T12:00:00.000Z"
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, not_found, rate_limited, internal_error
PATCH /v1/webhook-endpoints/{id}
Mudar um endpoint de webhook
Troca nome, URL, eventos ou campos do contato. enabled: true religa um endpoint desligado. Para pausar e reativar, use as rotas próprias; para trocar o segredo, rotate-secret.
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
id | caminho | string |
Corpo
| Campo | Tipo | Descrição |
|---|---|---|
name opcional | string | |
url opcional | string | |
events opcional | array | Os eventos que o endpoint recebe, ou ["*"] para todos (inclusive os que vierem) |
customer_fields opcional | array | Campos do contato que vão no corpo: nome, telefone, email. Vazio: só os ids. Documento nunca sai. |
enabled opcional | boolean |
Resposta 200
{
"object": "webhook_endpoint",
"id": "4d2c1b0a-9f8e-4d7c-8b6a-5f4e3d2c1b0a",
"name": "Meu sistema",
"url": "https://api.seusistema.com.br/torqyn",
"events": [
"message.delivered",
"message.failed",
"appointment.created"
],
"customer_fields": [],
"api_version": "2026-10-01",
"status": "active",
"pause_reason": null,
"previous_secret_valid_until": null,
"livemode": false,
"created_at": "2026-10-01T12:00:00.000Z"
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, not_found, payload_too_large, unsupported_media_type, rate_limited, internal_error
DELETE /v1/webhook-endpoints/{id}
Desligar um endpoint de webhook
Desliga o endpoint: para de receber eventos, e o histórico de entregas fica. Religue com PATCH e enabled: true.
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
id | caminho | string |
Resposta 200
{
"object": "webhook_endpoint",
"id": "4d2c1b0a-9f8e-4d7c-8b6a-5f4e3d2c1b0a",
"name": "Meu sistema",
"url": "https://api.seusistema.com.br/torqyn",
"events": [
"message.delivered",
"message.failed",
"appointment.created"
],
"customer_fields": [],
"api_version": "2026-10-01",
"status": "disabled",
"pause_reason": null,
"previous_secret_valid_until": null,
"livemode": false,
"created_at": "2026-10-01T12:00:00.000Z"
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, not_found, rate_limited, internal_error
POST /v1/webhook-endpoints/{id}/pause
Pausar um endpoint
As entregas param e esperam na fila até reativar (útil durante uma manutenção do seu lado).
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
id | caminho | string |
Resposta 200
{
"object": "webhook_endpoint",
"id": "4d2c1b0a-9f8e-4d7c-8b6a-5f4e3d2c1b0a",
"name": "Meu sistema",
"url": "https://api.seusistema.com.br/torqyn",
"events": [
"message.delivered",
"message.failed",
"appointment.created"
],
"customer_fields": [],
"api_version": "2026-10-01",
"status": "paused",
"pause_reason": "pausado pelo dono",
"previous_secret_valid_until": null,
"livemode": false,
"created_at": "2026-10-01T12:00:00.000Z"
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, not_found, rate_limited, internal_error
POST /v1/webhook-endpoints/{id}/resume
Reativar um endpoint
Volta a entregar o que ficou na fila, inclusive depois da pausa automática por falhas seguidas.
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
id | caminho | string |
Resposta 200
{
"object": "webhook_endpoint",
"id": "4d2c1b0a-9f8e-4d7c-8b6a-5f4e3d2c1b0a",
"name": "Meu sistema",
"url": "https://api.seusistema.com.br/torqyn",
"events": [
"message.delivered",
"message.failed",
"appointment.created"
],
"customer_fields": [],
"api_version": "2026-10-01",
"status": "active",
"pause_reason": null,
"previous_secret_valid_until": null,
"livemode": false,
"created_at": "2026-10-01T12:00:00.000Z"
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, not_found, rate_limited, internal_error
POST /v1/webhook-endpoints/{id}/rotate-secret
Trocar o segredo de um endpoint
Gera um segredo novo, devolvido só nesta resposta. Por 24 h cada entrega leva as duas assinaturas (v1=<novo>,v1=<anterior>): troque o segredo no seu sistema nesse meio-tempo, sem perder nenhum evento.
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
id | caminho | string |
Resposta 200
{
"object": "webhook_secret",
"secret": "whsec_9aT3kLp0Qz7xVb2Nc5Rm8Wd1Ye4Hf6Us",
"previous_secret_valid_until": "2026-10-02T12:00:00.000Z"
}
Erros possíveis: invalid_request, invalid_version, idempotency_key_required, unauthorized, forbidden_scope, not_found, idempotency_key_in_use, idempotency_key_reused, rate_limited, internal_error
POST /v1/webhook-endpoints/{id}/test
Mandar um evento de teste
Manda agora um evento ping, assinado como os outros, e devolve o que o seu servidor respondeu. Não conta para a pausa por falhas.
Parâmetros
| Nome | Onde | Descrição |
|---|---|---|
id | caminho | string |
Resposta 200
{
"object": "webhook_test",
"ok": true,
"status": 200,
"duration_ms": 84,
"error": null
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, not_found, rate_limited, internal_error
Eventos
POST /v1/events
Mandar um evento
Manda um evento de um tipo cadastrado para um contato. Com a janela de 24 h do WhatsApp aberta e a IA atendendo, a IA escreve seguindo a instrução do tipo; senão sai o modelo aprovado do tipo. O que impede o envio (sem telefone, sem canal, fora da janela sem modelo, modelo não aprovado, sem consentimento de marketing) volta com status: failed e o motivo. Quem pediu para sair nunca recebe: a resposta é opted_out e nada sai. O mesmo evento duas vezes, com a mesma Idempotency-Key, sai uma vez.
Corpo
| Campo | Tipo | Descrição |
|---|---|---|
type | string | A chave do tipo de evento, como cadastrada |
contact | object | |
data opcional | object | Os valores das variáveis do tipo; datas em ISO 8601 |
Resposta 202
{
"object": "event",
"type": "consulta_amanha",
"contact": {
"external_id": "fam-123"
},
"status": "delivered",
"reason": null,
"detail": null,
"via": "template",
"channel": "whatsapp",
"conversation_id": "9b2f4c1e-7d3a-4e5f-8a6b-1c2d3e4f5a6b"
}
Erros possíveis: invalid_request, invalid_version, idempotency_key_required, unauthorized, forbidden_scope, idempotency_key_in_use, payload_too_large, unsupported_media_type, idempotency_key_reused, rate_limited, internal_error
GET /v1/event-types
Listar os tipos de evento
Os tipos de evento cadastrados nesta conta, com as variáveis que cada um espera. Cadastrar e mudar é no painel, em Configurações → Desenvolvedores.
Resposta 200
{
"object": "list",
"data": [
{
"object": "event_type",
"type": "consulta_amanha",
"description": "Lembrete da consulta do dia seguinte",
"variables": [
{
"name": "data_consulta",
"type": "date",
"example": "2026-10-02T15:00:00-03:00",
"required": true
},
{
"name": "medico",
"type": "text",
"example": "Dra. Ana",
"required": true
}
],
"speaks_with": [
"ai",
"template"
]
}
]
}
Erros possíveis: invalid_request, invalid_version, unauthorized, forbidden_scope, rate_limited, internal_error