Sendry Docs
SDKs

Java SDK

Official Java SDK for the Sendry email API.

Installation

Maven

<dependency>
  <groupId>dev.sendry</groupId>
  <artifactId>sendry</artifactId>
  <version>0.1.0</version>
</dependency>

Gradle (Kotlin DSL)

dependencies {
    implementation("dev.sendry:sendry:0.1.0")
}

Requires Java 17 or newer. Built on the JDK's native java.net.http.HttpClient — no third-party HTTP layer.

Initialization

import dev.sendry.Sendry;
import dev.sendry.SendryConfig;
import java.time.Duration;

// Simple initialization
Sendry sendry = new Sendry(System.getenv("SENDRY_API_KEY"));

// With options
SendryConfig cfg = SendryConfig.builder("sn_live_...")
    .baseUrl("https://api.sendry.online")
    .timeout(Duration.ofSeconds(30))
    .retries(2)
    .build();
Sendry sendry = new Sendry(cfg);

Emails

import dev.sendry.types.SendEmailResponse;
import dev.sendry.types.Email;
import dev.sendry.types.PaginatedResponse;
import java.util.List;
import java.util.Map;

// Send a single email
SendEmailResponse res = sendry.emails.send(
    "from", "Acme <hello@acme.com>",
    "to", "user@example.com",
    "subject", "Welcome!",
    "html", "<h1>Welcome to Acme!</h1>"
);
System.out.println(res.id() + " " + res.status());

// Send to multiple recipients
sendry.emails.send(
    "from", "hello@acme.com",
    "to", List.of("alice@example.com", "bob@example.com"),
    "subject", "Team announcement",
    "html", "<p>Big news...</p>"
);

// Send using a template
sendry.emails.send(
    "from", "hello@acme.com",
    "to", "user@example.com",
    "template_id", "tmpl_abc123",
    "variables", Map.of("name", "Alice", "plan", "Pro")
);

// Batch send
sendry.emails.sendBatch(Map.of(
    "from", "hello@acme.com",
    "subject", "Order update",
    "emails", List.of(
        Map.of("to", "alice@example.com", "html", "<p>Alice, shipped!</p>"),
        Map.of("to", "bob@example.com",   "html", "<p>Bob, shipped!</p>")
    )
));

// Get email
Email email = sendry.emails.get("em_abc123");
System.out.println(email.status());

// List emails
PaginatedResponse<Email> page = sendry.emails.list(
    Map.of("limit", 25, "status", "delivered")
);
for (Email e : page.data()) {
    System.out.println(e.id() + " " + e.subject());
}

// Cancel
sendry.emails.cancel("em_abc123");

Domains

import dev.sendry.types.Domain;

// Add a domain
Domain domain = sendry.domains.create("acme.com");
for (Domain.DnsRecord r : domain.dnsRecords()) {
    System.out.printf("%s %s → %s%n", r.type(), r.host(), r.value());
}

// Verify
sendry.domains.verify("dom_abc123");

// List, get, delete
sendry.domains.list();
sendry.domains.get("dom_abc123");
sendry.domains.remove("dom_abc123");

Contacts & Audiences

import dev.sendry.types.Contact;
import dev.sendry.types.Audience;

Contact contact = sendry.contacts.create(Map.of(
    "email", "jane@example.com",
    "first_name", "Jane",
    "last_name", "Doe"
));

Audience audience = sendry.audiences.create(Map.of(
    "name", "Newsletter Subscribers"
));

sendry.audiences.addContacts(audience.id(), Map.of(
    "contact_ids", List.of(contact.id())
));

Analytics

import dev.sendry.types.AnalyticsResponse;

AnalyticsResponse stats = sendry.analytics.stats(Map.of(
    "from", "2026-05-01",
    "to", "2026-05-31",
    "granularity", "day"
));
System.out.println("Delivery rate: " + stats.summary().deliveryRate());

Error handling

import dev.sendry.*;

try {
    sendry.emails.send(/* ... */);
} catch (AuthenticationException e) {
    System.err.println("Bad API key: " + e.getMessage());
} catch (ValidationException e) {
    System.err.println("Validation failed: " + e.getDetails());
} catch (RateLimitException e) {
    // Back off and retry after e.getRetryAfter() seconds
    Thread.sleep(e.getRetryAfter() == null ? 1000 : e.getRetryAfter() * 1000L);
} catch (NotFoundException e) {
    System.err.println("Not found");
} catch (ApiException e) {
    System.err.printf("API error %d (%s): %s%n",
        e.getStatusCode(), e.getCode(), e.getMessage());
}

Webhook signature verification

Sendry signs every webhook payload using HMAC-SHA256(secret, "${timestamp}.${body}") with the hex digest in the x-sendry-signature header and the timestamp in x-sendry-timestamp.

import dev.sendry.WebhookVerifier;

// In your HTTP handler (Spring, Javalin, vanilla servlet, etc.):
String rawBody = readBody(request);                                  // raw, unparsed
String sig     = request.getHeader("x-sendry-signature");
long   ts      = Long.parseLong(request.getHeader("x-sendry-timestamp"));
String secret  = System.getenv("SENDRY_WEBHOOK_SECRET");

if (!WebhookVerifier.verify(rawBody, sig, ts, secret)) {
    response.setStatus(401);
    return;
}
// Safe to process — request originated from Sendry.

Comparison is constant-time via MessageDigest.isEqual.

Source

github.com/sendry-dev/sendry-java — Apache-2.0 licensed. Published to Maven Central as dev.sendry:sendry.

On this page