---
title: "Vários atendentes no mesmo número de WhatsApp: como fazer pela API"
description: "Um número, uma equipe atendendo — sem duas pessoas respondendo a mesma coisa. Como distribuir, travar a conversa por atendente e transferir sem repetir tudo."
url: "https://wame.api.br/blog/multiatendimento-mesmo-numero-whatsapp"
language: "pt-BR"
og:type: "article"
published_time: "2026-09-10"
modified_time: "2026-09-10"
author: "Raphael Serafim"
keywords: "vários atendentes mesmo número whatsapp, whatsapp multiatendimento, whatsapp api multiplos usuarios, distribuir atendimento whatsapp, transferir atendimento whatsapp, fila de atendimento whatsapp, bot para humano whatsapp"
reading_time: "9 min de leitura"
---

# Vários atendentes no mesmo número de WhatsApp: como fazer pela API

> Um número, uma equipe atendendo — sem duas pessoas respondendo a mesma coisa. Como distribuir, travar a conversa por atendente e transferir sem repetir tudo.

**O aplicativo do WhatsApp não foi feito para equipe.** Ele tem limite de dispositivos conectados, não distribui conversa, não registra quem respondeu o quê e não impede duas pessoas de responderem a mesma coisa.

Pela API o problema muda de natureza: as mensagens chegam no seu sistema, e **você** decide quem atende.

## O desenho

```
Cliente  →  WhatsApp  →  webhook  →  seu sistema
                                        ↓
                                   distribuição
                                        ↓
                            atendente 1, 2, 3…
                                        ↓
                            resposta  →  API  →  Cliente
```

Para o cliente, existe uma conversa só, com um número só. Toda a estrutura de equipe é invisível — e é assim que tem que ser.

## As tabelas mínimas

```sql
CREATE TABLE conversas (
  id             BIGSERIAL PRIMARY KEY,
  numero         VARCHAR(20) UNIQUE NOT NULL,
  atendente_id   BIGINT,                       -- NULL = na fila
  status         VARCHAR(20) DEFAULT 'aberta', -- aberta|pendente|fechada
  fila           VARCHAR(30) DEFAULT 'geral',
  atribuida_em   TIMESTAMPTZ,
  ultima_em      TIMESTAMPTZ
);

CREATE TABLE mensagens (
  id           BIGSERIAL PRIMARY KEY,
  conversa_id  BIGINT NOT NULL,
  direcao      VARCHAR(10) NOT NULL,    -- entrada|saida
  atendente_id BIGINT,                  -- quem respondeu
  texto        TEXT,
  message_id   VARCHAR(80) UNIQUE,      -- idempotência
  criada_em    TIMESTAMPTZ DEFAULT NOW()
);
```

O `numero UNIQUE` em `conversas` é o que garante uma conversa por cliente. O `message_id UNIQUE` evita mensagem duplicada quando o [webhook reentrega](/blog/webhook-producao-assinatura-retry-idempotencia).

## Receber e distribuir

```js
async function aoReceber(msg) {
  const conversa = await obterOuCriarConversa(msg.from);

  await db.mensagens.criar({
    conversa_id: conversa.id,
    direcao: 'entrada',
    texto: msg.text?.body,
    message_id: msg.id,          // duplicata falha aqui, e tudo bem
  });

  // Conversa já atribuída continua com quem está atendendo.
  // Trocar de atendente no meio é péssima experiência.
  if (conversa.atendente_id) {
    return notificarAtendente(conversa.atendente_id, conversa.id);
  }

  const atendente = await escolherAtendente(conversa.fila);
  if (!atendente) {
    await db.conversas.atualizar(conversa.id, { status: 'pendente' });
    return avisarForaDoHorario(msg.from);
  }

  await atribuir(conversa.id, atendente.id);
}
```

## A trava que evita resposta duplicada

Esta é a parte que dá problema em produção se for feita ingenuamente:

```js
// ERRADO: dois atendentes passam pelo "if" ao mesmo tempo
if (!conversa.atendente_id) {
  await db.conversas.atualizar(conversa.id, { atendente_id: meuId });
}

// CERTO: a condição está no próprio UPDATE, e o banco resolve
async function atribuir(conversaId, atendenteId) {
  const r = await db.query(
    `UPDATE conversas
        SET atendente_id = $1, atribuida_em = NOW(), status = 'aberta'
      WHERE id = $2 AND atendente_id IS NULL
      RETURNING id`,
    [atendenteId, conversaId],
  );
  return r.rowCount > 0;   // false = outro atendente pegou primeiro
}
```

O `WHERE atendente_id IS NULL` dentro do `UPDATE` é o que torna a operação atômica. Sem isso, dois atendentes abrem a mesma conversa e o cliente recebe duas respostas — que é a reclamação número um de quem monta multiatendimento na pressa.

## Distribuir com critério

```js
async function escolherAtendente(fila) {
  const disponiveis = await db.query(
    `SELECT a.id, COUNT(c.id) AS abertas
       FROM atendentes a
       LEFT JOIN conversas c
              ON c.atendente_id = a.id AND c.status = 'aberta'
      WHERE a.online = true AND $1 = ANY(a.filas)
      GROUP BY a.id
     HAVING COUNT(c.id) < a.limite_simultaneo
      ORDER BY abertas ASC, a.ultima_atribuicao ASC NULLS FIRST
      LIMIT 1`,
    [fila],
  );
  return disponiveis.rows[0] ?? null;
}
```

Duas coisas importam mais que o algoritmo:

**`limite_simultaneo`** — atendente com 15 conversas abertas não atende nenhuma bem. Um teto por pessoa protege a qualidade.

**Desempate por `ultima_atribuicao`** — sem ele, quem fecha rápido recebe tudo, e quem enrola fica livre. O desempate distribui de forma justa.

## Transferir sem fazer o cliente repetir

```js
async function transferir(conversaId, deId, paraId, motivo) {
  await db.conversas.atualizar(conversaId, {
    atendente_id: paraId,
    atribuida_em: new Date(),
  });

  await db.eventos.criar({
    conversa_id: conversaId, tipo: 'transferencia',
    de: deId, para: paraId, motivo,
  });

  // O que evita a pior frase do atendimento: "me conta de novo?"
  const resumo = await db.mensagens.ultimas(conversaId, 20);
  await notificarAtendente(paraId, { conversaId, resumo, motivo });

  await enviar(conversa.numero,
    'Vou te passar para quem cuida disso, já com tudo que conversamos aqui. 👍');
}
```

Avisar o cliente da transferência **e** entregar o histórico ao novo atendente. Faltando qualquer um dos dois, a transferência vira a experiência que todo mundo odeia.

## Do bot para o humano

O desenho mais eficiente combina os dois: o bot faz a triagem e resolve o repetitivo; a pessoa entra quando precisa.

```js
async function aoReceber(msg) {
  const conversa = await obterOuCriarConversa(msg.from);

  // Já está com humano? O bot não interfere mais. Ponto.
  if (conversa.atendente_id) return encaminharParaAtendente(conversa, msg);

  const r = await bot.responder(conversa.id, msg.text.body);

  if (r.querHumano) {
    const atendente = await escolherAtendente(r.fila ?? 'geral');
    if (atendente) {
      await atribuir(conversa.id, atendente.id);
      await notificarAtendente(atendente.id, {
        conversaId: conversa.id,
        resumo: r.resumoDaConversa,   // o bot entrega o contexto
      });
      return enviar(msg.from, 'Já te conecto com alguém do time. 👋');
    }
    await db.conversas.atualizar(conversa.id, { status: 'pendente' });
    return enviar(msg.from, 'Todo mundo está ocupado agora. Assim que liberar, te chamo aqui.');
  }

  await enviar(msg.from, r.texto);
}
```

**A primeira linha é a regra mais importante:** depois que a conversa foi para um humano, o bot não volta a responder. Bot que interrompe atendimento humano é pior que não ter bot.

Como montar o bot está em [chatbot de IA com a API da OpenAI](/blog/chatbot-ia-openai-whatsapp) — e o `[HUMANO]` daquele artigo é exatamente o `querHumano` daqui.

## Horário e fila de espera

```js
async function avisarForaDoHorario(numero) {
  if (dentroDoHorario()) {
    return enviar(numero, 'Recebemos sua mensagem! Já te respondo. 😊');
  }
  return enviar(numero,
    'Recebemos sua mensagem! Nosso atendimento é de segunda a sexta, das 8h às 18h. ' +
    'Respondo assim que abrirmos.');
}
```

Expectativa clara vale mais que resposta rápida. Cliente que sabe quando será atendido não manda mais cinco mensagens perguntando se tem alguém aí.

## Métricas que dizem alguma coisa

| Número | O que revela |
|---|---|
| Tempo até a primeira resposta | O que o cliente sente |
| Tempo até resolver | A eficiência real |
| Conversas por atendente | Distribuição justa |
| Taxa de transferência | Se a triagem está errando |
| Pendentes no pico | Se falta gente |

Taxa de transferência alta quase sempre significa fila mal desenhada, não atendente ruim.

## E se você já usa uma ferramenta pronta

Se a sua necessidade é atendimento e não um módulo dentro do seu produto, [integrar com o Chatwoot](/blog/integrar-wame-api-chatwoot) entrega caixa compartilhada, atribuição e histórico prontos.

Construir faz sentido quando o atendimento precisa viver **dentro** do sistema que você já vende — com o cliente, o pedido e o histórico na mesma tela. É o caso típico de quem [entrega software sob medida](/api-whatsapp-para-software-house).

## Conclusão

Multiatendimento não é recurso do WhatsApp: é o seu sistema decidindo quem responde. As peças são poucas — uma conversa por número, atribuição atômica, transferência com histórico e bot que sai de cena quando o humano entra.

A que mais economiza retrabalho é a trava atômica. É uma linha de SQL, e sem ela o cliente recebe duas respostas diferentes da mesma empresa.

## Perguntas frequentes

### Como colocar vários atendentes no mesmo número de WhatsApp?

Pela API. As mensagens chegam num webhook e o seu sistema decide quem atende, distribuindo as conversas entre os atendentes conectados. Todos respondem pelo mesmo número, e para o cliente existe uma conversa só — o que o aplicativo comum não permite.

### Qual o limite de atendentes num número?

Pela API não há o limite de dispositivos do aplicativo, porque quem conversa com o WhatsApp é o seu sistema, não os aparelhos. O limite prático passa a ser o volume de mensagens que o número comporta e a capacidade da sua equipe.

### Como evitar que dois atendentes respondam a mesma conversa?

Com trava de atribuição no banco. A conversa é atribuída a um atendente numa operação atômica, e a interface só permite responder quem está com ela. Sem isso, duas pessoas abrem a mesma conversa ao mesmo tempo e o cliente recebe duas respostas diferentes.

### Como transferir uma conversa entre atendentes?

Trocando a atribuição e preservando o histórico. O ponto importante é passar junto o resumo do que já foi tratado, para o cliente não precisar repetir. Transferência que faz o cliente contar tudo de novo é a maior reclamação de atendimento em qualquer canal.

### Dá para o bot atender primeiro e passar para o humano?

Sim, e é o desenho mais eficiente. O bot faz a triagem e resolve o que é repetitivo; quando identifica que precisa de gente, marca a conversa para atendimento humano e entrega o histórico ao atendente. O erro é o bot insistir depois de a pessoa já ter pedido um humano.
