entries) {
+ if (entries == null || entries.isEmpty()) return this;
+ if (query == null) query = new HashMap<>();
+ entries.forEach((k, v) -> { if (v != null) query.put(k, v); });
+ return this;
+ }
}
/** Configurable retry parameters. */
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/AccountNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/AccountNotFoundException.java
new file mode 100644
index 0000000..929f17a
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/AccountNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class AccountNotFoundException extends NotFoundException {
+ public AccountNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/BankConnectionNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/BankConnectionNotFoundException.java
new file mode 100644
index 0000000..fa5238f
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/BankConnectionNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class BankConnectionNotFoundException extends NotFoundException {
+ public BankConnectionNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/BankSubmissionException.java b/packages/java/src/main/java/com/tesote/sdk/errors/BankSubmissionException.java
new file mode 100644
index 0000000..8c1f386
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/BankSubmissionException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class BankSubmissionException extends UnprocessableContentException {
+ public BankSubmissionException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/BankUnderMaintenanceException.java b/packages/java/src/main/java/com/tesote/sdk/errors/BankUnderMaintenanceException.java
new file mode 100644
index 0000000..0811738
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/BankUnderMaintenanceException.java
@@ -0,0 +1,16 @@
+package com.tesote.sdk.errors;
+
+/**
+ * 503 — the upstream bank reported maintenance. Retry after the configured
+ * window. Distinct from the broader {@link ServiceUnavailableException}
+ * (which signals API-side pause mode).
+ */
+public final class BankUnderMaintenanceException extends ApiException {
+ public BankUnderMaintenanceException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/BatchNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/BatchNotFoundException.java
new file mode 100644
index 0000000..e3131bd
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/BatchNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class BatchNotFoundException extends NotFoundException {
+ public BatchNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/BatchValidationException.java b/packages/java/src/main/java/com/tesote/sdk/errors/BatchValidationException.java
new file mode 100644
index 0000000..900ec51
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/BatchValidationException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class BatchValidationException extends ValidationException {
+ public BatchValidationException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/CategoryNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/CategoryNotFoundException.java
new file mode 100644
index 0000000..9a58896
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/CategoryNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class CategoryNotFoundException extends NotFoundException {
+ public CategoryNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/CounterpartyNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/CounterpartyNotFoundException.java
new file mode 100644
index 0000000..f488ed0
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/CounterpartyNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class CounterpartyNotFoundException extends NotFoundException {
+ public CounterpartyNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/ErrorDispatcher.java b/packages/java/src/main/java/com/tesote/sdk/errors/ErrorDispatcher.java
index dd884e3..d8dd439 100644
--- a/packages/java/src/main/java/com/tesote/sdk/errors/ErrorDispatcher.java
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/ErrorDispatcher.java
@@ -44,12 +44,84 @@ public static ApiException dispatch(
case "INVALID_DATE_RANGE" -> new InvalidDateRangeException(
message, code, httpStatus, requestId, errorId, retryAfter,
responseBody, requestSummary, attempts, cause);
+ case "MISSING_DATE_RANGE" -> new MissingDateRangeException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "INVALID_CURSOR" -> new InvalidCursorException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "INVALID_COUNT" -> new InvalidCountException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "INVALID_LIMIT" -> new InvalidLimitException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "INVALID_QUERY" -> new InvalidQueryException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
case "UNPROCESSABLE_CONTENT" -> new UnprocessableContentException(
message, code, httpStatus, requestId, errorId, retryAfter,
responseBody, requestSummary, attempts, cause);
case "RATE_LIMIT_EXCEEDED" -> new RateLimitExceededException(
message, code, httpStatus, requestId, errorId, retryAfter,
responseBody, requestSummary, attempts, cause);
+ case "SYNC_RATE_LIMIT_EXCEEDED" -> new SyncRateLimitExceededException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "SYNC_IN_PROGRESS" -> new SyncInProgressException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "INVALID_ORDER_STATE" -> new InvalidOrderStateException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "VALIDATION_ERROR" -> new ValidationException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "BATCH_VALIDATION_ERROR" -> new BatchValidationException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "BANK_SUBMISSION_ERROR" -> new BankSubmissionException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "BANK_UNDER_MAINTENANCE" -> new BankUnderMaintenanceException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "INTERNAL_ERROR" -> new InternalErrorException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "ACCOUNT_NOT_FOUND" -> new AccountNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "TRANSACTION_NOT_FOUND" -> new TransactionNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "SYNC_SESSION_NOT_FOUND" -> new SyncSessionNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "PAYMENT_METHOD_NOT_FOUND" -> new PaymentMethodNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "TRANSACTION_ORDER_NOT_FOUND" -> new TransactionOrderNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "BATCH_NOT_FOUND" -> new BatchNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "BANK_CONNECTION_NOT_FOUND" -> new BankConnectionNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "CATEGORY_NOT_FOUND" -> new CategoryNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "COUNTERPARTY_NOT_FOUND" -> new CounterpartyNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "LEGAL_ENTITY_NOT_FOUND" -> new LegalEntityNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
+ case "WEBHOOK_NOT_FOUND" -> new WebhookNotFoundException(
+ message, code, httpStatus, requestId, errorId, retryAfter,
+ responseBody, requestSummary, attempts, cause);
default -> {
// why: 503 with no envelope code is the documented "pause mode"
// signal — surface it as ServiceUnavailableException so callers
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/InternalErrorException.java b/packages/java/src/main/java/com/tesote/sdk/errors/InternalErrorException.java
new file mode 100644
index 0000000..e041dd4
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/InternalErrorException.java
@@ -0,0 +1,14 @@
+package com.tesote.sdk.errors;
+
+/**
+ * 500 — server-side error. Includes {@link #errorId()} for support tickets.
+ */
+public final class InternalErrorException extends ApiException {
+ public InternalErrorException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/InvalidCountException.java b/packages/java/src/main/java/com/tesote/sdk/errors/InvalidCountException.java
new file mode 100644
index 0000000..4c81f05
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/InvalidCountException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class InvalidCountException extends UnprocessableContentException {
+ public InvalidCountException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/InvalidCursorException.java b/packages/java/src/main/java/com/tesote/sdk/errors/InvalidCursorException.java
new file mode 100644
index 0000000..c4349a9
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/InvalidCursorException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class InvalidCursorException extends UnprocessableContentException {
+ public InvalidCursorException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/InvalidLimitException.java b/packages/java/src/main/java/com/tesote/sdk/errors/InvalidLimitException.java
new file mode 100644
index 0000000..67cc27c
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/InvalidLimitException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class InvalidLimitException extends UnprocessableContentException {
+ public InvalidLimitException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/InvalidOrderStateException.java b/packages/java/src/main/java/com/tesote/sdk/errors/InvalidOrderStateException.java
new file mode 100644
index 0000000..31d29d3
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/InvalidOrderStateException.java
@@ -0,0 +1,15 @@
+package com.tesote.sdk.errors;
+
+/**
+ * 409 — order is not in a state that permits the attempted transition
+ * (e.g., submitting a non-draft order, cancelling a completed order).
+ */
+public final class InvalidOrderStateException extends ApiException {
+ public InvalidOrderStateException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/InvalidQueryException.java b/packages/java/src/main/java/com/tesote/sdk/errors/InvalidQueryException.java
new file mode 100644
index 0000000..6a5d70c
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/InvalidQueryException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class InvalidQueryException extends UnprocessableContentException {
+ public InvalidQueryException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/LegalEntityNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/LegalEntityNotFoundException.java
new file mode 100644
index 0000000..3006291
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/LegalEntityNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class LegalEntityNotFoundException extends NotFoundException {
+ public LegalEntityNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/MissingDateRangeException.java b/packages/java/src/main/java/com/tesote/sdk/errors/MissingDateRangeException.java
new file mode 100644
index 0000000..339bee9
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/MissingDateRangeException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class MissingDateRangeException extends UnprocessableContentException {
+ public MissingDateRangeException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/NotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/NotFoundException.java
new file mode 100644
index 0000000..bf70112
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/NotFoundException.java
@@ -0,0 +1,16 @@
+package com.tesote.sdk.errors;
+
+/**
+ * Resource-not-found family. Concrete subclasses correspond to specific
+ * {@code *_NOT_FOUND} error codes; callers may catch this base when the
+ * specific resource doesn't matter.
+ */
+public class NotFoundException extends ApiException {
+ public NotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/PaymentMethodNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/PaymentMethodNotFoundException.java
new file mode 100644
index 0000000..97f4324
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/PaymentMethodNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class PaymentMethodNotFoundException extends NotFoundException {
+ public PaymentMethodNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/SyncInProgressException.java b/packages/java/src/main/java/com/tesote/sdk/errors/SyncInProgressException.java
new file mode 100644
index 0000000..599f6ee
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/SyncInProgressException.java
@@ -0,0 +1,16 @@
+package com.tesote.sdk.errors;
+
+/**
+ * 409 — a sync session for the bank connection is already running.
+ * The active session id is in {@link #responseBody()} as
+ * {@code current_session_id}.
+ */
+public final class SyncInProgressException extends ApiException {
+ public SyncInProgressException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/SyncRateLimitExceededException.java b/packages/java/src/main/java/com/tesote/sdk/errors/SyncRateLimitExceededException.java
new file mode 100644
index 0000000..8498cdf
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/SyncRateLimitExceededException.java
@@ -0,0 +1,15 @@
+package com.tesote.sdk.errors;
+
+/**
+ * 429 — per-bank-connection sync was triggered too soon after the previous
+ * one. Distinct from the per-API-key {@link RateLimitExceededException}.
+ */
+public final class SyncRateLimitExceededException extends ApiException {
+ public SyncRateLimitExceededException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/SyncSessionNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/SyncSessionNotFoundException.java
new file mode 100644
index 0000000..e1d1b2b
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/SyncSessionNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class SyncSessionNotFoundException extends NotFoundException {
+ public SyncSessionNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/TransactionNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/TransactionNotFoundException.java
new file mode 100644
index 0000000..5c8396d
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/TransactionNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class TransactionNotFoundException extends NotFoundException {
+ public TransactionNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/TransactionOrderNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/TransactionOrderNotFoundException.java
new file mode 100644
index 0000000..e8d030d
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/TransactionOrderNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class TransactionOrderNotFoundException extends NotFoundException {
+ public TransactionOrderNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/ValidationException.java b/packages/java/src/main/java/com/tesote/sdk/errors/ValidationException.java
new file mode 100644
index 0000000..97d93f1
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/ValidationException.java
@@ -0,0 +1,15 @@
+package com.tesote.sdk.errors;
+
+/**
+ * Generic 400 {@code VALIDATION_ERROR} from the API. Specialized subclasses
+ * exist for batch / bank-submission validation.
+ */
+public class ValidationException extends ApiException {
+ public ValidationException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/errors/WebhookNotFoundException.java b/packages/java/src/main/java/com/tesote/sdk/errors/WebhookNotFoundException.java
new file mode 100644
index 0000000..119f07f
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/errors/WebhookNotFoundException.java
@@ -0,0 +1,11 @@
+package com.tesote.sdk.errors;
+
+public final class WebhookNotFoundException extends NotFoundException {
+ public WebhookNotFoundException(String message, String errorCode, int httpStatus,
+ String requestId, String errorId, Integer retryAfter,
+ String responseBody, RequestSummary requestSummary,
+ int attempts, Throwable cause) {
+ super(message, errorCode, httpStatus, requestId, errorId,
+ retryAfter, responseBody, requestSummary, attempts, cause);
+ }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/internal/Json.java b/packages/java/src/main/java/com/tesote/sdk/internal/Json.java
index 373c02f..8ed2b89 100644
--- a/packages/java/src/main/java/com/tesote/sdk/internal/Json.java
+++ b/packages/java/src/main/java/com/tesote/sdk/internal/Json.java
@@ -3,10 +3,15 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.core.type.TypeReference;
/**
* Thin wrapper around a single shared {@link ObjectMapper}. ObjectMapper is
* thread-safe once configured; sharing avoids re-allocating per request.
+ *
+ * No JSR-310 module: the SDK keeps jackson-databind as its only runtime
+ * dep, so date/time API fields are exposed as {@code String} on the model
+ * records and callers parse with {@link java.time.OffsetDateTime#parse}.
*/
public final class Json {
public static final ObjectMapper MAPPER = new ObjectMapper();
@@ -31,4 +36,31 @@ public static String stringify(Object value) {
throw new IllegalStateException("failed to serialize " + value.getClass(), e);
}
}
+
+ public static T treeToValue(JsonNode node, Class type) {
+ if (node == null || node.isMissingNode() || node.isNull()) return null;
+ try {
+ return MAPPER.treeToValue(node, type);
+ } catch (JsonProcessingException e) {
+ throw new IllegalStateException(
+ "failed to deserialize " + type.getSimpleName() + ": " + e.getMessage(), e);
+ }
+ }
+
+ public static T treeToValue(JsonNode node, TypeReference type) {
+ if (node == null || node.isMissingNode() || node.isNull()) return null;
+ try {
+ return MAPPER.readValue(MAPPER.treeAsTokens(node), type);
+ } catch (java.io.IOException e) {
+ throw new IllegalStateException("failed to deserialize: " + e.getMessage(), e);
+ }
+ }
+
+ public static byte[] toBytes(Object value) {
+ try {
+ return MAPPER.writeValueAsBytes(value);
+ } catch (JsonProcessingException e) {
+ throw new IllegalStateException("failed to serialize " + value.getClass(), e);
+ }
+ }
}
diff --git a/packages/java/src/main/java/com/tesote/sdk/internal/QueryParams.java b/packages/java/src/main/java/com/tesote/sdk/internal/QueryParams.java
new file mode 100644
index 0000000..4938f50
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/internal/QueryParams.java
@@ -0,0 +1,33 @@
+package com.tesote.sdk.internal;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Tiny builder for query-string maps. Skips null values so callers don't
+ * have to gate every {@code .put} on a not-null check.
+ */
+public final class QueryParams {
+ private final Map values = new LinkedHashMap<>();
+
+ public static QueryParams of() { return new QueryParams(); }
+
+ public QueryParams put(String key, String value) {
+ if (value != null) values.put(key, value);
+ return this;
+ }
+
+ public QueryParams put(String key, Number value) {
+ if (value != null) values.put(key, value.toString());
+ return this;
+ }
+
+ public QueryParams put(String key, Boolean value) {
+ if (value != null) values.put(key, value.toString());
+ return this;
+ }
+
+ public Map build() { return values; }
+
+ public boolean isEmpty() { return values.isEmpty(); }
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/models/Account.java b/packages/java/src/main/java/com/tesote/sdk/models/Account.java
new file mode 100644
index 0000000..04c4692
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/models/Account.java
@@ -0,0 +1,41 @@
+package com.tesote.sdk.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Account record (v1 and v2 share the same shape).
+ *
+ * Balance fields inside {@link AccountData} only appear when the
+ * workspace has {@code display_balances_in_api} enabled.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record Account(
+ @JsonProperty("id") String id,
+ @JsonProperty("name") String name,
+ @JsonProperty("data") AccountData data,
+ @JsonProperty("bank") Bank bank,
+ @JsonProperty("legal_entity") LegalEntity legalEntity,
+ @JsonProperty("tesote_created_at") String tesoteCreatedAt,
+ @JsonProperty("tesote_updated_at") String tesoteUpdatedAt
+) {
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public record AccountData(
+ @JsonProperty("masked_account_number") String maskedAccountNumber,
+ @JsonProperty("currency") String currency,
+ @JsonProperty("transactions_data_current_as_of") String transactionsDataCurrentAsOf,
+ @JsonProperty("balance_data_current_as_of") String balanceDataCurrentAsOf,
+ @JsonProperty("custom_user_provided_identifier") String customUserProvidedIdentifier,
+ @JsonProperty("balance_cents") String balanceCents,
+ @JsonProperty("available_balance_cents") String availableBalanceCents
+ ) {}
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public record Bank(@JsonProperty("name") String name) {}
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public record LegalEntity(
+ @JsonProperty("id") String id,
+ @JsonProperty("legal_name") String legalName
+ ) {}
+}
diff --git a/packages/java/src/main/java/com/tesote/sdk/models/AccountSyncResponse.java b/packages/java/src/main/java/com/tesote/sdk/models/AccountSyncResponse.java
new file mode 100644
index 0000000..d4ecc2c
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/models/AccountSyncResponse.java
@@ -0,0 +1,15 @@
+package com.tesote.sdk.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Response body for {@code POST /v2/accounts/{id}/sync}.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record AccountSyncResponse(
+ @JsonProperty("message") String message,
+ @JsonProperty("sync_session_id") String syncSessionId,
+ @JsonProperty("status") String status,
+ @JsonProperty("started_at") String startedAt
+) {}
diff --git a/packages/java/src/main/java/com/tesote/sdk/models/AccountsPage.java b/packages/java/src/main/java/com/tesote/sdk/models/AccountsPage.java
new file mode 100644
index 0000000..8e1d2c8
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/models/AccountsPage.java
@@ -0,0 +1,13 @@
+package com.tesote.sdk.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record AccountsPage(
+ @JsonProperty("total") Integer total,
+ @JsonProperty("accounts") List accounts,
+ @JsonProperty("pagination") PagePagination pagination
+) {}
diff --git a/packages/java/src/main/java/com/tesote/sdk/models/BatchActionResponse.java b/packages/java/src/main/java/com/tesote/sdk/models/BatchActionResponse.java
new file mode 100644
index 0000000..68f740d
--- /dev/null
+++ b/packages/java/src/main/java/com/tesote/sdk/models/BatchActionResponse.java
@@ -0,0 +1,22 @@
+package com.tesote.sdk.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Response shape for batch approve / submit / cancel.
+ * Different actions populate different counts; cancel additionally fills
+ * {@code skipped} and may include per-order {@code errors}.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record BatchActionResponse(
+ @JsonProperty("approved") Integer approved,
+ @JsonProperty("enqueued") Integer enqueued,
+ @JsonProperty("cancelled") Integer cancelled,
+ @JsonProperty("failed") Integer failed,
+ @JsonProperty("skipped") Integer skipped,
+ @JsonProperty("errors") List