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.

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.

Escopo contacts:read

Parâmetros

NomeOndeDescrição
external_idcaminhostring

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.

Escopo contacts:write

Parâmetros

NomeOndeDescrição
external_idcaminhostring

Corpo

CampoTipoDescrição
name opcionalstring | null
phone opcionalstring | null
email opcionalstring | null
tags opcionalarray

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).

Escopo consents:write · exige Idempotency-Key

Parâmetros

NomeOndeDescrição
external_idcaminhostring

Corpo

CampoTipoDescrição
channelstring (whatsapp, email, instagram, messenger, telegram)
purposestring (marketing, utility)
evidencestring

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.

Escopo consents:write

Parâmetros

NomeOndeDescrição
external_idcaminhostring
channelcaminhostring
purposecaminhostring

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.

Escopo events:read

Parâmetros

NomeOndeDescrição
limit opcionalqueryinteger
starting_after opcionalquerystring
type opcionalquerySó 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.

Escopo webhooks:read

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.

Escopo webhooks:write · exige Idempotency-Key

Corpo

CampoTipoDescrição
namestring
urlstring
eventsarrayOs eventos que o endpoint recebe, ou ["*"] para todos (inclusive os que vierem)
customer_fields opcionalarrayCampos 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.

Escopo webhooks:read

Parâmetros

NomeOndeDescrição
idcaminhostring

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.

Escopo webhooks:write

Parâmetros

NomeOndeDescrição
idcaminhostring

Corpo

CampoTipoDescrição
name opcionalstring
url opcionalstring
events opcionalarrayOs eventos que o endpoint recebe, ou ["*"] para todos (inclusive os que vierem)
customer_fields opcionalarrayCampos do contato que vão no corpo: nome, telefone, email. Vazio: só os ids. Documento nunca sai.
enabled opcionalboolean

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.

Escopo webhooks:write

Parâmetros

NomeOndeDescrição
idcaminhostring

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).

Escopo webhooks:write

Parâmetros

NomeOndeDescrição
idcaminhostring

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.

Escopo webhooks:write

Parâmetros

NomeOndeDescrição
idcaminhostring

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.

Escopo webhooks:write · exige Idempotency-Key

Parâmetros

NomeOndeDescrição
idcaminhostring

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.

Escopo webhooks:write

Parâmetros

NomeOndeDescrição
idcaminhostring

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.

Escopo events:write · exige Idempotency-Key

Corpo

CampoTipoDescrição
typestringA chave do tipo de evento, como cadastrada
contactobject
data opcionalobjectOs 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.

Escopo events:read

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