diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e08c794
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+target/
+.DS_Store
diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md
deleted file mode 100644
index aaa0a6e..0000000
--- a/IMPLEMENTATION_SUMMARY.md
+++ /dev/null
@@ -1,408 +0,0 @@
-# Java Client Implementation Summary
-
-This document summarizes the Java client library implementation based on the Go client at `client.go` and user-friendly Java library design best practices.
-
-## Research Summary
-
-### Java Library Design Best Practices
-
-Based on comprehensive research (sources below), the key principles applied:
-
-1. **Minimalism** - Simple, focused API with justified methods
-2. **Constructor Injection** - Dependencies through constructors (via builder pattern)
-3. **User-Centric Design** - Focus on developer experience and ease of use
-4. **Builder Pattern** - Fluent builders for objects with multiple parameters
-5. **Type Safety** - Strong typing with enums and POJOs
-6. **Clear Error Handling** - Structured exception hierarchy
-7. **Immutability** - Thread-safe immutable models where appropriate
-
-## Structure Overview
-
-### Multi-Module Maven Structure
-
-```
-gradientlabs-java/
-├── pom.xml # Parent POM (packaging=pom)
-├── gradient-labs-client/ # Core client library module
-│ ├── pom.xml
-│ └── src/main/java/
-│ └── ai.gradientlabs.client/
-│ ├── GradientLabsClient.java # Main client with fluent builder
-│ ├── exception/ # Exception hierarchy
-│ │ ├── GradientLabsException.java # Base exception
-│ │ ├── ResponseException.java # API error responses
-│ │ └── InvalidWebhookSignatureException.java
-│ ├── model/ # Domain models (POJOs)
-│ │ ├── Conversation.java, Message.java, Tool.java, Note.java
-│ │ ├── Channel.java, ConversationStatus.java, NoteStatus.java (enums)
-│ │ ├── ParticipantType.java, AttachmentType.java (enums)
-│ │ └── AgentMetadata.java, Attachment.java
-│ ├── request/ # Request builders
-│ │ ├── StartConversationRequest.java
-│ │ ├── AddMessageRequest.java
-│ │ ├── AssignmentRequest.java
-│ │ ├── CreateNoteRequest.java
-│ │ ├── UpdateNoteRequest.java
-│ │ ├── SetNoteStatusRequest.java
-│ │ └── ... (all operations)
-│ ├── webhook/ # Webhook handling
-│ │ ├── Webhook.java, WebhookType.java
-│ │ ├── WebhookVerifier.java # HMAC signature verification
-│ │ └── event/ # Event types
-│ │ ├── AgentMessageEvent.java
-│ │ ├── ConversationHandOffEvent.java
-│ │ └── ... (all event types)
-│ └── internal/ # Internal implementation
-│ └── HttpClientWrapper.java # HTTP client abstraction
-└── gradient-labs-spring-boot-starter/ # Spring Boot auto-configuration module
- ├── pom.xml
- ├── README.md
- └── src/main/
- ├── java/ai.gradientlabs.spring/
- │ ├── GradientLabsProperties.java # Configuration properties
- │ └── GradientLabsAutoConfiguration.java # Auto-configuration
- └── resources/META-INF/spring/
- └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
-```
-
-## Key Implementation Details
-
-### 1. Client Creation (Builder Pattern)
-
-**Go Pattern:**
-```go
-client, err := glabs.NewClient(
- glabs.WithAPIKey(apiKey),
- glabs.WithWebhookSigningKey(webhookKey),
-)
-```
-
-**Java Pattern:**
-```java
-GradientLabsClient client = GradientLabsClient.builder()
- .apiKey(apiKey)
- .webhookSigningKey(webhookKey)
- .build();
-```
-
-### 2. Request Objects (Immutable Builders)
-
-All request objects follow this pattern:
-- Immutable final fields
-- Builder for construction
-- Validation in `build()` method
-- Fluent helper methods (e.g., `addMetadata()`, `addResource()`)
-
-### 3. Error Handling
-
-**Hierarchy:**
-- `GradientLabsException` (unchecked base)
- - `ResponseException` - API errors with status code, message, details, trace ID
- - `InvalidWebhookSignatureException` - Webhook verification failures
-
-### 4. Webhook Verification
-
-Implements HMAC-SHA256 signature verification matching the Go implementation:
-- Parses `X-GradientLabs-Signature` header
-- Verifies timestamp is within leeway (default 5 minutes)
-- Computes and compares signature
-- Type-safe event parsing
-
-### 5. HTTP Client
-
-Uses Java 11+ `HttpClient` with:
-- Automatic JSON serialization/deserialization (Jackson)
-- Bearer token authentication
-- Custom User-Agent: `Gradient-Labs-Java/{version} (Java/{javaVersion})`
-- Synchronous and asynchronous variants
-
-### 6. Type Safety
-
-- Enums for all constants (Channel, ConversationStatus, ParticipantType, etc.)
-- Strong typing throughout
-- Jackson annotations for JSON mapping
-- Java 8+ time API (`Instant`, `Duration`)
-
-## Files Created
-
-### Core Library
-
-1. **Build Configuration**
- - `pom.xml` - Maven project configuration
-
-2. **Main Client** (1 file)
- - `GradientLabsClient.java` - Main client with builder
-
-3. **Exceptions** (3 files)
- - `GradientLabsException.java`
- - `ResponseException.java`
- - `InvalidWebhookSignatureException.java`
-
-4. **Models** (24 files)
- - `Conversation.java`, `AgentMetadata.java`
- - `Message.java`, `Attachment.java`
- - `Tool.java`, `Note.java`
- - `HandOffTarget.java`, `HandOffTargetsResponse.java`
- - `Procedure.java`, `ProcedureVersion.java`
- - `UserDetails.java`, `GatedConfig.java`
- - `PaginationInfo.java`
- - `Channel.java`, `ConversationStatus.java`, `NoteStatus.java`
- - `ParticipantType.java`, `AttachmentType.java`
- - `Visibility.java`, `PublicationStatus.java`, `UsageStatus.java`
- - `ProcedureStatus.java`
-
-4a. **Response Classes** (2 files, in client package)
- - `ProcedureListResponse.java` - Wraps procedures list with pagination
- - `ListProcedureVersionsResponse.java` - Wraps procedure versions list
-
-5. **Request Objects** (20 files)
- - `StartConversationRequest.java`
- - `AddMessageRequest.java`
- - `AssignmentRequest.java`
- - `EventRequest.java`
- - `FinishConversationRequest.java`
- - `ReadConversationRequest.java`
- - `CreateToolRequest.java`
- - `UpdateToolRequest.java`
- - `CreateNoteRequest.java`
- - `UpdateNoteRequest.java`
- - `SetNoteStatusRequest.java`
- - `UpsertHandOffTargetRequest.java`
- - `DeleteHandOffTargetRequest.java`
- - `SetDefaultHandOffTargetRequest.java`
- - `UpsertArticleRequest.java`
- - `UpsertArticleTopicRequest.java`
- - `SetArticleUsageStatusRequest.java`
- - `ListProceduresRequest.java`
- - `SetProcedureLimitRequest.java`
- - `SetProcedureGatedVersionRequest.java`
-
-6. **Webhook Handling** (9 files)
- - `Webhook.java`, `WebhookType.java`
- - `WebhookVerifier.java` - Signature verification
- - `WebhookConversation.java`
- - `AgentMessageEvent.java`
- - `ConversationHandOffEvent.java`
- - `ConversationFinishedEvent.java`
- - `ActionExecuteEvent.java`
- - `ResourcePullEvent.java`
-
-7. **Internal** (1 file)
- - `HttpClientWrapper.java` - HTTP operations (extended with DELETE body support)
-
-### Documentation (4 files)
-
-- `DESIGN.md` - Comprehensive design document
-- `README.md` - User documentation with examples
-- `IMPLEMENTATION_SUMMARY.md` - This file
-- `.gitignore` - Git ignore rules
-
-### Examples (6 files)
-
-- `BasicConversationExample.java` - Complete conversation flow
-- `WebhookHandlerExample.java` - Webhook handling
-- `NotesExample.java` - Note management operations
-- `HandOffTargetsExample.java` - Hand-off target management
-- `ArticlesExample.java` - Article and topic management for knowledge base
-- `ProceduresExample.java` - Procedure and version management for AI agent instructions
-
-## API Coverage
-
-### Implemented Operations
-
-✅ **Conversations**
-- Start, Read, Finish, Cancel, Resume, Rate
-- Add Message, Assign, Add Event
-
-✅ **Tools**
-- List, Create, Read, Update, Delete
-
-✅ **Notes**
-- Create, Update, Delete, Set Status
-- Support for draft/live/deleted status
-- Time-based relevance (valid_from, valid_to)
-- External ID mapping
-
-✅ **Hand-Off Targets**
-- List, Upsert (Create/Update), Delete
-- Set Default by Channel
-- Requires Management API key
-
-✅ **Articles**
-- Upsert Article (Create/Update), Delete
-- Upsert Article Topic (Create/Update)
-- Set Usage Status (enable/disable for AI agent)
-- Support for visibility levels (public/users/internal)
-- Support for publication status (draft/published)
-- Requires Integration API key
-
-✅ **Procedures**
-- List, Read (with pagination and filtering)
-- Set Limit (daily usage limits)
-- List Versions
-- Set/Unset Gated Version (gradual rollout)
-- Set/Unset Live Version
-- Support for gated and live version management
-- Requires Management API key
-
-✅ **Webhooks**
-- Parse and verify
-- All event types (agent.message, conversation.hand_off, etc.)
-
-### Spring Boot Integration
-
-✅ **Spring Boot Starter** (completed)
-- Auto-configuration for Spring Boot 3.x+
-- Type-safe configuration properties via `application.yml` or `application.properties`
-- Automatic bean creation with `@ConditionalOnMissingBean` for customization
-- IDE autocomplete support via configuration metadata
-- See [Spring Boot Starter README](gradient-labs-spring-boot-starter/README.md) for details
-
-**Configuration Example:**
-```yaml
-gradientlabs:
- api-key: ${GLABS_API_KEY}
- base-url: https://api.gradient-labs.ai
- webhook-signing-key: ${GLABS_WEBHOOK_KEY}
- webhook-leeway: 10m
-```
-
-**Usage Example:**
-```java
-@Service
-public class ConversationService {
- private final GradientLabsClient client;
-
- public ConversationService(GradientLabsClient client) {
- this.client = client; // Auto-injected
- }
-}
-```
-
-### Future Enhancements
-
-The following could be added in future versions:
-- Resource Sources/Types API
-- List Notes operation (with filtering/pagination)
-- Pagination helpers for list operations
-- Retry logic with exponential backoff
-
-## Key Differences from Go Client
-
-1. **Builders vs Functional Options**
- - Go uses functional options pattern
- - Java uses builder pattern (more idiomatic)
-
-2. **Error Handling**
- - Go returns `(result, error)` tuple
- - Java throws exceptions (unchecked)
-
-3. **Context**
- - Go uses `context.Context` for cancellation
- - Java uses method overloads or CompletableFuture
-
-4. **Time**
- - Go uses `time.Time` and `time.Duration`
- - Java uses `java.time.Instant` and `Duration`
-
-5. **JSON**
- - Go uses struct tags
- - Java uses Jackson annotations
-
-## Dependencies
-
-**Runtime:**
-- Jackson 2.18.2 (JSON)
-- SLF4J 2.0.16 (logging, optional)
-
-**Build:**
-- Java 11+ (LTS)
-- Maven or Gradle
-
-**Test:**
-- JUnit 5.11.4
-- Mockito 5.14.2
-
-## Validation
-
-The implementation follows best practices:
-- ✅ Minimal API surface
-- ✅ Type-safe throughout
-- ✅ Clear error messages with trace IDs
-- ✅ Comprehensive documentation
-- ✅ Complete examples
-- ✅ Thread-safe immutable models
-- ✅ No dependencies except Jackson
-- ✅ Java 11+ compatible
-
-## Usage Example
-
-```java
-// Create client
-GradientLabsClient client = GradientLabsClient.builder()
- .apiKey(System.getenv("GLABS_API_KEY"))
- .webhookSigningKey(System.getenv("GLABS_WEBHOOK_KEY"))
- .build();
-
-// Start conversation
-Conversation conv = client.startConversation(
- StartConversationRequest.builder()
- .id("conv-123")
- .customerId("user-456")
- .channel(Channel.CHAT)
- .build()
-);
-
-// Add message
-Message msg = client.addMessage(
- conv.getId(),
- AddMessageRequest.builder()
- .id("msg-789")
- .body("Hello!")
- .participantId("user-456")
- .participantType(ParticipantType.CUSTOMER)
- .build()
-);
-
-// Create a note
-Note note = client.createNote(
- CreateNoteRequest.builder()
- .externalId("note-001")
- .title("Support Hours")
- .body("Our support team is available Monday-Friday, 9am-5pm EST")
- .build()
-);
-
-// Handle webhook
-Webhook webhook = client.parseWebhook(request);
-if (webhook.getType() == WebhookType.AGENT_MESSAGE) {
- AgentMessageEvent event = webhook.asAgentMessage();
- System.out.println("Agent: " + event.getBody());
-}
-```
-
-## Research Sources
-
-The design is based on industry best practices from:
-
-- [Designing a User Friendly Java Library | Baeldung](https://www.baeldung.com/design-a-user-friendly-java-library)
-- [Java API Best Practices - DZone Refcards](https://dzone.com/refcardz/java-api-best-practices)
-- [API design: principles and best practices · YourBasic](https://yourbasic.org/algorithms/your-basic-api/)
-- [Google Best Practices for Java Libraries](https://jlbp.dev/)
-- [API design practices for Java - IBM](https://developer.ibm.com/articles/api-design-practices-for-java/)
-- [Best Practices for API Design in Java - JAVAPRO International](https://javapro.io/2025/06/04/best-practices-for-api-design-in-java/)
-- [Effective API Design in Java — A Guide to Creating Strong and User-Friendly APIs - Medium](https://medium.com/@AlexanderObregon/effective-api-design-in-java-a-guide-to-creating-robust-and-user-friendly-apis-e75942348bc0)
-
-## Next Steps
-
-To complete the implementation:
-
-1. **Add remaining models** - Procedure, ResourceSource, ResourceType
-2. **Add remaining operations** - Procedure, ResourceSource/Type APIs
-3. **Add tests** - Unit tests and integration tests
-4. **Add pagination helpers** - Iterator-style API for list operations
-5. **Add retry logic** - Configurable retry with exponential backoff
-6. **Publish to Maven Central** - For easy consumption
-7. **Create Spring Boot starter** - Auto-configuration for Spring apps
-
-The base structure is complete and ready for these enhancements!
diff --git a/gradient-labs-client/pom.xml b/gradient-labs-client/pom.xml
index 1021451..f325c56 100644
--- a/gradient-labs-client/pom.xml
+++ b/gradient-labs-client/pom.xml
@@ -7,7 +7,7 @@
+ * Deletion is performed asynchronously; a successful call is acknowledged + * with a 202 Accepted response. + *
+ * Note: Requires a Management API key. + * + * @param conversationId the conversation ID + * @throws GradientLabsException if the request fails + */ + public void deleteConversation(String conversationId) { + String path = String.format("/conversations/%s", conversationId); + httpClient.delete(path, Void.class); + } + // ==================== Tool Operations ==================== /** @@ -1123,6 +1139,22 @@ public BackOfficeTask readBackOfficeTask(String taskId) { return httpClient.get(path, null, BackOfficeTask.class); } + /** + * Deletes a back office task. + *
+ * Deletion is performed asynchronously; a successful call is acknowledged + * with a 202 Accepted response. + *
+ * Note: Requires a Management API key.
+ *
+ * @param taskId the task ID
+ * @throws GradientLabsException if the request fails
+ */
+ public void deleteBackOfficeTask(String taskId) {
+ String path = String.format("/back-office-tasks/%s", taskId);
+ httpClient.delete(path, Void.class);
+ }
+
// ==================== Voice Call Context Operations ====================
/**
diff --git a/gradient-labs-client/src/test/java/ai/gradientlabs/client/GradientLabsClientTest.java b/gradient-labs-client/src/test/java/ai/gradientlabs/client/GradientLabsClientTest.java
new file mode 100644
index 0000000..71f96b8
--- /dev/null
+++ b/gradient-labs-client/src/test/java/ai/gradientlabs/client/GradientLabsClientTest.java
@@ -0,0 +1,185 @@
+package ai.gradientlabs.client;
+
+import ai.gradientlabs.client.internal.HttpClientWrapper;
+import org.junit.jupiter.api.Test;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import java.net.Authenticator;
+import java.net.CookieHandler;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpHeaders;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.net.http.WebSocket;
+import java.time.Duration;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Exercises the DELETE plumbing that {@code deleteConversation} and
+ * {@code deleteBackOfficeTask} delegate to.
+ */
+class GradientLabsClientTest {
+
+ @Test
+ void deleteSendsDeleteRequestWithoutBodyAndReturnsNullOnAccepted() {
+ RecordingHttpClient httpClient = new RecordingHttpClient();
+ HttpClientWrapper wrapper = new HttpClientWrapper(httpClient, "test-key", "test-agent");
+
+ Void result = wrapper.delete("https://api.example.com/conversations/conv-123", Void.class);
+
+ assertNull(result);
+ assertEquals("DELETE", httpClient.lastRequest.method());
+ assertEquals("https://api.example.com/conversations/conv-123", httpClient.lastRequest.uri().toString());
+ assertTrue(httpClient.lastRequest.bodyPublisher().isPresent());
+ assertEquals(0, httpClient.lastRequest.bodyPublisher().get().contentLength());
+ }
+
+ @Test
+ void deleteSendsDeleteToBackOfficeTaskPath() {
+ RecordingHttpClient httpClient = new RecordingHttpClient();
+ HttpClientWrapper wrapper = new HttpClientWrapper(httpClient, "test-key", "test-agent");
+
+ wrapper.delete("https://api.example.com/back-office-tasks/task-456", Void.class);
+
+ assertEquals("DELETE", httpClient.lastRequest.method());
+ assertEquals("https://api.example.com/back-office-tasks/task-456", httpClient.lastRequest.uri().toString());
+ }
+
+ /**
+ * Minimal {@link HttpClient} that records the last request and always replies
+ * with an empty 202 Accepted response, mirroring the API's delete endpoints.
+ */
+ private static final class RecordingHttpClient extends HttpClient {
+ private HttpRequest lastRequest;
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public