---
title: "API de WhatsApp em PHP com cURL e Guzzle | D-API"
description: "Integre a API de WhatsApp em PHP puro com cURL ou Guzzle, receba o webhook via php://input e rode tudo em hospedagem compartilhada ou em sistemas legados."
url: https://d-api.cloud/api-whatsapp-php
---

Por linguagem

# API de WhatsApp em PHP: cURL, Guzzle e webhook em qualquer hospedagem

Para usar a API de WhatsApp em PHP você não precisa de framework nem de servidor dedicado: uma função com cURL envia a mensagem e um arquivo que lê php://input recebe o webhook. É o caminho para sistemas legados e hospedagens compartilhadas, onde não dá para instalar worker nem abrir porta.

Por Time de engenharia D-APIAtualizado em 24 de set de 20266 min de leitura

## O cenário: sistema antigo, hospedagem simples

Muito sistema de gestão, loja e área do cliente no Brasil ainda roda em PHP procedural, em uma hospedagem compartilhada com cPanel, sem acesso root. Reescrever para colocar WhatsApp não é opção. A boa notícia é que a [API de WhatsApp](https://d-api.cloud/) da D-API é REST: não exige biblioteca, processo em segundo plano nem conexão permanente com o WhatsApp do lado do seu servidor. Quem mantém a conexão com o número é a D-API; o seu PHP só faz requisições HTTPS e recebe requisições HTTPS.

O que o servidor precisa ter:

-   extensão cURL ativa, presente em praticamente toda hospedagem;
-   saída para `https://api.d-api.cloud` na porta 443;
-   um endereço público com HTTPS para o arquivo do webhook;
-   acesso ao cron do painel, se você quiser processar eventos em lote.

## Enviar mensagem com cURL puro

A função abaixo cabe em um arquivo `dapi.php` e pode ser incluída em qualquer tela do sistema. Ela define tempo máximo de conexão e de resposta, trata falha de rede e converte a resposta de erro da API, que chega como `{"success": false, "error": "...", "statusCode": 400}`, em exceção.

```
<?php
// dapi.php
define('DAPI_API_KEY', getenv('DAPI_API_KEY'));

function dapi_post(string $caminho, array $payload): array
{
    $ch = curl_init('https://api.d-api.cloud' . $caminho);
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 3,   // segundos para abrir a conexão
        CURLOPT_TIMEOUT        => 10,  // segundos para a requisição inteira
        CURLOPT_HTTPHEADER     => [
            'Authorization: ' . DAPI_API_KEY,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS     => json_encode($payload),
    ]);

    $corpo = curl_exec($ch);
    if ($corpo === false) {
        $erro = curl_error($ch);
        curl_close($ch);
        throw new RuntimeException('Falha de rede com a D-API: ' . $erro);
    }

    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);
    $json = json_decode($corpo, true) ?: [];

    if ($status >= 400) {
        throw new RuntimeException($json['error'] ?? 'HTTP ' . $status, $status);
    }
    return $json;
}

// uso
dapi_post('/api/v1/messages/send/text', [
    'sessionId' => 'loja',
    'to'        => '5511999999999',
    'text'      => 'Recebemos seu pedido 4812. Obrigado!',
]);
```

Trocando o caminho, a mesma função envia imagem com legenda: use `/api/v1/messages/send/image` com os campos `image` (URL pública ou base64) e `caption`. O timeout é o que impede que a página do sistema fique carregando se a rede oscilar. Em hospedagem compartilhada o PHP costuma ter limite de execução baixo, e uma chamada sem timeout pode derrubar a requisição do usuário inteira.

## A mesma chamada com Guzzle

Se o projeto já usa Composer, o Guzzle deixa o código mais legível e separa com clareza erro de rede de erro devolvido pela API.

```
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\ConnectException;

$dapi = new Client([
    'base_uri'        => 'https://api.d-api.cloud',
    'timeout'         => 10,
    'connect_timeout' => 3,
    'headers'         => ['Authorization' => getenv('DAPI_API_KEY')],
]);

try {
    $dapi->post('/api/v1/messages/send/text', [
        'json' => ['sessionId' => 'loja', 'to' => '5511999999999', 'text' => 'Pedido enviado.'],
    ]);
} catch (BadResponseException $e) {
    $erro = json_decode((string) $e->getResponse()->getBody(), true);
    error_log('D-API recusou: ' . ($erro['error'] ?? $e->getMessage()));
} catch (ConnectException $e) {
    error_log('D-API inacessível ou timeout: ' . $e->getMessage());
}
```

`BadResponseException` cobre respostas 4xx e 5xx. `ConnectException` cobre DNS, conexão recusada e tempo esgotado. Na primeira, reveja o payload; na segunda, vale tentar de novo mais tarde.

## Receber o webhook com php://input

O webhook chega como POST com corpo JSON. Em PHP isso significa ler `php://input`: o `$_POST` vem vazio porque não é um formulário. Em hospedagem compartilhada, o jeito mais seguro de lidar com eventos é gravar primeiro e processar depois, porque o arquivo precisa responder rápido e o PHP-FPM do provedor costuma ter poucos processos disponíveis.

```
<?php
// webhook-whatsapp.php  (URL cadastrada: https://seusite.com.br/webhook-whatsapp.php?token=SEGREDO)
require __DIR__ . '/config.php';

if (!hash_equals(WEBHOOK_TOKEN, $_GET['token'] ?? '')) {
    http_response_code(401);
    exit;
}

$bruto  = file_get_contents('php://input');
$evento = json_decode($bruto, true);
if (!is_array($evento) || !isset($evento['event'])) {
    http_response_code(400);
    exit;
}

// chave única evita gravar duas vezes o mesmo evento reenviado
$chave = $evento['data']['id'] ?? $evento['traceId'];
$pdo->prepare(
    'INSERT IGNORE INTO whatsapp_eventos (chave, evento, sessao, payload, criado_em)
     VALUES (?, ?, ?, ?, NOW())'
)->execute([$chave, $evento['event'], $evento['sessionId'], $bruto]);

http_response_code(200);
echo 'ok';
```

A tabela `whatsapp_eventos` precisa de índice único em `chave`; é ele que faz o `INSERT IGNORE` descartar repetições. O webhook não tem assinatura, então o token na query string é a proteção do endpoint. Um script agendado no cron, rodando a cada minuto, lê os eventos pendentes e faz o trabalho mais demorado: responder o cliente, atualizar o pedido, avisar o financeiro.

Nesse script, marque cada linha como processada só depois de concluir o trabalho e limite quantas linhas ele pega por execução, por exemplo 100. Assim uma fila acumulada não estoura o tempo máximo de execução do PHP configurado pelo provedor.

## Armadilhas comuns em servidor compartilhado

| Sintoma | Causa provável | O que fazer |
| --- | --- | --- |
| Eventos param de chegar depois de uma mudança no site | Regra do .htaccess redirecionando o arquivo e devolvendo 404 | Exclua o caminho do webhook das regras de rewrite. Respostas 404 e 410 encerram as tentativas |
| Webhook recebe 403 sem motivo aparente | Firewall de aplicação do provedor barrando POST com JSON | Peça ao suporte uma exceção para aquele arquivo |
| Mesma mensagem processada duas vezes | Resposta lenta fez a entrega ser repetida | Gravar e responder, processar no cron, índice único |
| Envio falha com erro de certificado | Pacote de CAs desatualizado no servidor | Atualizar o PHP ou o bundle de certificados, nunca desligar a verificação SSL |

## Onde isso se encaixa no seu sistema

Os pontos de entrada mais comuns em sistemas PHP antigos são o fechamento do pedido, a emissão do boleto e o agendamento. Uma chamada a `dapi_post` logo depois do `INSERT` que já existe resolve a maior parte. Para régua de cobrança, veja o guia de [cobrança pelo WhatsApp](https://d-api.cloud/api-whatsapp-cobranca). Se o seu projeto está em Laravel, o [guia de Laravel](https://d-api.cloud/api-whatsapp-laravel) mostra a versão com filas e notificações. Os detalhes de cada evento recebido estão em [webhook de WhatsApp](https://d-api.cloud/webhook-api-whatsapp).

A conexão usada nesses exemplos é a não oficial, pareada por QR Code, que não exige aprovação de template para mandar a primeira mensagem. As regras e cuidados desse modelo estão em [API não oficial de WhatsApp](https://d-api.cloud/api-nao-oficial-de-whatsapp), e os planos, cobrados por conexão, em [preços](https://d-api.cloud/pricing).

## Perguntas frequentes

### Funciona em hospedagem compartilhada?

Na maioria dos casos, sim. O envio precisa só da extensão cURL e de saída liberada para HTTPS. O webhook é um arquivo PHP comum acessível pela internet. Confirme com o provedor se requisições de saída não são bloqueadas.

### Preciso do Composer?

Não. O exemplo com cURL usa apenas funções nativas do PHP e cabe em um único arquivo incluído com require. O Composer só entra se você quiser usar o Guzzle ou já usa em outras partes do projeto.

### Qual versão mínima do PHP?

Os exemplos desta página rodam em PHP 7.4 ou superior. Se o sistema ainda estiver em uma versão mais antiga, a lógica é a mesma, mas será preciso remover as declarações de tipo das funções.

### Meu webhook recebe o POST mas $\_POST vem vazio. Por quê?

Porque o corpo chega como JSON, e o PHP só preenche $\_POST para formulários. Leia o corpo bruto com file\_get\_contents em php://input e decodifique com json\_decode.

### Posso usar o mesmo código em um Laravel?

Pode, mas o Laravel já tem o Http client, filas e notificações que deixam a integração mais organizada. Para esse caso existe um guia específico de Laravel.

Teste a API de WhatsApp da D-API3 dias grátis, sem cartão.[Começar trial grátis](https://app.d-api.cloud/register)

## Continue lendo

-   [Por linguagemAPI de WhatsApp em Laravel](https://d-api.cloud/api-whatsapp-laravel)
-   [Por linguagemAPI de WhatsApp em Java](https://d-api.cloud/api-whatsapp-java)
-   [Por linguagemAPI de WhatsApp em C# e .NET](https://d-api.cloud/api-whatsapp-csharp)
-   [Por linguagemAPI de WhatsApp em Go](https://d-api.cloud/api-whatsapp-golang)

## Teste a API de WhatsApp da D-API

Trial de 3 dias com acesso completo. Sem cartão, sem fidelidade.

[Começar trial grátis](https://app.d-api.cloud/register)[Ver documentação](https://docs.d-api.cloud/)
