Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0] - 2026-04-22

### Added

- `UpdateWebhookOptions.url()` — matches the renamed API field on `PUT /webhooks/{id}`

### Changed

- Webhook event types are now namespaced (e.g. `message.delivery`, `engagement.click`, `generation.generation_failure`). The server emits and accepts the namespaced form on both `POST` and `PUT`. Update any hard-coded event strings in `CreateWebhookOptions.events` / `UpdateWebhookOptions.events`.

### Deprecated

- `UpdateWebhookOptions.target()` / `getTarget()` — use `url()` / `getUrl()`. The builder still accepts `target` for source compatibility and serializes it as `url`, so existing callers keep working.

## [1.0.0] - 2026-04-21

### Added
Expand Down Expand Up @@ -57,6 +71,7 @@ Initial release.
- Bearer token auth, Gson-based JSON serialization
- Structured exceptions: `LettrException`, `LettrApiException`, `LettrValidationException`

[1.1.0]: https://github.com/lettr/lettr-java/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/lettr/lettr-java/compare/v0.2.0...v1.0.0
[0.2.0]: https://github.com/lettr/lettr-java/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/lettr/lettr-java/releases/tag/v0.1.0
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ Webhook webhook = lettr.webhooks().create(
.authUsername("user")
.authPassword("secret")
.eventsMode("selected")
.events(List.of("delivery", "bounce"))
.events(List.of("message.delivery", "message.bounce"))
.build()
);
System.out.println("Webhook ID: " + webhook.getId());
Expand All @@ -315,7 +315,7 @@ Webhook webhook = lettr.webhooks().get("webhook-abc123");
Webhook updated = lettr.webhooks().update("webhook-abc123",
UpdateWebhookOptions.builder()
.name("Updated Webhook")
.target("https://new.example.com/webhook")
.url("https://new.example.com/webhook")
.active(false)
.build()
);
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GROUP=com.lettr
VERSION=1.0.0
VERSION=1.1.0
POM_ARTIFACT_ID=lettr-java
POM_NAME=Lettr Java SDK
POM_DESCRIPTION=Java SDK for the Lettr Email API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
public class UpdateWebhookOptions {

private final String name;
private final String target;
private final String url;

@SerializedName("auth_type") private final String authType;
@SerializedName("auth_username") private final String authUsername;
Expand All @@ -27,7 +27,7 @@ public class UpdateWebhookOptions {

private UpdateWebhookOptions(Builder builder) {
this.name = builder.name;
this.target = builder.target;
this.url = builder.url;
this.authType = builder.authType;
this.authUsername = builder.authUsername;
this.authPassword = builder.authPassword;
Expand All @@ -44,7 +44,10 @@ public static Builder builder() {
}

@Nullable public String getName() { return name; }
@Nullable public String getTarget() { return target; }
@Nullable public String getUrl() { return url; }
/** @deprecated use {@link #getUrl()} — the API field was renamed from {@code target} to {@code url}. */
@Deprecated
@Nullable public String getTarget() { return url; }
@Nullable public String getAuthType() { return authType; }
@Nullable public String getAuthUsername() { return authUsername; }
@Nullable public String getAuthPassword() { return authPassword; }
Expand All @@ -56,7 +59,7 @@ public static Builder builder() {

public static class Builder {
private String name;
private String target;
private String url;
private String authType;
private String authUsername;
private String authPassword;
Expand All @@ -71,8 +74,14 @@ private Builder() {}
/** <b>(optional)</b> Sets the new webhook name. Max length: 255. */
@Nonnull public Builder name(@Nullable String name) { this.name = name; return this; }

/** <b>(optional)</b> Sets the new webhook target URL. Max length: 2048. */
@Nonnull public Builder target(@Nullable String target) { this.target = target; return this; }
/** <b>(optional)</b> Sets the new webhook URL. Max length: 2048. */
@Nonnull public Builder url(@Nullable String url) { this.url = url; return this; }

/**
* @deprecated use {@link #url(String)} — the API field was renamed from {@code target} to {@code url}.
*/
@Deprecated
@Nonnull public Builder target(@Nullable String target) { this.url = target; return this; }

/** <b>(optional)</b> Sets the new authentication type. */
@Nonnull public Builder authType(@Nullable String authType) { this.authType = authType; return this; }
Expand Down
40 changes: 37 additions & 3 deletions src/test/java/com/lettr/services/webhooks/WebhooksTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ void createWebhookOptionsBuildsWithAllFields() {
.authUsername("user")
.authPassword("pass")
.eventsMode("selected")
.events(Arrays.asList("delivery", "bounce"))
.events(Arrays.asList("message.delivery", "message.bounce"))
.build();

assertEquals("Test Webhook", options.getName());
Expand Down Expand Up @@ -108,7 +108,7 @@ void updateWebhookOptionsBuildsWithNoFields() {
void updateWebhookOptionsBuildsWithAllFields() {
UpdateWebhookOptions options = UpdateWebhookOptions.builder()
.name("Updated")
.target("https://new.example.com/webhook")
.url("https://new.example.com/webhook")
.authType("oauth2")
.oauthClientId("client-id")
.oauthClientSecret("secret")
Expand All @@ -118,11 +118,45 @@ void updateWebhookOptionsBuildsWithAllFields() {
.build();

assertEquals("Updated", options.getName());
assertEquals("https://new.example.com/webhook", options.getTarget());
assertEquals("https://new.example.com/webhook", options.getUrl());
assertEquals("oauth2", options.getAuthType());
assertEquals(false, options.getActive());
}

@Test
@SuppressWarnings("deprecation")
void updateWebhookOptionsDeprecatedTargetStillWorks() {
UpdateWebhookOptions options = UpdateWebhookOptions.builder()
.target("https://legacy.example.com/webhook")
.build();

assertEquals("https://legacy.example.com/webhook", options.getUrl());
assertEquals("https://legacy.example.com/webhook", options.getTarget());
}

@Test
void updateWebhookOptionsSerializesUrlNotTarget() {
UpdateWebhookOptions options = UpdateWebhookOptions.builder()
.url("https://new.example.com/webhook")
.build();

String json = gson.toJson(options);
assertTrue(json.contains("\"url\""));
assertFalse(json.contains("\"target\""));
}

@Test
@SuppressWarnings("deprecation")
void updateWebhookOptionsDeprecatedTargetSerializesAsUrl() {
UpdateWebhookOptions options = UpdateWebhookOptions.builder()
.target("https://legacy.example.com/webhook")
.build();

String json = gson.toJson(options);
assertTrue(json.contains("\"url\""));
assertFalse(json.contains("\"target\""));
}

// --- Webhook deserialization ---

@Test
Expand Down
Loading