Integração

API de WhatsApp para Laravel

Integre WhatsApp em aplicações Laravel com HTTP Client.

// Laravel — Http Facade
use Illuminate\Support\Facades\Http;

Http::withToken(config('services.zapixo.key'))
    ->post('https://zapixo.com.br/api/v1/messages/text', [
        'instance' => 'laravel-app',
        'number' => $telefone,
        'text' => $mensagem,
    ]);

No Laravel, o Zapixo cabe no que o framework já tem: o Http facade envia, uma rota de API recebe o webhook e um job na fila processa a mensagem fora da requisição. Nada para instalar além do próprio Laravel.

Se o projeto não usa Laravel, a guia de PHP mostra o mesmo com cURL.

O que dá para fazer com Laravel e WhatsApp

  • Notificação a partir de evento

    Pedido pago dispara um listener, que coloca o envio na fila sem travar a tela do usuário.

  • Atendimento no painel

    O webhook grava a mensagem, e a equipe responde pela tela do próprio sistema.

  • Disparo agendado

    O Scheduler monta a lista do dia e distribui os jobs no tempo, dentro do limite da API.

  • Bot com regra de negócio

    O job consulta os models e responde com o dado real: pedido, agenda, saldo.

Enviar mensagens pelo Laravel

  1. Configure em services.php

    Leia sempre por config(), nunca por env() fora dos arquivos de configuração: depois de php artisan config:cache, o env() passa a devolver null.

    // config/services.php
    'zapixo' => [
        'key'            => env('ZAPIXO_API_KEY'),
        'webhook_secret' => env('ZAPIXO_WEBHOOK_SECRET'),
    ],
  2. Envio com o Http facade

    withToken monta o header Authorization: Bearer sozinho:

    use Illuminate\Support\Facades\Http;
    
    $resposta = Http::withToken(config('services.zapixo.key'))
        ->timeout(15)
        ->post('https://zapixo.com.br/api/v1/messages/text', [
            'instance' => 'laravel-app',
            'number'   => preg_replace('/\D/', '', $telefone),
            'text'     => $mensagem,
        ]);
    
    if ($resposta->failed()) {
        report(new \RuntimeException("Zapixo {$resposta->status()}: {$resposta->body()}"));
    }
  3. Envie de dentro de um job

    Chamada HTTP no meio da requisição deixa o usuário esperando. No job, o 429 devolve o trabalho para a fila em vez de perder a mensagem (imports omitidos):

    class EnviarWhatsApp implements ShouldQueue
    {
        use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
        public int $tries = 5;
    
        public function __construct(public string $numero, public string $texto) {}
    
        public function handle(): void
        {
            $r = Http::withToken(config('services.zapixo.key'))
                ->timeout(15)
                ->post('https://zapixo.com.br/api/v1/messages/text', [
                    'instance' => 'laravel-app',
                    'number'   => $this->numero,
                    'text'     => $this->texto,
                ]);
    
            if ($r->status() === 429) {
                $this->release(60);
                return;
            }
    
            $r->throw();
        }
    }

Receber mensagens no Laravel

  1. Crie a rota de API

    Em routes/api.php — no Laravel 11 em diante, o arquivo é criado com php artisan install:api. Rota de API não passa pela verificação de CSRF, que recusaria o POST do Zapixo:

    use App\Http\Controllers\ZapixoWebhook;
    
    Route::post('/webhooks/zapixo', ZapixoWebhook::class);
  2. Cole a URL no painel

    Em Webhook de mensagens, com o segredo no fim: https://seuapp.com/api/webhooks/zapixo?chave=SEU_SEGREDO. O Zapixo não assina as requisições; o segredo na URL é a proteção.

  3. O controller

    Confere o segredo, filtra fromMe, põe o trabalho na fila e responde na hora:

    class ZapixoWebhook
    {
        public function __invoke(Request $request)
        {
            $segredo = config('services.zapixo.webhook_secret');
            abort_unless($segredo && hash_equals($segredo, (string) $request->query('chave')), 401);
    
            if ($request->input('event') === 'messages.upsert' && ! $request->boolean('data.key.fromMe')) {
                ProcessarMensagemZapixo::dispatch($request->input('data'));
            }
    
            return response()->noContent();
        }
    }
  4. O job, sem processar duas vezes

    Cache::add só grava se a chave não existir — é o jeito mais curto de ignorar a repetição de um webhook:

    public function handle(): void
    {
        $id = $this->data['key']['id'] ?? null;
        if (! $id || ! Cache::add("zapixo:msg:{$id}", true, now()->addDay())) {
            return; // já processada
        }
    
        $numero = explode('@', $this->data['key']['remoteJid'])[0];
        $msg    = $this->data['message'] ?? [];
        $texto  = $msg['conversation'] ?? ($msg['extendedTextMessage']['text'] ?? '');
    
        // a regra do seu negócio entra aqui
    }

Erros comuns e como resolver

O webhook responde 419
É a proteção de CSRF: a rota foi para routes/web.php. Mova para routes/api.php, ou exclua o caminho da verificação.
`env()` devolvendo null em produção
Com a configuração em cache, só config() enxerga os valores. Troque todo env('ZAPIXO_...') fora de config/ por config('services.zapixo...').
Job rodando dentro da requisição
Com QUEUE_CONNECTION=sync, o job roda na hora e o webhook passa dos 10 segundos. Use database ou redis e deixe um php artisan queue:work rodando.
Mensagem processada duas vezes
Se a rota não devolver 2xx em até 10 segundos, o Zapixo tenta de novo, até 3 vezes. O Cache::add com key.id do exemplo resolve.
429 em disparo para lista
60 requisições por minuto por chave. Distribua os jobs com ->delay(now()->addSeconds($i)) e deixe o release do job cuidar do que passar do limite.
Erros 401, 404 e 502
401: chave errada ou vazia — confira config('services.zapixo.key'). 404: instance não bate com o nome no painel. 502: a entrega ao WhatsApp falhou, e o motivo vem em details.

Perguntas frequentes

Precisa de pacote para usar o Zapixo no Laravel?

Não. O Http facade, a fila e as rotas do próprio Laravel resolvem.

Funciona no Laravel 10 e no 11?

Funciona. A diferença é a rota de API: do Laravel 11 em diante, routes/api.php é criado com php artisan install:api.

Qual driver de fila usar?

Database basta para começar; Redis quando o volume crescer. O que não pode é sync em produção.

O webhook do Zapixo tem assinatura?

Não. Proteja a rota com um segredo na URL e confira com hash_equals.

O trial tem limite de mensagens?

Tem. Ao atingir o limite, a API responde 402 com o link para assinar.

Leia também