Java-SDK
Das offizielle Paket `owlat-sdk` stellt einen typisierten Client bereit, um aus jeder JVM-Anwendung mit der Owlat-API zu arbeiten. Erfordert Java 11+.
Das offizielle Paket owlat-sdk stellt einen typisierten Client bereit, um aus jeder JVM-Anwendung mit der Owlat-API zu arbeiten. Erfordert Java 11+.
Installation
<dependency>
<groupId>com.owlat</groupId>
<artifactId>owlat-sdk</artifactId>
<version>0.1.0</version>
</dependency>
implementation 'com.owlat:owlat-sdk:0.1.0'
Schnellstart
import com.owlat.sdk.Owlat;
import com.owlat.sdk.model.contact.Contact;
import com.owlat.sdk.model.contact.CreateContactParams;
Owlat owlat = new Owlat("lm_live_...");
Contact contact = owlat.contacts().create(
CreateContactParams.builder("mira@acme.io")
.firstName("Mira")
.lastName("Chen")
.build()
);
Sie können auch ein vollständiges Konfigurationsobjekt übergeben:
import com.owlat.sdk.OwlatConfig;
import java.time.Duration;
Owlat owlat = new Owlat(
OwlatConfig.builder("lm_live_...")
// Self-hosting? Point baseUrl at your Convex deployment site URL —
// the API is served from https://<your-deployment>.convex.site/api/v1/*
.baseUrl("https://your-deployment.convex.site")
.timeout(Duration.ofSeconds(60))
.build()
);
Ressourcen
Kontakte
Kontakte in Ihrer Zielgruppe verwalten.
// Create a contact
Contact contact = owlat.contacts().create(
CreateContactParams.builder("mira@acme.io")
.firstName("Mira")
.lastName("Chen")
.build()
);
// Get by ID or email
Contact found = owlat.contacts().get("mira@acme.io");
// Update a contact
Contact updated = owlat.contacts().update("mira@acme.io",
UpdateContactParams.builder()
.lastName("Chen-Lopez")
.build()
);
// List with cursor-based pagination
PaginatedResponse<Contact> page = owlat.contacts().list(
PaginationParams.builder()
.limit(25)
.search("jane")
.build()
);
// Fetch the next page with the returned cursor
if (!page.getPagination().isDone()) {
PaginatedResponse<Contact> next = owlat.contacts().list(
PaginationParams.builder()
.cursor(page.getPagination().getCursor())
.build()
);
}
// Or let the SDK follow cursors for you — listAll() returns a lazy
// Stream<Contact> that fetches pages on demand (limit/search honored,
// any supplied cursor ignored), so you never hold the full set in memory
owlat.contacts().listAll().forEach(c -> System.out.println(c.getEmail()));
// Delete a contact
DeleteContactResponse deleted = owlat.contacts().delete("mira@acme.io");
Transaktional
Transaktionale E-Mails mit vorgefertigten Vorlagen versenden.
import com.owlat.sdk.model.transactional.SendTransactionalParams;
import com.owlat.sdk.model.transactional.SendTransactionalResponse;
SendTransactionalResponse result = owlat.transactional().send(
SendTransactionalParams.builder("buyer@example.com")
.slug("order-confirmation")
.dataVariables(Map.of(
"orderId", "ORD-7291",
"total", "$142.00"
))
.language("en")
.build()
);
Sie können die Vorlage über slug oder transactionalId identifizieren. Mindestens eines von beiden muss angegeben werden.
Anhänge
Sie können Dateien über Base64-kodierten Inhalt oder HTTPS-URLs anhängen (max. 10 Dateien, insgesamt 10 MB):
import com.owlat.sdk.model.transactional.TransactionalAttachment;
SendTransactionalResponse result = owlat.transactional().send(
SendTransactionalParams.builder("buyer@example.com")
.slug("order-confirmation")
.attachment(TransactionalAttachment.builder("invoice.pdf")
.content(base64String)
.contentType("application/pdf")
.build())
.attachment(TransactionalAttachment.builder("receipt.pdf")
.url("https://your-api.com/receipts/ORD-7291.pdf")
.build())
.build()
);
Events
Eigene Ereignisse senden, um Automationen auszulösen und Segmente aufzubauen.
import com.owlat.sdk.model.event.SendEventParams;
import com.owlat.sdk.model.event.SendEventResponse;
SendEventResponse result = owlat.events().send(
SendEventParams.builder("mira@acme.io", "plan_upgraded")
.eventProperties(Map.of(
"plan", "pro",
"mrr", 49
))
.createContactIfNotExists(true)
.build()
);
// result.getTriggeredAutomations() — number of automations triggered (int)
Themen
Themen-Mitgliedschaften verwalten. Die API ist reine Schreib-API (Hinzufügen/Entfernen) — es gibt keinen
List-/Get-Endpunkt; kopieren Sie die topicId daher aus dem Dashboard (Themen → ein Thema).
import com.owlat.sdk.model.topic.*;
// Add a contact to a topic
AddToTopicResponse added = owlat.topics().addContact(
AddToTopicParams.builder("topic_abc123")
.email("mira@acme.io")
.build()
);
// added.getDoiStatus() — NOT_REQUIRED, PENDING, or CONFIRMED
// Remove a contact from a topic
RemoveFromTopicResponse removed = owlat.topics().removeContact(
new RemoveFromTopicParams("topic_abc123", "mira@acme.io")
);
Fehlerbehandlung
Alle Exceptions erben von OwlatException (unchecked) und enthalten den HTTP-Statuscode sowie Informationen zum Rate-Limit.
import com.owlat.sdk.exception.*;
try {
owlat.transactional().send(
SendTransactionalParams.builder("user@example.com").slug("welcome").build()
);
} catch (InvalidStateException e) {
// 422 — unverified sending domain, blocked recipient, unpublished template.
// The most common transactional failure mode.
System.err.println("Cannot send yet: " + e.getMessage());
} catch (ForbiddenException e) {
// 403 — suspended / abuse-blocked account.
System.err.println("Not permitted: " + e.getMessage());
} catch (LimitReachedException e) {
System.err.println("Plan limit reached: " + e.getMessage());
} catch (NotFoundException e) {
System.err.println("Not found: " + e.getMessage());
} catch (AuthenticationException e) {
System.err.println("Invalid API key");
} catch (RateLimitException e) {
System.err.println("Rate limited. Retry after " + e.getRetryAfter() + "s");
} catch (ValidationException e) {
System.err.println("Invalid request: " + e.getMessage());
} catch (ConflictException e) {
System.err.println("Conflict: " + e.getMessage());
} catch (OwlatException e) {
System.err.println("API error " + e.getStatusCode() + ": " + e.getMessage());
}
| Exception-Klasse | HTTP-Status | Wann |
|---|---|---|
AuthenticationException | 401 | Ungültiger oder fehlender API-Key |
LimitReachedException | 402 | Tarif- oder Kontingentgrenze erreicht |
ForbiddenException | 403 | Authentifiziert, aber nicht berechtigt (gesperrt / wegen Missbrauchs blockiert) |
NotFoundException | 404 | Ressource existiert nicht |
ConflictException | 409 | Doppelte Ressource (z. B. bereits vorhandene E-Mail-Adresse) |
InvalidStateException | 422 | Der Zustand der Ressource blockiert die Operation (nicht verifizierte Domain, blockierter Empfänger, nicht veröffentlichte Vorlage) |
RateLimitException | 429 | Zu viele Anfragen |
ValidationException | 400 | Der Request-Body besteht die Validierung nicht |
Jede Exception stellt getRateLimit() bereit, das ein RateLimitInfo mit getLimit(), getRemaining() und getReset() zurückliefert.
Rate-Limit-Tracking
Rate-Limit-Header werden aus jeder Antwort automatisch geparst. Greifen Sie über getRateLimit() an jeder Exception darauf zu oder sehen Sie sich die Response-Header direkt an:
| Header | Beschreibung |
|---|---|
X-RateLimit-Limit | Maximale Anzahl Anfragen pro Fenster |
X-RateLimit-Remaining | Im aktuellen Fenster verbleibende Anfragen |
X-RateLimit-Reset | Sekunden bis zum Zurücksetzen des Fensters |
Das Java-SDK wiederholt vorübergehende Fehler automatisch — bis zu 2 Wiederholungen
(insgesamt 3 Versuche) mit exponentiellem Backoff (500 ms Basis, jeweils verdoppelt).
Ein 429 wird unabhängig von der Methode stets wiederholt, unter Beachtung des
Retry-After-Headers (gedeckelt bei 30 s). Ein 5xx oder ein Netzwerk-/Timeout-Fehler wird nur bei
idempotenten Methoden (GET/PUT/DELETE) wiederholt.
POST-Sendungen — transactional().send(...) und events().send(...) — werden bei einem 5xx
oder Netzwerkfehler nicht automatisch wiederholt, da der Server keinen Idempotenzschlüssel hat
und eine vom Server bereits verarbeitete Sendung nicht dupliziert werden darf. Diese erscheinen als
Exceptions, die Sie selbst behandeln; spielen Sie sie niemals blind erneut ab. Eine
RateLimitException (429) stellt weiterhin getRetryAfter() bereit, falls Sie zurückregeln und
einen Aufruf manuell wiederholen möchten.
Aus dem Quellcode bauen
cd packages/sdk-java
mvn compile
mvn test
Für die Integration mit Node.js, Bun und Deno siehe die Referenz zum TypeScript-SDK.