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-KlasseHTTP-StatusWann
AuthenticationException401Ungültiger oder fehlender API-Key
LimitReachedException402Tarif- oder Kontingentgrenze erreicht
ForbiddenException403Authentifiziert, aber nicht berechtigt (gesperrt / wegen Missbrauchs blockiert)
NotFoundException404Ressource existiert nicht
ConflictException409Doppelte Ressource (z. B. bereits vorhandene E-Mail-Adresse)
InvalidStateException422Der Zustand der Ressource blockiert die Operation (nicht verifizierte Domain, blockierter Empfänger, nicht veröffentlichte Vorlage)
RateLimitException429Zu viele Anfragen
ValidationException400Der 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:

HeaderBeschreibung
X-RateLimit-LimitMaximale Anzahl Anfragen pro Fenster
X-RateLimit-RemainingIm aktuellen Fenster verbleibende Anfragen
X-RateLimit-ResetSekunden bis zum Zurücksetzen des Fensters
Retries und Idempotenz

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

Sie nutzen TypeScript?

Für die Integration mit Node.js, Bun und Deno siehe die Referenz zum TypeScript-SDK.