Java
The official client for Java.
This is the official Java client. It needs Java 11 or newer, and its only dependency is Gson. Models are in com.vojomedia.model, and request builders in com.vojomedia.param.
Install
The library isn't published to Maven Central yet. Until it is, ask your Vojo contact for a copy.
Make your first call
import com.vojomedia.VojoClient;
import com.vojomedia.model.Organisation;
VojoClient client = VojoClient.create(System.getenv("VOJO_KEY"));
Organisation organisation = client.organisation().retrieve();
System.out.println(organisation.getName());VojoClient.fromEnv() reads VOJO_SECRET_KEY instead.
Call the API
Methods follow the API's paths, and request builders use the API's field names in camelCase. Each reference page shows its method. Most methods return the resource itself, and statistics and issues keep getData() and getMeta().
Site site = client.sites().create(CreateSiteRequest.builder().name("Leeds Trinity").build());
Screen screen = client.screens().create(CreateScreenRequest.builder()
.name("Lobby North")
.siteId(site.getId())
.sizeInches(55.0)
.build());
Screen same = client.screens().retrieve(screen.getId());
// Fields you don't set are unchanged. Setting null sends null.
client.screens().update(screen.getId(), UpdateScreenRequest.builder().name("Lobby South").notes(null).build());Page through a list
Looping walks every item across every page, fetching more as it goes. .stream() works too.
for (Screen screen : client.screens().list(ScreensListParams.builder().hasHarvester(true).build())) {
System.out.println(screen.getName());
}firstPage gives you one page, if you want to page yourself.
Page<Screen> page = client.screens().list(ScreensListParams.builder().limit(100).build()).firstPage();
System.out.println(page.getData().size() + " " + page.hasMore());Handle errors
Every exception derives from the unchecked VojoException.
try {
client.screens().retrieve("scr_missing");
} catch (NotFoundException e) {
System.out.println(e.getCode() + " " + e.getRequestId());
} catch (RateLimitException e) {
e.getRetryAfter().ifPresent(wait -> System.out.println("wait " + wait));
} catch (ApiException e) {
System.out.println(e.getStatus() + " " + e.getCode());
}| Exception | When |
|---|---|
AuthenticationException |
401 |
PermissionException |
402 or 403 |
NotFoundException |
404 |
ValidationException |
400, 413 or 422 |
ConflictException |
409 |
RateLimitException |
429: see getRetryAfter() |
ServerException |
5xx |
ApiConnectionException, ApiTimeoutException |
No response |
PaginationException |
The API's paging data couldn't be followed safely |
WebhookVerificationException |
A webhook delivery failed verification: see getKind() |
API errors carry getStatus(), getCode(), getProblem() (the full problem response), getRequestId() and getHeaders(). Always handle a code you haven't seen, because new ones can be added.
Verify webhooks
Verifying needs only the endpoint's signing secret, not an API key. Pass the raw request body, exactly as you received it. This example is from a servlet.
byte[] body = request.getInputStream().readAllBytes();
Event event;
try {
event = Webhook.constructEvent(body, request.getHeader(Webhook.SIGNATURE_HEADER), signingSecret);
} catch (WebhookVerificationException e) {
response.setStatus(400);
return;
}
if (EventTypes.VERIFICATION_SCORE.equals(event.getType())) {
VerificationScoreEvent score = event.as(VerificationScoreEvent.class);
}
response.setStatus(204);Deliveries can repeat, so deduplicate on event.getId(), which stays the same across retries. While you switch to a new secret after a roll, hold both: Webhook.builder().secret(newSecret).secret(oldSecret).build().constructEvent(body, header).
To test your own handler, sign a payload with Webhook.generateTestHeader(body, secret, timestampSeconds).
Retries and idempotency
Failed requests are retried twice when that's safe: connection failures, 429, and 5xx on operations that can't apply twice.
Every create is sent with an Idempotency-Key, reused across its retries. To retry across your own restarts, supply your own.
Site site = client.sites().create(
CreateSiteRequest.builder().name("Leeds Trinity").build(),
RequestOptions.builder().idempotencyKey("site-" + yourRecordId).build());Configure the client
VojoClient client = VojoClient.builder()
.secretKey(key)
.organisation("org_123") // OAuth access tokens only.
.version("2026-10-07") // Default: the version this release was built against.
.timeout(Duration.ofSeconds(30)) // Default 60 seconds.
.maxRetries(5) // Default 2.
.baseUrl("http://127.0.0.1:4020") // For example, the mock server.
.build();
// For one call.
client.screens().retrieve(screenId, RequestOptions.builder().timeout(Duration.ofSeconds(5)).build());tokenSupplier(...) reads the token on each request, for OAuth tokens that refresh.
Results carry the HTTP details on getLastResponse(): status, headers, request id, meta and whether it was an idempotent replay. Check getMeta() after you wrap a creative, and quote the request id when you contact Vojo.