Installation
Add the EnviaAI SDK to your project using Maven or Gradle.Copy
<dependency>
<groupId>com.enviaai</groupId>
<artifactId>enviaai-java</artifactId>
<version>1.0.0</version>
</dependency>
Requirements
- Java 11 or higher
- Gson (included as dependency)
Quick Start
Copy
import com.enviaai.EnviaAI;
import com.enviaai.models.*;
public class QuickStart {
public static void main(String[] args) {
// Initialize client
EnviaAI client = new EnviaAI("your-api-key");
// Send a message
SendMessageResponse response = client.messages().send(
SendMessageRequest.builder()
.instanceId("inst_abc123")
.to("5511999999999")
.text("Hello from EnviaAI!")
.build()
);
System.out.println("Message sent: " + response.getMessageId());
}
}
Configuration
Basic Configuration
Copy
// Simple initialization with API key
EnviaAI client = new EnviaAI("your-api-key");
Advanced Configuration
Copy
// Using the builder pattern
EnviaAI client = EnviaAI.builder()
.apiKey("your-api-key")
.baseUrl("https://api.enviaai.app") // Optional
.timeout(60000) // 60 seconds
.addHeader("X-Custom-Header", "value")
.build();
Environment Variables
Copy
// Load API key from environment
String apiKey = System.getenv("ENVIAAI_API_KEY");
EnviaAI client = new EnviaAI(apiKey);
Sending Messages
Text Message
Copy
SendMessageResponse response = client.messages().send(
SendMessageRequest.builder()
.instanceId("inst_abc123")
.to("5511999999999")
.text("Hello! How can I help you?")
.build()
);
System.out.println("Message ID: " + response.getMessageId());
System.out.println("Status: " + response.getStatus());
Image Message
Copy
SendMessageResponse response = client.messages().send(
SendMessageRequest.builder()
.instanceId("inst_abc123")
.to("5511999999999")
.mediaUrl("https://example.com/image.jpg")
.mediaType(MessageType.IMAGE)
.caption("Check out our product!")
.build()
);
Document
Copy
SendMessageResponse response = client.messages().send(
SendMessageRequest.builder()
.instanceId("inst_abc123")
.to("5511999999999")
.mediaUrl("https://example.com/invoice.pdf")
.mediaType(MessageType.DOCUMENT)
.filename("Invoice-2026-001.pdf")
.caption("Here is your invoice")
.build()
);
Reply to Message
Copy
SendMessageResponse response = client.messages().send(
SendMessageRequest.builder()
.instanceId("inst_abc123")
.to("5511999999999")
.text("Thanks for your question!")
.quotedMessageId("msg_original123")
.build()
);
Managing Instances
List Instances
Copy
import java.util.List;
List<Instance> instances = client.instances().list();
for (Instance instance : instances) {
System.out.printf("%s: %s (%s)%n",
instance.getId(),
instance.getName(),
instance.getStatus()
);
}
Filter by Status
Copy
List<Instance> connected = client.instances().list(
InstanceStatus.CONNECTED
);
Get Instance Status
Copy
InstanceStatusResponse status = client.instances().getStatus("inst_abc123");
System.out.println("Status: " + status.getStatus());
System.out.println("Phone: " + status.getPhone());
if (status.getStats() != null) {
System.out.println("Messages sent today: " + status.getStats().getMessagesSent());
System.out.println("Messages received: " + status.getStats().getMessagesReceived());
}
Connect Instance (QR Code)
Copy
QRCodeResponse qr = client.instances().connect("inst_abc123");
if (qr.getQrCode() != null) {
System.out.println("Scan QR code: " + qr.getQrCode());
System.out.println("Expires at: " + qr.getExpiresAt());
}
Disconnect Instance
Copy
client.instances().disconnect("inst_abc123");
Chat History
List Chats
Copy
import java.util.List;
List<Chat> chats = client.messages().getChats("inst_abc123");
for (Chat chat : chats) {
System.out.printf("%s - %s (Unread: %d)%n",
chat.getPhone(),
chat.getContactName(),
chat.getUnreadCount()
);
}
Get Messages from Chat
Copy
List<Message> messages = client.messages().getMessages(
"inst_abc123",
"5511999999999",
50 // limit
);
for (Message msg : messages) {
String direction = msg.getDirection() == MessageDirection.INBOUND ? "←" : "→";
System.out.printf("%s %s: %s%n",
direction,
msg.getTimestamp(),
msg.getContent()
);
}
Error Handling
Exception Types
Copy
import com.enviaai.EnviaAIException;
try {
client.messages().send(request);
} catch (EnviaAIException.AuthenticationException e) {
System.err.println("Invalid API key: " + e.getMessage());
} catch (EnviaAIException.RateLimitException e) {
System.err.println("Rate limited. Retry after: " + e.getRetryAfter() + "s");
} catch (EnviaAIException.ValidationException e) {
System.err.println("Invalid request: " + e.getMessage());
} catch (EnviaAIException.NotFoundException e) {
System.err.println("Resource not found: " + e.getMessage());
} catch (EnviaAIException.ServerException e) {
System.err.println("Server error: " + e.getMessage());
} catch (EnviaAIException e) {
System.err.println("API error: " + e.getCode() + " - " + e.getMessage());
}
Error Response Structure
Copy
try {
client.messages().send(request);
} catch (EnviaAIException e) {
System.err.println("Error code: " + e.getCode());
System.err.println("Message: " + e.getMessage());
System.err.println("Request ID: " + e.getRequestId());
System.err.println("HTTP Status: " + e.getStatusCode());
}
Webhook Handler
Spring Boot
Copy
import org.springframework.web.bind.annotation.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.HexFormat;
@RestController
@RequestMapping("/webhooks")
public class WebhookController {
private static final String WEBHOOK_SECRET = System.getenv("WEBHOOK_SECRET");
@PostMapping("/enviaai")
public ResponseEntity<String> handleWebhook(
@RequestBody String payload,
@RequestHeader("X-EnviaAI-Signature") String signature) {
// Verify signature
if (!verifySignature(payload, signature)) {
return ResponseEntity.status(401).body("Invalid signature");
}
// Parse event
WebhookEvent event = parseEvent(payload);
// Process asynchronously
processEventAsync(event);
// Respond quickly
return ResponseEntity.ok("OK");
}
private boolean verifySignature(String payload, String signature) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(WEBHOOK_SECRET.getBytes(), "HmacSHA256"));
String expected = "sha256=" + HexFormat.of().formatHex(mac.doFinal(payload.getBytes()));
return signature.equals(expected);
} catch (Exception e) {
return false;
}
}
}
Jakarta EE / JAX-RS
Copy
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Response;
@Path("/webhooks")
public class WebhookResource {
@POST
@Path("/enviaai")
@Consumes("application/json")
public Response handleWebhook(
String payload,
@HeaderParam("X-EnviaAI-Signature") String signature) {
if (!verifySignature(payload, signature)) {
return Response.status(401).entity("Invalid signature").build();
}
// Process event
processEvent(payload);
return Response.ok("OK").build();
}
}
Async Operations
CompletableFuture (Manual)
Copy
import java.util.concurrent.CompletableFuture;
CompletableFuture.supplyAsync(() -> {
return client.messages().send(request);
}).thenAccept(response -> {
System.out.println("Message sent: " + response.getMessageId());
}).exceptionally(e -> {
System.err.println("Failed: " + e.getMessage());
return null;
});
Bulk Messaging
Copy
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
List<String> recipients = List.of(
"5511999999991",
"5511999999992",
"5511999999993"
);
List<CompletableFuture<SendMessageResponse>> futures = recipients.stream()
.map(phone -> CompletableFuture.supplyAsync(() -> {
return client.messages().send(
SendMessageRequest.builder()
.instanceId("inst_abc123")
.to(phone)
.text("Hello! Special offer just for you.")
.build()
);
}))
.collect(Collectors.toList());
// Wait for all to complete
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
// Get results
for (CompletableFuture<SendMessageResponse> future : futures) {
try {
SendMessageResponse response = future.get();
System.out.println("Sent: " + response.getMessageId());
} catch (Exception e) {
System.err.println("Failed: " + e.getMessage());
}
}
Best Practices
Use Environment Variables
Never hardcode API keys. Use environment variables or secret managers.
Handle Errors Gracefully
Implement proper error handling with retries for rate limits.
Use Connection Pooling
The SDK uses connection pooling internally. Reuse the client instance.
Verify Webhooks
Always verify webhook signatures before processing events.
Singleton Pattern
Copy
public class EnviaAIClient {
private static volatile EnviaAI instance;
public static EnviaAI getInstance() {
if (instance == null) {
synchronized (EnviaAIClient.class) {
if (instance == null) {
instance = new EnviaAI(System.getenv("ENVIAAI_API_KEY"));
}
}
}
return instance;
}
}
// Usage
EnviaAI client = EnviaAIClient.getInstance();