Requirements
- PHP 8.0 or higher
- Composer
- ext-json
- ext-curl
Installation
Install the EnviaAI SDK using Composer:Copy
composer require enviaai/sdk
Quick Start
Copy
<?php
require 'vendor/autoload.php';
use EnviaAI\EnviaAI;
$client = new EnviaAI('your-api-key');
// Send a message
$response = $client->messages->send([
'instanceId' => 'inst_abc123',
'to' => '5511999999999',
'message' => 'Hello from EnviaAI!'
]);
echo "Message ID: " . $response->messageId;
Configuration
Client Options
Copy
use EnviaAI\EnviaAI;
$client = new EnviaAI('your-api-key', [
'baseUrl' => 'https://api.enviaai.app', // Optional
'timeout' => 30, // Optional, timeout in seconds
'maxRetries' => 3 // Optional
]);
Environment Variables
The SDK can read from environment variables:Copy
export ENVIAAI_API_KEY=your-api-key
export ENVIAAI_BASE_URL=https://api.enviaai.app
Copy
// API key is read from ENVIAAI_API_KEY
$client = new EnviaAI();
Messages API
Send Text Message
Copy
$response = $client->messages->send([
'instanceId' => 'inst_abc123',
'to' => '5511999999999',
'message' => 'Hello! How can I help you?'
]);
Send Media Message
Copy
// Image
$imageResponse = $client->messages->send([
'instanceId' => 'inst_abc123',
'to' => '5511999999999',
'mediaUrl' => 'https://example.com/image.jpg',
'mediaType' => 'image',
'caption' => 'Check this out!'
]);
// Document
$docResponse = $client->messages->send([
'instanceId' => 'inst_abc123',
'to' => '5511999999999',
'mediaUrl' => 'https://example.com/document.pdf',
'mediaType' => 'document',
'filename' => 'invoice.pdf'
]);
// Audio
$audioResponse = $client->messages->send([
'instanceId' => 'inst_abc123',
'to' => '5511999999999',
'mediaUrl' => 'https://example.com/audio.mp3',
'mediaType' => 'audio'
]);
Get Message Status
Copy
$status = $client->messages->getStatus('msg_xyz789');
echo $status->status; // 'sent', 'delivered', 'read', 'failed'
List Chats
Copy
$chats = $client->messages->getChats([
'instanceId' => 'inst_abc123',
'limit' => 20,
'unreadOnly' => true
]);
foreach ($chats->data as $chat) {
echo "{$chat->contactName}: {$chat->unreadCount} unread\n";
}
Get Chat Messages
Copy
$messages = $client->messages->getChatMessages([
'instanceId' => 'inst_abc123',
'chatId' => 'chat_def456',
'limit' => 50
]);
Instances API
List Instances
Copy
$instances = $client->instances->list();
foreach ($instances->data as $instance) {
echo "{$instance->name}: {$instance->status}\n";
}
Get Instance Status
Copy
$status = $client->instances->getStatus('inst_abc123');
if ($status->data->status === 'connected') {
echo "Connected as {$status->data->phone}";
} elseif ($status->data->status === 'qr_pending') {
echo "Scan QR: {$status->data->qrCode}";
}
Connect Instance
Copy
$client->instances->connect('inst_abc123');
// Poll for QR code
$status = $client->instances->getStatus('inst_abc123');
if ($status->data->qrCode) {
// Display QR code to user
}
Disconnect Instance
Copy
$client->instances->disconnect('inst_abc123');
Webhooks API
Create Webhook
Copy
$webhook = $client->webhooks->create([
'url' => 'https://myapp.com/webhooks/enviaai',
'events' => ['message.received', 'message.delivered'],
'secret' => 'my-webhook-secret'
]);
List Webhooks
Copy
$webhooks = $client->webhooks->list();
Update Webhook
Copy
$client->webhooks->update('wh_abc123', [
'events' => ['message.received', 'message.delivered', 'message.read']
]);
Delete Webhook
Copy
$client->webhooks->delete('wh_abc123');
Verify Webhook Signature
Copy
use EnviaAI\EnviaAI;
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_ENVIAAI_SIGNATURE'] ?? '';
$isValid = EnviaAI::verifyWebhookSignature(
$payload,
$signature,
'your-webhook-secret'
);
if (!$isValid) {
http_response_code(401);
exit('Invalid signature');
}
Error Handling
Copy
use EnviaAI\EnviaAI;
use EnviaAI\Exceptions\EnviaAIException;
use EnviaAI\Exceptions\AuthenticationException;
use EnviaAI\Exceptions\RateLimitException;
use EnviaAI\Exceptions\ValidationException;
use EnviaAI\Exceptions\NotFoundException;
$client = new EnviaAI('your-api-key');
try {
$response = $client->messages->send([
'instanceId' => 'inst_abc123',
'to' => '5511999999999',
'message' => 'Hello!'
]);
} catch (RateLimitException $e) {
echo "Rate limited. Retry after {$e->getRetryAfter()} seconds";
} catch (AuthenticationException $e) {
echo "Invalid API key";
} catch (ValidationException $e) {
echo "Validation error: {$e->getMessage()}";
} catch (NotFoundException $e) {
echo "Resource not found";
} catch (EnviaAIException $e) {
echo "API Error: {$e->getErrorCode()} - {$e->getMessage()}";
}
Exception Types
| Exception Class | Description |
|---|---|
EnviaAIException | Base exception for all API errors |
AuthenticationException | Invalid or missing API key |
RateLimitException | Rate limit exceeded |
ValidationException | Invalid request parameters |
NotFoundException | Resource not found |
ServerException | Internal server error |
Pagination
Copy
// Manual pagination
$offset = 0;
$limit = 50;
$allChats = [];
do {
$response = $client->messages->getChats([
'instanceId' => 'inst_abc123',
'limit' => $limit,
'offset' => $offset
]);
$allChats = array_merge($allChats, $response->data);
$offset += $limit;
} while ($response->pagination->hasMore);
// Using iterator
foreach ($client->messages->iterateChats(['instanceId' => 'inst_abc123']) as $chat) {
echo $chat->contactName . "\n";
}
Logging
Enable debug logging with PSR-3 compatible loggers:Copy
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use EnviaAI\EnviaAI;
$logger = new Logger('enviaai');
$logger->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG));
$client = new EnviaAI('your-api-key', [
'logger' => $logger
]);
Examples
Laravel Webhook Handler
Copy
// routes/api.php
Route::post('/webhooks/enviaai', [WebhookController::class, 'handle']);
// app/Http/Controllers/WebhookController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use EnviaAI\EnviaAI;
class WebhookController extends Controller
{
private EnviaAI $client;
public function __construct()
{
$this->client = new EnviaAI(config('services.enviaai.key'));
}
public function handle(Request $request)
{
$signature = $request->header('X-EnviaAI-Signature');
if (!EnviaAI::verifyWebhookSignature(
$request->getContent(),
$signature,
config('services.enviaai.webhook_secret')
)) {
return response()->json(['error' => 'Invalid signature'], 401);
}
$event = $request->all();
if ($event['event'] === 'message.received') {
// Auto-reply
$this->client->messages->send([
'instanceId' => $event['data']['instanceId'],
'to' => $event['data']['from'],
'message' => "Thanks for your message! We'll get back to you soon."
]);
}
return response()->json(['status' => 'ok']);
}
}
// config/services.php
return [
'enviaai' => [
'key' => env('ENVIAAI_API_KEY'),
'webhook_secret' => env('ENVIAAI_WEBHOOK_SECRET'),
],
];
Symfony Webhook Handler
Copy
// src/Controller/WebhookController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use EnviaAI\EnviaAI;
class WebhookController extends AbstractController
{
private EnviaAI $client;
public function __construct()
{
$this->client = new EnviaAI($_ENV['ENVIAAI_API_KEY']);
}
#[Route('/webhooks/enviaai', methods: ['POST'])]
public function handle(Request $request): JsonResponse
{
$signature = $request->headers->get('X-EnviaAI-Signature');
if (!EnviaAI::verifyWebhookSignature(
$request->getContent(),
$signature,
$_ENV['ENVIAAI_WEBHOOK_SECRET']
)) {
return new JsonResponse(['error' => 'Invalid signature'], 401);
}
$event = json_decode($request->getContent(), true);
if ($event['event'] === 'message.received') {
$this->client->messages->send([
'instanceId' => $event['data']['instanceId'],
'to' => $event['data']['from'],
'message' => "Thanks for your message!"
]);
}
return new JsonResponse(['status' => 'ok']);
}
}
Bulk Message Sending
Copy
use EnviaAI\EnviaAI;
use GuzzleHttp\Promise\Utils;
$client = new EnviaAI('your-api-key');
$contacts = [
['phone' => '5511999999999', 'name' => 'John'],
['phone' => '5521988888888', 'name' => 'Jane'],
// ... more contacts
];
// Using Guzzle promises for concurrent requests
$promises = [];
foreach ($contacts as $contact) {
$promises[] = $client->messages->sendAsync([
'instanceId' => 'inst_abc123',
'to' => $contact['phone'],
'message' => "Hello {$contact['name']}! Check out our new products."
]);
}
$results = Utils::settle($promises)->wait();
$success = 0;
$failed = 0;
foreach ($results as $result) {
if ($result['state'] === 'fulfilled') {
$success++;
} else {
$failed++;
}
}
echo "Sent: $success, Failed: $failed\n";
Queue Integration (Laravel)
Copy
// app/Jobs/SendWhatsAppMessage.php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use EnviaAI\EnviaAI;
class SendWhatsAppMessage implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
private string $instanceId,
private string $to,
private string $message
) {}
public function handle(): void
{
$client = new EnviaAI(config('services.enviaai.key'));
$client->messages->send([
'instanceId' => $this->instanceId,
'to' => $this->to,
'message' => $this->message
]);
}
public function failed(\Throwable $exception): void
{
// Log failure
\Log::error('WhatsApp message failed', [
'to' => $this->to,
'error' => $exception->getMessage()
]);
}
}
// Usage
SendWhatsAppMessage::dispatch('inst_abc123', '5511999999999', 'Hello!');