Skip to content
Draft
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,10 @@ You can find the [Mailtrap Java API reference](https://mailtrap.github.io/mailtr

- [Email Templates](examples/java/io/mailtrap/examples/emailtemplates/EmailTemplatesExample.java)

### Email Marketing API

- [Email Campaigns](examples/java/io/mailtrap/examples/emailcampaigns/EmailCampaignsExample.java)

## Contributing

Bug reports and pull requests are welcome on [GitHub](https://github.com/mailtrap/mailtrap-java). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](CODE_OF_CONDUCT.md).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package io.mailtrap.examples.emailcampaigns;

import io.mailtrap.config.MailtrapConfig;
import io.mailtrap.factory.MailtrapClientFactory;
import io.mailtrap.model.DeliveryMode;
import io.mailtrap.model.request.emailcampaigns.CreateEmailCampaign;
import io.mailtrap.model.request.emailcampaigns.ScheduleEmailCampaignRequest;
import io.mailtrap.model.request.emailcampaigns.TemplateAttributes;
import io.mailtrap.model.request.emailcampaigns.UpdateEmailCampaign;
import io.mailtrap.model.response.emailcampaigns.DeliveryOptions;
import io.mailtrap.model.response.emailcampaigns.ReplyTo;

import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.List;

public class EmailCampaignsExample {

private static final String TOKEN = "<YOUR MAILTRAP TOKEN>";
// UUID of a verified sending domain on the account.
private static final String MAILSEND_DOMAIN_ID = "<YOUR SENDING DOMAIN UUID>";
private static final long CONTACT_LIST_ID = 55L;

public static void main(String[] args) {
final var config = new MailtrapConfig.Builder()
.token(TOKEN)
.build();

final var client = MailtrapClientFactory.createMailtrapClient(config);

// The campaign endpoints are token-scoped: the account is resolved from the API token.
final var campaigns = client.emailCampaignsApi().emailCampaigns();

// List campaigns (newest first). `search` filters by name; `token` is the page number.
final var page = campaigns.getEmailCampaigns(50, "Spring", 1);
System.out.println(page);

// Create a campaign — it starts in the `draft` state. The request body is flat.
final var created = campaigns.createEmailCampaign(
CreateEmailCampaign.builder()
.name("Spring Sale")
.mailsendDomainId(MAILSEND_DOMAIN_ID)
.fromDisplayName("Acme Marketing")
.fromLocalPart("news")
.replyTo(ReplyTo.builder()
.displayName("Acme Support")
.localPart("support")
.domain("acme.com")
.build())
.templateAttributes(TemplateAttributes.builder()
.subject("Spring is here — 30% off")
.build())
.contactListIds(List.of(CONTACT_LIST_ID))
.build());
System.out.println(created.getData());

final var campaignId = created.getData().getId();

// Retrieve a single campaign.
final var fetched = campaigns.getEmailCampaign(campaignId);
System.out.println(fetched.getData());

// Update is a PATCH — only the provided fields change. The template is edited in place;
// `bodyHtml` is the design and must contain an unsubscribe link.
final var updated = campaigns.updateEmailCampaign(campaignId,
UpdateEmailCampaign.builder()
.name("Spring Sale (updated)")
.templateAttributes(TemplateAttributes.builder()
.subject("New subject")
.bodyHtml("<html><body><h1>Hi {{first_name}}!</h1>"
+ "<p><a href=\"__unsubscribe_url__\">Unsubscribe</a></p></body></html>")
.mergeTags(List.of("first_name"))
.build())
.deliveryMode(DeliveryMode.GRADUAL)
.deliveryOptions(DeliveryOptions.builder().emailsPerHour(1000).build())
.build());
System.out.println(updated.getData());

// Schedule the draft to send later — the time must be in the future, at most 1 month
// ahead; it comes back in currentStateMetadata.scheduledAt.
final var scheduled = campaigns.scheduleEmailCampaign(campaignId,
new ScheduleEmailCampaignRequest(OffsetDateTime.now(ZoneOffset.UTC).plusDays(1)));
System.out.println(scheduled.getData().getCurrentStateMetadata().getScheduledAt());

// Cancel the scheduled send — the campaign returns to `draft`.
final var cancelled = campaigns.cancelEmailCampaign(campaignId);
System.out.println(cancelled.getData().getCurrentState());

// Or start sending immediately.
final var started = campaigns.startEmailCampaign(campaignId);
System.out.println(started.getData().getCurrentState());

// Aggregated performance statistics; narrow the window with start/end dates (YYYY-MM-DD).
final var today = LocalDate.now(ZoneOffset.UTC);
final var stats = campaigns.getEmailCampaignStats(campaignId, today.minusDays(30).toString(), today.toString());
System.out.println(stats.getData());

// Delete returns 204 No Content.
campaigns.deleteEmailCampaign(campaignId);
}
}
130 changes: 130 additions & 0 deletions src/main/java/io/mailtrap/api/emailcampaigns/EmailCampaigns.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package io.mailtrap.api.emailcampaigns;

import io.mailtrap.model.request.emailcampaigns.CreateEmailCampaign;
import io.mailtrap.model.request.emailcampaigns.ScheduleEmailCampaignRequest;
import io.mailtrap.model.request.emailcampaigns.UpdateEmailCampaign;
import io.mailtrap.model.response.emailcampaigns.EmailCampaignListResponse;
import io.mailtrap.model.response.emailcampaigns.EmailCampaignResponse;
import io.mailtrap.model.response.emailcampaigns.EmailCampaignStatsResponse;

/**
* Email Campaigns API. Manage email marketing campaigns and retrieve their performance
* statistics.
*
* <p>The account is resolved from the API token, so these endpoints are token-scoped and the
* path is not account-scoped.
*/
public interface EmailCampaigns {

/**
* List the account's email campaigns, newest first.
*
* @param perPage number of campaigns per page (max 100, default 50); {@code null} to omit
* @param search filter campaigns by name; {@code null} to omit
* @param token page number to retrieve (page-token pagination, default 1);
* {@code null} to omit
* @return a page of campaigns and the pagination metadata
*/
EmailCampaignListResponse getEmailCampaigns(Integer perPage, String search, Integer token);

/**
* Create a new email campaign in the {@code draft} state.
*
* @param request the campaign attributes ({@code name}, {@code mailsendDomainId},
* {@code fromLocalPart} and a template {@code subject} are required)
* @return the created email campaign
*/
EmailCampaignResponse createEmailCampaign(CreateEmailCampaign request);

/**
* Get a single email campaign by ID.
*
* @param emailCampaignId unique email campaign ID
* @return the email campaign
*/
EmailCampaignResponse getEmailCampaign(long emailCampaignId);

/**
* Update an existing {@code draft} email campaign. Only the provided attributes are
* changed.
*
* @param emailCampaignId unique email campaign ID
* @param request the attributes to update
* @return the updated email campaign
*/
EmailCampaignResponse updateEmailCampaign(long emailCampaignId, UpdateEmailCampaign request);

/**
* Delete an email campaign. The campaign must not be in a sending state.
*
* @param emailCampaignId unique email campaign ID
*/
void deleteEmailCampaign(long emailCampaignId);

/**
* Start sending a {@code draft} campaign immediately.
*
* @param emailCampaignId unique email campaign ID
* @return the started email campaign
*/
EmailCampaignResponse startEmailCampaign(long emailCampaignId);

/**
* Schedule a {@code draft} campaign to start sending at a future time. The scheduled time
* is reported back in {@code currentStateMetadata.scheduledAt}.
*
* @param emailCampaignId unique email campaign ID
* @param request when to start sending the campaign
* @return the scheduled email campaign
*/
EmailCampaignResponse scheduleEmailCampaign(long emailCampaignId, ScheduleEmailCampaignRequest request);

/**
* Cancel a {@code scheduled} campaign, returning it to the {@code draft} state.
*
* @param emailCampaignId unique email campaign ID
* @return the cancelled email campaign
*/
EmailCampaignResponse cancelEmailCampaign(long emailCampaignId);

/**
* Terminate a campaign that is currently sending ({@code started}, {@code queued} or
* {@code paused}), aborting the in-flight send.
*
* @param emailCampaignId unique email campaign ID
* @return the terminated email campaign
*/
EmailCampaignResponse terminateEmailCampaign(long emailCampaignId);

/**
* Reset a {@code scheduled} campaign back to the {@code draft} state.
*
* @param emailCampaignId unique email campaign ID
* @return the reset email campaign
*/
EmailCampaignResponse resetEmailCampaign(long emailCampaignId);

/**
* Get aggregated performance statistics for an email campaign over the whole period since
* the campaign was last started. If the campaign has never been started, all counts and
* rates are returned as {@code 0}.
*
* @param emailCampaignId unique email campaign ID
* @return aggregated campaign statistics
*/
EmailCampaignStatsResponse getEmailCampaignStats(long emailCampaignId);

/**
* Get aggregated performance statistics for an email campaign over a narrowed aggregation
* window.
*
* @param emailCampaignId unique email campaign ID
* @param startDate start of the aggregation window (inclusive), in
* {@code YYYY-MM-DD} format; {@code null} to omit
* @param endDate end of the aggregation window (inclusive), in {@code YYYY-MM-DD}
* format; {@code null} to omit
* @return aggregated campaign statistics
*/
EmailCampaignStatsResponse getEmailCampaignStats(long emailCampaignId, String startDate, String endDate);

}
138 changes: 138 additions & 0 deletions src/main/java/io/mailtrap/api/emailcampaigns/EmailCampaignsImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package io.mailtrap.api.emailcampaigns;

import io.mailtrap.Constants;
import io.mailtrap.api.apiresource.ApiResource;
import io.mailtrap.config.MailtrapConfig;
import io.mailtrap.http.RequestData;
import io.mailtrap.model.AbstractModel;
import io.mailtrap.model.request.emailcampaigns.CreateEmailCampaign;
import io.mailtrap.model.request.emailcampaigns.ScheduleEmailCampaignRequest;
import io.mailtrap.model.request.emailcampaigns.UpdateEmailCampaign;
import io.mailtrap.model.response.emailcampaigns.EmailCampaignListResponse;
import io.mailtrap.model.response.emailcampaigns.EmailCampaignResponse;
import io.mailtrap.model.response.emailcampaigns.EmailCampaignStatsResponse;

import java.util.Optional;

import static io.mailtrap.http.RequestData.entry;

public class EmailCampaignsImpl extends ApiResource implements EmailCampaigns {

private static final String BASE_PATH = "/api/email_campaigns";

public EmailCampaignsImpl(final MailtrapConfig config) {
super(config);
this.apiHost = Constants.GENERAL_HOST;
}

@Override
public EmailCampaignListResponse getEmailCampaigns(final Integer perPage, final String search, final Integer token) {
final var queryParams = RequestData.buildQueryParams(
entry("per_page", Optional.ofNullable(perPage)),
entry("search", Optional.ofNullable(search)),
entry("token", Optional.ofNullable(token))
);

return httpClient.get(
apiHost + BASE_PATH,
new RequestData(queryParams),
EmailCampaignListResponse.class
);
}

@Override
public EmailCampaignResponse createEmailCampaign(final CreateEmailCampaign request) {
return httpClient.post(
apiHost + BASE_PATH,
request,
new RequestData(),
EmailCampaignResponse.class
);
}

@Override
public EmailCampaignResponse getEmailCampaign(final long emailCampaignId) {
return httpClient.get(
String.format(apiHost + BASE_PATH + "/%d", emailCampaignId),
new RequestData(),
EmailCampaignResponse.class
);
}

@Override
public EmailCampaignResponse updateEmailCampaign(final long emailCampaignId, final UpdateEmailCampaign request) {
return httpClient.patch(
String.format(apiHost + BASE_PATH + "/%d", emailCampaignId),
request,
new RequestData(),
EmailCampaignResponse.class
);
}

@Override
public void deleteEmailCampaign(final long emailCampaignId) {
httpClient.delete(
String.format(apiHost + BASE_PATH + "/%d", emailCampaignId),
new RequestData(),
Void.class
);
}

@Override
public EmailCampaignResponse startEmailCampaign(final long emailCampaignId) {
return performLifecycleAction(emailCampaignId, "start");
}

@Override
public EmailCampaignResponse scheduleEmailCampaign(final long emailCampaignId, final ScheduleEmailCampaignRequest request) {
return httpClient.post(
String.format(apiHost + BASE_PATH + "/%d/schedule", emailCampaignId),
request,
new RequestData(),
EmailCampaignResponse.class
);
}

@Override
public EmailCampaignResponse cancelEmailCampaign(final long emailCampaignId) {
return performLifecycleAction(emailCampaignId, "cancel");
}

@Override
public EmailCampaignResponse terminateEmailCampaign(final long emailCampaignId) {
return performLifecycleAction(emailCampaignId, "terminate");
}

@Override
public EmailCampaignResponse resetEmailCampaign(final long emailCampaignId) {
return performLifecycleAction(emailCampaignId, "reset");
}

@Override
public EmailCampaignStatsResponse getEmailCampaignStats(final long emailCampaignId) {
return getEmailCampaignStats(emailCampaignId, null, null);
}

@Override
public EmailCampaignStatsResponse getEmailCampaignStats(final long emailCampaignId, final String startDate, final String endDate) {
final var queryParams = RequestData.buildQueryParams(
entry("start_date", Optional.ofNullable(startDate)),
entry("end_date", Optional.ofNullable(endDate))
);

return httpClient.get(
String.format(apiHost + BASE_PATH + "/%d/stats", emailCampaignId),
new RequestData(queryParams),
EmailCampaignStatsResponse.class
);
}

private EmailCampaignResponse performLifecycleAction(final long emailCampaignId, final String action) {
return httpClient.post(
String.format(apiHost + BASE_PATH + "/%d/%s", emailCampaignId, action),
(AbstractModel) null,
new RequestData(),
EmailCampaignResponse.class
);
}
}
6 changes: 6 additions & 0 deletions src/main/java/io/mailtrap/client/MailtrapClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ public class MailtrapClient {
@Getter
private final MailtrapOrganizationsApi organizationsApi;

/**
* API for Mailtrap.io Email Campaigns functionality
*/
@Getter
private final MailtrapEmailCampaignsApi emailCampaignsApi;

/**
* Utility class which holds sending context (which API to use: Email Sending API, Bulk Sending API or
* Email Testing API, inbox id for Email Testing API) to make it possible to perform send directly from MailtrapClient
Expand Down
Loading
Loading