Java

The official Schematic Java library, supporting Java 8+.

Installation and Setup

  1. Add the dependency using your build tool of choice:

Using Gradle in build.gradle:

1dependencies {
2 implementation 'com.schematichq:schematic-java:0.x.x'
3}

Using Maven in pom.xml:

1<dependency>
2 <groupId>com.schematichq</groupId>
3 <artifactId>schematic-java</artifactId>
4 <version>0.x.x</version>
5</dependency>
  1. Issue an API key for the appropriate environment using the Schematic app.

  2. Using this secret key, initialize a client in your application:

1import com.schematic.api.Schematic;
2
3Schematic schematic = Schematic.builder()
4 .apiKey("YOUR_API_KEY")
5 .build();

Usage

Sending identify events

Create or update users and companies using identify events.

1import com.fasterxml.jackson.databind.JsonNode;
2import com.schematic.api.Schematic;
3import com.schematic.api.core.ObjectMappers;
4import java.util.HashMap;
5import java.util.Map;
6
7Schematic schematic = Schematic.builder()
8 .apiKey("YOUR_API_KEY")
9 .build();
10
11Map<String, String> keys = new HashMap<>();
12keys.put("email", "wcoyote@acme.net");
13keys.put("user_id", "your-user-id");
14
15EventBodyIdentifyCompany company = EventBodyIdentifyCompany.builder()
16 .name("Acme Widgets, Inc.")
17 .build();
18
19Map<String, JsonNode> traits = new HashMap<>();
20traits.put("city", ObjectMappers.JSON_MAPPER.valueToTree("Atlanta"));
21traits.put("high_score", ObjectMappers.JSON_MAPPER.valueToTree(25));
22traits.put("is_active", ObjectMappers.JSON_MAPPER.valueToTree(true));
23
24schematic.identify(keys, company, "Wile E. Coyote", traits);

This call is non-blocking and there is no response to check.

Sending track events

Track activity in your application using track events; these events can later be used to produce metrics for targeting.

1import com.schematic.api.Schematic;
2import java.util.HashMap;
3import java.util.Map;
4
5Schematic schematic = Schematic.builder()
6 .apiKey("YOUR_API_KEY")
7 .build();
8
9Map<String, String> company = new HashMap<>();
10company.put("id", "your-company-id");
11
12Map<String, String> user = new HashMap<>();
13user.put("user_id", "your-user-id");
14
15Map<String, Object> traits = new HashMap<>();
16
17schematic.track("some-action", company, user, traits);

This call is non-blocking and there is no response to check.

If you want to record large numbers of the same event at once, or perhaps measure usage in terms of a unit like tokens or memory, you can optionally specify a quantity for your event:

1schematic.track("some-action", company, user, traits, 10);

Creating and updating companies

Although it is faster to create companies and users via identify events, if you need to handle a response, you can use the companies API to upsert companies. Because you use your own identifiers to identify companies, rather than a Schematic company ID, creating and updating companies are both done via the same upsert operation:

1import com.fasterxml.jackson.databind.JsonNode;
2import com.schematic.api.Schematic;
3import com.schematic.api.core.ObjectMappers;
4import com.schematic.api.types.UpsertCompanyRequestBody;
5import java.util.HashMap;
6import java.util.Map;
7
8Schematic schematic = Schematic.builder()
9 .apiKey("YOUR_API_KEY")
10 .build();
11
12Map<String, String> keys = new HashMap<>();
13keys.put("id", "your-company-id");
14
15Map<String, JsonNode> traits = new HashMap<>();
16traits.put("city", ObjectMappers.JSON_MAPPER.valueToTree("Atlanta"));
17traits.put("high_score", ObjectMappers.JSON_MAPPER.valueToTree(25));
18traits.put("is_active", ObjectMappers.JSON_MAPPER.valueToTree(true));
19
20UpsertCompanyRequestBody request = UpsertCompanyRequestBody.builder()
21 .keys(keys)
22 .name("Acme Widgets, Inc.")
23 .traits(traits)
24 .build();
25
26var response = schematic.companies.upsertCompany(request);
27System.out.println("Company upserted: " + response.getData().getName());

Checking flags

When checking a flag, you’ll provide keys for a company and/or keys for a user. You can also provide no keys at all, in which case you’ll get the default value for the flag.

1import com.schematic.api.Schematic;
2import java.util.HashMap;
3import java.util.Map;
4
5Schematic schematic = Schematic.builder()
6 .apiKey("YOUR_API_KEY")
7 .build();
8
9Map<String, String> company = new HashMap<>();
10company.put("id", "your-company-id");
11
12Map<String, String> user = new HashMap<>();
13user.put("user_id", "your-user-id");
14
15boolean flagValue = schematic.checkFlag("some-flag-key", company, user);

Webhook Verification

Schematic can send webhooks to notify your application of events. To ensure the security of these webhooks, Schematic signs each request using HMAC-SHA256. The Java SDK provides utility functions to verify these signatures.

Verifying Webhook Signatures

When your application receives a webhook request from Schematic, you should verify its signature to ensure it’s authentic:

1import com.schematic.webhook.WebhookVerifier;
2import com.schematic.webhook.WebhookSignatureException;
3import java.util.Map;
4import java.util.HashMap;
5import java.io.BufferedReader;
6import java.io.IOException;
7
8// In your webhook endpoint handler:
9public void handleWebhook(HttpServletRequest request, HttpServletResponse response) throws IOException {
10 // Read the request body
11 String body = request.getReader().lines().collect(Collectors.joining("\n"));
12
13 // Get the required headers
14 Map<String, String> headers = new HashMap<>();
15 headers.put(WebhookVerifier.WEBHOOK_SIGNATURE_HEADER,
16 request.getHeader(WebhookVerifier.WEBHOOK_SIGNATURE_HEADER));
17 headers.put(WebhookVerifier.WEBHOOK_TIMESTAMP_HEADER,
18 request.getHeader(WebhookVerifier.WEBHOOK_TIMESTAMP_HEADER));
19
20 String webhookSecret = "your-webhook-secret";
21
22 try {
23 // Verify the webhook signature
24 WebhookVerifier.verifyWebhookSignature(body, headers, webhookSecret);
25
26 // Process the webhook payload
27 // ...
28
29 response.setStatus(HttpServletResponse.SC_OK);
30 } catch (WebhookSignatureException e) {
31 // Handle signature verification failure
32 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
33 response.getWriter().write("Invalid signature: " + e.getMessage());
34 }
35}

Verifying Signatures Manually

If you need to verify a webhook signature outside of the context of a servlet request, you can use the verifySignature method:

1import com.schematic.webhook.WebhookVerifier;
2import com.schematic.webhook.WebhookSignatureException;
3
4public void verifyWebhookManually(String body, String signature, String timestamp, String secret) {
5 try {
6 WebhookVerifier.verifySignature(body, signature, timestamp, secret);
7 System.out.println("Signature verification successful!");
8 } catch (WebhookSignatureException e) {
9 System.out.println("Signature verification failed: " + e.getMessage());
10 }
11}

Configuration Options

There are a number of configuration options that can be specified using the builder when instantiating the Schematic client.

Flag Check Options

By default, the client will do some local caching for flag checks. If you would like to change this behavior, you can do so using initialization options to specify the cache providers:

1import com.schematic.api.Schematic;
2import com.schematic.api.cache.LocalCache;
3import java.time.Duration;
4import java.util.Collections;
5
6Schematic schematic = Schematic.builder()
7 .apiKey("YOUR_API_KEY")
8 .cacheProviders(Collections.singletonList(new LocalCache<>()))
9 .build();

You can also disable local caching entirely; bear in mind that, in this case, every flag check will result in a network request:

1import com.schematic.api.Schematic;
2import java.util.Collections;
3
4Schematic schematic = Schematic.builder()
5 .apiKey("YOUR_API_KEY")
6 .cacheProviders(Collections.emptyList())
7 .build();

You may want to specify default flag values for your application, which will be used if there is a service interruption or if the client is running in offline mode:

1import com.schematic.api.Schematic;
2import java.util.HashMap;
3import java.util.Map;
4
5Map<String, Boolean> flagDefaults = new HashMap<>();
6flagDefaults.put("some-flag-key", true);
7
8Schematic schematic = Schematic.builder()
9 .apiKey("YOUR_API_KEY")
10 .flagDefaults(flagDefaults)
11 .build();

Offline Mode

In development or testing environments, you may want to avoid making network requests when checking flags or submitting events. You can run Schematic in offline mode:

1import com.schematic.api.Schematic;
2
3Schematic schematic = Schematic.builder()
4 .apiKey("YOUR_API_KEY")
5 .offline(true)
6 .build();

When in offline mode:

  1. Flag checks will return the default value for the flag being checked (false by default, or as specified in flagDefaults)
  2. Events (identify and track) will be skipped completely
  3. All other API calls will use a no-op HTTP client that doesn’t make actual network requests, returning empty responses

This is especially useful for development, testing, or when running unit tests that shouldn’t depend on the Schematic API.

Offline mode works well with flag defaults:

1import com.schematic.api.Schematic;
2import java.util.HashMap;
3import java.util.Map;
4
5Map<String, Boolean> flagDefaults = new HashMap<>();
6flagDefaults.put("some-flag-key", true);
7
8Schematic schematic = Schematic.builder()
9 .apiKey("YOUR_API_KEY")
10 .offline(true)
11 .flagDefaults(flagDefaults)
12 .build();
13
14boolean flagValue = schematic.checkFlag("some-flag-key", null, null); // Returns true

Event Buffer

Schematic API uses an Event Buffer to batch Identify and Track requests and avoid multiple API calls. You can set the event buffer flush period:

1import com.schematic.api.Schematic;
2import java.time.Duration;
3
4Schematic schematic = Schematic.builder()
5 .apiKey("YOUR_API_KEY")
6 .eventBufferInterval(Duration.ofSeconds(5))
7 .build();

Exception Handling

When the API returns a non-success status code (4xx or 5xx response), a subclass of SchematicException will be thrown.

1import com.schematic.api.core.SchematicException;
2
3try {
4 schematic.companies.getCompany(...);
5} catch (SchematicException e) {
6 System.out.println(e.message());
7}

The SDK also supports error handling for first class exceptions with strongly typed body fields.

1import com.schematic.api.errors.InvalidRequestError;
2
3try {
4 schematic.companies.getCompany(...);
5} catch (InvalidRequestError e) {
6 System.out.println(e.message());
7 System.out.println(e.getBody().getMissingField());
8}

Retries

The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long as the request is deemed retriable and the number of retry attempts has not grown larger than the configured retry limit (default: 2).

A request is deemed retriable when any of the following HTTP status codes is returned:

  • 408 (Timeout)
  • 429 (Too Many Requests)
  • 5XX (Internal Server Errors)