⌘K

Java SDK

Integrate document ingestion and talent-graph queries into JVM applications with the synchronous Java client.

Every call works in your company workspace, which is identified by your API key. Requires Java 17 or later.

Install

Version 1.0.0 is on Maven Central. Add this dependency to your pom.xml:

xml
<dependency>
  <groupId>ai.workerbee</groupId>
  <artifactId>workerbee-sdk</artifactId>
  <version>1.0.0</version>
</dependency>

For Gradle:

kotlin
repositories {
    mavenCentral()
}
dependencies {
    implementation("ai.workerbee:workerbee-sdk:1.0.0")
}

To use the library without a build tool, download the JAR (ai.workerbee:workerbee-sdk:1.0.0); Maven and Gradle resolve its dependencies automatically. The Java package is ai.workerbee.sdk. To upgrade, change the dependency version. The source is in the workerbee-sdk repository on GitHub.

Configure authentication

Create a key in Settings → Workspaces & SDK → SDK API keys. Upload-and-query examples need files:write, files:read, and graph:read. Restrict the key to Default or allow all company workspaces. Store the full key in your application’s secret manager or environment.

bash
export WORKERBEE_API_KEY="YOUR_API_KEY"

A key restricted to workspaces other than Default can’t use these methods, and its restrictions aren’t widened automatically.

java
try (var wb = Workerbee.builder()
        .timeout(Duration.ofSeconds(60))
        .maxRetries(3)
        .build()) {
    for (var file : wb.listFiles()) {
        System.out.println(file.id() + " " + file.status());
    }
}

The client reads your key from WORKERBEE_API_KEY; the builder also accepts apiKey(...). This fragment uses the imports shown in the complete quickstart below. Use Duration for timeouts. Collections are lazy Iterable values.

Upload a resume and query skills

Add Quickstart.java to your application, set the key, and run it with the SDK and its dependencies on the classpath. Put a resume at ./resume.pdf.

java
import ai.workerbee.sdk.*;
import java.nio.file.Path;
import java.time.Duration;
import java.util.List;

public class Quickstart {
    public static void main(String[] args) {
        // Production by default; reads WORKERBEE_API_KEY.
        try (var wb = Workerbee.builder().build()) {
            var resume = wb.uploadFile(Path.of("./resume.pdf"), new UploadOptions()
                .documentTypes("resume")
                .externalId("employee-123")
                .sourceSystem("hris")
                .sourceDocumentId("employee-123-resume")
                .sourceRevision("v1")
                .idempotencyKey("employee-123-resume-v1"));
            System.out.println(resume.id() + " " + resume.status());
            resume.waitUntilReady(Duration.ofMinutes(10), Duration.ofSeconds(2));

            var response = wb.chat(
                List.of(Message.user("List this employee's skills")),
                new Subject("employee-123", "hris"), 20);
            if (response.refused()) {
                System.out.println(response.raw().path("refusal_reason").asText());
            } else if (!response.clarification().isMissingNode()
                    && !response.clarification().isNull()) {
                System.out.println(response.clarification());
            } else {
                System.out.println(response.interpretation());
                response.rows().forEach(System.out::println);
                System.out.println(response.references());
            }
        }
    }
}
Processing is asynchronous

A wait succeeds at PROCESSED. Inspect adapter outcomes, readiness, and has_queryable_content: stored-only or irrelevant content may not appear in answers, and graph visibility may lag ingestion. A wait timeout does not cancel server processing.

Document types and formats

Document typeHow to use it
resumeEmployee resume. Use a stable employee external ID and source system.
job_descriptionJob description. Use your job or requisition external ID.
performance_review / reviewEmployee review. Supply employee identity. Reviews are linked to the employee, but their evidence isn’t added to graph queries yet.
call_transcriptWritten call or meeting transcript; no audio transcription. Currently stored with a deferred adapter and no searchable graph content.
otherGeneric content or ZIP default. Use this type on its own.
java
try (var wb = Workerbee.builder().build()) {
    var jd = wb.uploadFile(Path.of("./job-description.pdf"), new UploadOptions()
        .documentTypes("job_description")
        .externalId("requisition-456").sourceSystem("hris"));
    var review = wb.uploadFile(Path.of("./review.pdf"), new UploadOptions()
        .documentTypes("performance_review")
        .externalId("employee-123").sourceSystem("hris"));
    System.out.println(jd.id() + " " + review.id());
}

Java uses fluent camelCase setters: documentTypes, externalId, sourceSystem, sourceDocumentId, sourceRevision, and idempotencyKey. Wire fields use snake_case; the SDK handles that conversion.

Declare types in document_types, not inside metadata. Supported formats include PDF, DOCX, TXT, Markdown, JSON, and ZIP; SRT and VTT are accepted as text. JSON is document text, not an arbitrary graph-property payload. Legacy DOC files, scanned PDFs, audio and video aren’t supported.

Default limitValue
Single document25 MiB
ZIP upload100 MiB
Expanded ZIP500 MiB
ZIP entries200

Limits can differ by deployment. Nested archives and unsafe paths are rejected. In a mixed resume/JD document, provide typed subjects for employee and job identities. A mixed document is not a ZIP and does not trigger ZIP matching.

Source revisions and retries

Keep external_id and source_system stable across an employee’s uploads and queries. Names alone are not reliable identities. source_document_id identifies a source document; source_revision identifies an immutable version. Change the revision when bytes change; reusing one for different content returns SOURCE_REVISION_CONFLICT.

Use an idempotency_key for retries of the same upload request. Reusing it for different input is a conflict. The clients retry eligible safe requests and transient failures; unkeyed upload creation is not blindly retried.

ZIP uploads and matching

For mixed-document ZIP uploads, include workerbee-manifest.json at the archive root so Workerbee knows the document type and identity of each included file. Declare every document using its exact archive path, including subdirectories. An entry’s types replace the ZIP default; an entry omitted from the manifest inherits that default, so do not rely on filenames to identify resumes or job descriptions. The manifest itself is control information, not an ingested document.

json
{
  "files": {
    "resumes/employee-123.pdf": {
      "document_types": ["resume"],
      "external_id": "employee-123",
      "metadata": {"match_job_paths": ["jobs/platform-engineer.pdf"]}
    },
    "jobs/platform-engineer.pdf": {
      "document_types": ["job_description"],
      "external_id": "requisition-456"
    }
  }
}
text
hiring-bundle.zip
├── workerbee-manifest.json
├── resumes/employee-123.pdf
└── jobs/platform-engineer.pdf

The example below packages local ./resume.pdf and ./job-description.pdf under those paths, writes the manifest, then uploads the ZIP with the generic other default and hris source system. Per-file declarations select the resume and job-description processing. One invalid child does not discard successful siblings.

Use this fragment inside a method declared with throws java.io.IOException, with the imports from the quickstart. ZIP creation uses the Java standard library.

java
String manifest = """
    {
      "files": {
        "resumes/employee-123.pdf": {
          "document_types": ["resume"],
          "external_id": "employee-123",
          "metadata": {"match_job_paths": ["jobs/platform-engineer.pdf"]}
        },
        "jobs/platform-engineer.pdf": {
          "document_types": ["job_description"],
          "external_id": "requisition-456"
        }
      }
    }
    """;
try (var bundle = new java.util.zip.ZipOutputStream(
        java.nio.file.Files.newOutputStream(Path.of("hiring-bundle.zip")))) {
    bundle.putNextEntry(new java.util.zip.ZipEntry("workerbee-manifest.json"));
    bundle.write(manifest.getBytes(java.nio.charset.StandardCharsets.UTF_8));
    bundle.closeEntry();
    bundle.putNextEntry(new java.util.zip.ZipEntry("resumes/employee-123.pdf"));
    java.nio.file.Files.copy(Path.of("./resume.pdf"), bundle);
    bundle.closeEntry();
    bundle.putNextEntry(new java.util.zip.ZipEntry("jobs/platform-engineer.pdf"));
    java.nio.file.Files.copy(Path.of("./job-description.pdf"), bundle);
    bundle.closeEntry();
}

try (var wb = Workerbee.builder().build()) {
    var archive = wb.uploadFile(Path.of("./hiring-bundle.zip"), new UploadOptions()
        .documentTypes("other").sourceSystem("hris"));
    try {
        archive.waitUntilReady(Duration.ofMinutes(10), Duration.ofSeconds(2));
    } catch (WorkerbeeException error) {
        if (error.kind() != WorkerbeeException.Kind.PARTIAL_SUCCESS) throw error;
        archive = error.file();
    }
    for (var child : archive.childrenFiles()) {
        System.out.println(child.data().path("archive_path").asText()
            + " " + child.id() + " " + child.status());
    }
    System.out.println(archive.adapters());
}

With ZIP auto-matching enabled, a ZIP containing one JD and resumes adds successfully processed employees to that JD’s Internal Talent pool. Multiple JDs require explicit metadata.match_job_paths on each resume; paths must identify JDs in the same ZIP. Resume-only archives do not assign employees to a job.

Matching runs separately from ingestion. Inspect the parent’s archive_matching adapter: PENDING/RUNNING, NEEDS_REVIEW, or SUCCEEDED. Retryable scoring failures are recovered by the backend. A processed ZIP can still have matching in progress.

For individual uploads, open Workspaces & SDK → Employees, choose an employee and job, then select Match and open ranking. The SDK doesn’t expose employee-profile or direct job-matching methods; chat is not a replacement for a match operation.

Manage files

java
try (var wb = Workerbee.builder().build()) {
    for (var file : wb.listFiles("PROCESSED", 50)) {
        System.out.println(file.id() + " " + file.filename());
    }
    var file = wb.getFile("YOUR_FILE_ID");
    file.refresh();
    file.download(Path.of("./downloaded-source.pdf"));
    // Only when intended:
    // file.retry();
    // file.delete();
}

Lists paginate as you iterate. file.data() returns a JSON snapshot; adapters() exposes processing outcomes. Retrying failed processing preserves successful adapters. Deleting a source does not remove an employee from an existing job pool.

Chat responses

Check refusal and clarification before consuming rows. interpretation describes the query; row fields depend on the question. references retain available source identities, and audit provides query context. Not every result or aggregate has a reference.

A subject selects the employee using the same external ID and source system used for ingestion. Without one, supported inventory queries may be company-wide. Workspace restrictions control resource access; do not treat workspace selection or a chat subject as a department-level privacy boundary.

Java returns Jackson JsonNode values for rows, references, and clarification. response.raw() preserves fields such as refusal reason, audit, primitive, and graph. Access known fields with path(...) rather than assuming a fixed row class.

Handle errors and processing states

OutcomeAction
PROCESSEDInspect adapters and queryable-content indicators before expecting graph results.
NEEDS_REVIEWResolve identity or processing ambiguity in the Work Intelligence Console (WIC).
PARTIAL_SUCCESSInspect failed adapters or ZIP children; preserve successful results.
FAILEDRead the error and retry only when appropriate.
DELETEDThe source was deleted; stop polling.
Wait timeoutProcessing continues. Refresh the same file later.
java
try (var wb = Workerbee.builder().build()) {
    var file = wb.getFile("YOUR_FILE_ID");
    file.waitUntilReady(Duration.ofMinutes(10), Duration.ofSeconds(2));
} catch (WorkerbeeException error) {
    System.err.println(error.kind() + " " + error.code());
    System.err.println("Request ID: " + error.requestId());
    if (error.kind() == WorkerbeeException.Kind.READY_TIMEOUT) {
        System.err.println("Processing continues on the server.");
    }
    if (error.file() != null) {
        System.err.println(error.file().status());
        System.err.println(error.file().adapters());
    }
}

kind() distinguishes authentication, permission, validation, conflict, rate-limit, connection, server, upload, and processing failures. status(), code(), requestId(), retryable(), and details() retain available error context. A processing failure includes file().

Last updated