Java

Add com.coss:coss with Gradle or Maven, configure the client, handle errors and verify webhooks in Java.

The Java SDK (Java 8+) is com.coss:coss on Maven Central, built on OkHttp and Jackson.

// build.gradle.kts
dependencies {
    implementation("com.coss:coss:0.1.0")
}
<!-- pom.xml -->
<dependency>
  <groupId>com.coss</groupId>
  <artifactId>coss</artifactId>
  <version>0.1.0</version>
</dependency>
import com.coss.Coss;
import com.coss.types.Email;
import com.coss.types.SendEmailRequest;
import com.coss.types.SendEmailRequestTo;
import java.util.Arrays;

Coss client = Coss.builder().build(); // reads COSS_API_KEY

Email email = client.email().send(SendEmailRequest.builder()
        .from("Acme <hello@acme.com>")
        .to(SendEmailRequestTo.of(Arrays.asList("ada@example.com")))
        .subject("Welcome to Acme")
        .html("<p>Thanks for signing up!</p>")
        .build());
System.out.println(email.getId() + " " + email.getStatus());

Requests are immutable objects with builders; required fields come first. AsyncCoss (AsyncCoss.builder()) has the same methods returning CompletableFutures.

Options

Every method also takes a RequestOptions (RequestOptions.builder()) for that call only.

Errors

A failed request throws an exception named after its status (com.coss.errors.NotFoundError, UnprocessableEntityError, …) whose body() is the API's error envelope. They all extend com.coss.core.CossApiException (statusCode(), body()); a request that never got a response throws com.coss.core.CossException.

import com.coss.errors.NotFoundError;

try {
    client.email().get("email_01k7c8m4q2f3e9v6t5r1n0p8yz");
} catch (NotFoundError e) {
    System.out.println(e.statusCode() + " " + e.body().getError().getCode() + " " + e.body().getError().getRequestId());
}

Idempotency and lists

Requests of endpoints that take an Idempotency-Key have an idempotencyKey(…) builder method; every other POST gets a random key of its own, reused by its retries.

List methods return one page. Pass the last id as after while getHasMore() is true:

import com.coss.resources.email.requests.ListEmailRequest;
import com.coss.types.EmailSummary;
import com.coss.types.EmailSummaryList;

EmailSummaryList page = client.email().list(ListEmailRequest.builder().limit(100).build());
while (true) {
    for (EmailSummary email : page.getData()) System.out.println(email.getId());
    if (!page.getHasMore()) break;
    String last = page.getData().get(page.getData().size() - 1).getId();
    page = client.email().list(ListEmailRequest.builder().limit(100).after(last).build());
}

Webhooks

com.coss.Webhooks.verify checks a webhook request's signature and age and returns the parsed event (a Jackson JsonNode); it throws WebhookVerificationException when you must reject the request. Pass the raw body exactly as received and the headers as a Map (values may be strings or lists of them). Webhooks.verifyLogDrainBatch does the same for log drains and returns the batch's lines.

import com.coss.WebhookVerificationException;
import com.coss.Webhooks;
import com.fasterxml.jackson.databind.JsonNode;

@PostMapping("/webhooks/coss")
ResponseEntity<Void> webhook(@RequestBody String body, @RequestHeader Map<String, String> headers) {
    try {
        JsonNode event = Webhooks.verify(body, headers, System.getenv("COSS_WEBHOOK_SECRET"));
        log.info(event.get("type").asText());
        return ResponseEntity.noContent().build();
    } catch (WebhookVerificationException e) {
        return ResponseEntity.badRequest().build();
    }
}