User.getPasswordSet()/setPasswordSet(Boolean)expose theis_password_setproperty thatGET /users/selfreturns. It isfalsefor an account that can only sign in through a social provider, so a caller can tell whether the email/password and change-password routes apply before offering them.BaseResourcegains threeprotectedhelpers for subclasses and resources:orEmpty(List)normalizes an array endpoint's"data": nullto an empty list, andsignerAccessCodeQuery(String)/optionalSignerAccessCodeQuery(String)build thesigner-access-codequery map, with the required variant rejecting a blank code. The parameter name is available as theSIGNER_ACCESS_CODEconstant.
DocumentActivity.getPayload()documents that its value is a JSON object for most events but a JSON array for others, such asdocument_prepared. It stays typed asObjectfor that reason; deserializing it as a map fails on the array form and takes the wholedocuments.activities(...)call with it.getOrigin()documents itsipanduser-agentkeys and that it is null for server-generated events.- Every list-returning method builds its non-null result through the shared
orEmpty(...), replacing twelve copies of the same null check and the two different empty-list idioms they used. signerSelf,assignments, andfieldsbuild thesigner-access-codequery through the sharedBaseResourcehelpers instead of three private variants of the same code.
docs/API_REFERENCE.mdrecordsis_password_setonUser, the twoDocumentActivitypayload shapes, and the two digital-certificate routes the API deploys without publishing a schema for, which is why the SDK does not wrap them.README.mdand the POM describe what this client wraps as the Assinafy API, which is what the API calls itself; there is no separately named "Assinafy Webforms API". Thewebformsin the artifact name is historical and the README says so.README.mdcompares this client against the currentcom.assinafy:assinafy-sdk. Both cover all 89 operations behind the sameAssinafyClient, but they are not drop-in equivalents: configuration differs, and automatic retry on 429/503 exists only here while a pluggable logger and a sandbox-URL constant exist only there.README.mdadds the release-profile command that gates Javadoc separately fromverify.
- 221 mock-backed unit tests + 34 live sandbox tests, green on JDK 25. A new test pins both
DocumentActivitypayload shapes. LiveSmokeTest.templateDocumentRoundTriptreats a workspace with no template as an unmet precondition and skips, matching every other environment check in the suite. The API publishes no template-creation route, so a workspace without one cannot reach this flow at all.
signers.create(...)now always sends the creation request. It previously looked the signer up by email first and returned the existing record instead of creating one, so the same call meant "create" or "fetch" depending on workspace state. Creating a signer whose email already exists now raisesApiException. Callsigners.findOrCreate(payload[, accountId])where the previous find-then-create behaviour is what you want;uploadAndRequestSignatures(...)uses it internally, so that workflow is unchanged.DocumentStatsRowexposes the full KPI set returned by the stats endpoints: notification counts (email,whatsapp,bypass), verification counts (email,whatsapp,bypass,digital_certificate), viewed, completed, and certified.getSignatureRequestsEmail()andgetSignatureRequestsWhatsapp()remain as short accessors for the notification counters.ResendCostEstimateextendsCostEstimate, so a resend estimate exposes the complete cost breakdown and balances alongside itstotalandhas_sufficient_creditsaccessors.documents.replaceTags(...)anddocuments.appendTags(...)document that the API accepts tag IDs.BaseResource.normalizeBaseUrl(String)is the single base-URL rule, used by bothAssinafyClientand every resource.UploadAndRequestSignaturesResult.getSignerIds()returns an unmodifiable copy of the supplied list.- Removed
protectedtransport overloads fromBaseResourcethat had no callers (httpPostBinary, theClass-typed and query-parameterhttpDeletevariants, the no-queryhttpGetList, and the no-queryhttpPutVoid). Only a third-party subclass ofBaseResourcecould reference them.
signers.findOrCreate(payload[, accountId])— searches by exact case-insensitive email, creates only when absent, and recovers from a duplicate-email response caused by a concurrent creator.
documents.isFullySigned(...)anddocuments.getSigningProgress(...)no longer throwNullPointerExceptionwhen the API answers with a"data": nullenvelope; they report "not signed" and an empty progress instead.documents.waitUntilReady(...)keeps polling in the same case.signers.update(...)andsigners.findByEmail(...)reject a malformed email with a message naming the field, andsigners.findByEmail(...)now walks every page the API reports rather than only the first 100 results.signerSelf.verifyEmail(...)accepts verification codes containing characters outside the URL-safe path set; the code travels in the request body, so the path-segment rule did not apply.fields.validateMultiple(...)rejects an entry with a missingfield_idbefore the request is sent.documents.sendToken(...)rejects achannelother thanemailorwhatsappbefore the request is sent. The endpoint requires{recipient, channel}; an{email}body is answered with HTTP 400.uploadAndRequestSignatures(...)deletes the uploaded document when a later stage fails, attaching any cleanup failure to the original exception as a suppressed exception.AssinafyClientOptions.maxRetriesrejects a negative value instead of silently treating it as zero.
- Added the
sandbox-liveGitHub Actions workflow (manual dispatch,sandboxenvironment) mirroring the GitLabsandbox:livejob, and scoped CIpushbuilds to the default branch and version tags so a pull request builds once. - Reproducible-build timestamp updated to the release date.
- 220 mock-backed unit tests + 34 live smoke tests (skipped without credentials), green on JDK 25.
documents.waitUntilReady(...)no longer sleeps past its deadline: the final wait is clamped to the time remaining, so the method returns or times out withinmaxWaitMs.
- Account endpoints (
client.accounts): list, create, get, update, delete, theme, logo download/upload/ delete, and account document KPIs. - User endpoints (
client.users): authenticated profile, notification preferences (get and merge-update), and cross-account document KPIs. - Typed assignment building:
CollectAssignmentEntry,CollectFieldPlacement,DisplaySettings,AssignmentSignEntry, andTemplateEditorFieldreplace hand-built maps for collect assignments, item signing, and template editor fields. AccountPayload,AccountTheme,DocumentStatsRow, andNotificationPreferencesmodels.docs/API_REFERENCE.md: the SDK-to-endpoint map, request payload catalog, and response field catalog.
- Java 25 is the bytecode baseline (was Java 21); the build enforces JDK 25 or newer.
- OkHttp 4.12.0 → 5.5.0 via the
okhttp-jvmartifact, with an explicitokio-jvmdependency; Jackson 2.22.2, JUnit Jupiter 6.1.3. - Every Maven plugin is version-pinned, and the release profile fails the build on any Javadoc warning.
This is a breaking release: several methods that returned untyped Map<String, Object> now return typed
models, one method that targeted a non-existent route was removed, and a signer-facing payload's field names
were corrected.
- Typed returns replace raw
Map<String, Object>(the SDK is now uniformly typed):assignments.estimateCost(...)→CostEstimate(wasMap).documents.estimateCostFromTemplate(...)→CostEstimate(wasMap).assignments.estimateResendCost(...)→ResendCostEstimate(wasMap) withtotal,breakdown,credit_balance, andhas_sufficient_creditsfields.assignments.resendNotification(...)→ResendResult(wasMap).documents.verify(...)→DocumentVerification(wasMap).
tags.delete(...),documents.detachTag(...), anddocuments.sendToken(...)now returnvoid(wereMap<String, Object>). The server response carried only a boolean/echo already asserted by the envelope path.- Removed
webhooks.deleteSubscription(). It targetedDELETE /accounts/{id}/webhooks/subscriptions, a route the API does not define. Usewebhooks.inactivate()(PUT .../inactivate) to stop deliveries — there is no hard-delete server-side. signerSelf.confirmSignerData(...)now returns the updatedSigner(wasvoid), andConfirmSignerDataPayloadfields are corrected to the endpoint's real schema:full_name,email,government_id(removedwhatsapp_phone_numberandhas_accepted_terms, which the endpoint ignores).WebhookSubscriptionno longer exposesgetId()/getCreatedAt()— the API models the subscription as a singleton per account and returns neither field (both getters always returnednull).
signerSelf.acceptTerms(...)andsignerSelf.verifyEmail(...)now send thesigner-access-codeas the required query parameter instead of in the JSON body. Per the API's security scheme the code is a query parameter, so the previous body placement failed authentication against the real API.verifyEmailsends only{"verification-code": ...}in the body;acceptTermssends no body.fields.validateMultiple(...)now serializes anullvalue as{"field_id": ..., "value": null}instead of dropping thevaluekey (@JsonInclude(NON_NULL)was removed fromFieldValidationPayload). The API requires the key to be present, so anullvalue previously produced an HTTP 400 — inconsistent with the singlevalidate(...)path, which already worked.- The void and binary transport paths now surface an error envelope returned under HTTP 200.
executeVoid/executeBinaryinspect the envelope status (as the typed path already did), so a{"status": 4xx, ...}body under HTTP 200 on a delete/download raisesApiExceptioninstead of being swallowed (or returned as if it were the artifact). ApiException.getRetryAfterSeconds()is only populated on retryable statuses (429/503). It previously fell back to the always-presentX-Rate-Limit-Resetheader on every error, so a permanent 400/401 wrongly reported a retry hint. A caller keying retries on its presence no longer backs off on non-retryable failures.
- New endpoint coverage:
documents.rename(documentId, name)→PATCH /documents/{id}(and a newhttpPatchtransport helper).documents.search(params[, accountId])→GET /accounts/{id}/documents/search(lightweight search).assignments.list([params][, accountId])→GET /assignments(sends the account context as the camelCaseaccountIdquery parameter, as the API requires).signerSelf.searchDocuments(signerId, signerAccessCode, term)→GET /signers/{id}/documents/search.auth.linkSocialLogin(provider, token)→POST /auth/link-social-login.webhooks.update(...)— discoverability alias forregister(...)(the spec names the PUT "Update").
Signer.getSignatureReusable()(is_signature_reusablefromGET /signers/self), and anuploadSignature(..., Boolean reuse)overload that sets thereusequery parameter.- New typed models:
CostEstimate,CostEstimateBreakdownItem,DocumentVerification,ResendCostEstimate,ResendResult.
- Dependency bumps (patch): Jackson Databind 2.22.0 → 2.22.1, JUnit Jupiter 6.1.0 → 6.1.2. AssertJ (3.27.7),
and the compiler/surefire plugins are kept on their latest stable releases (the newer 4.0.0-M1 /
4.0.0-beta / 3.6.0-M1 artifacts are pre-releases). OkHttp remains on 4.12.0 — 5.x publishes as a
Kotlin-Multiplatform artifact and requires switching to
okhttp-jvmplus MockWebServer API changes; tracked separately. - GitHub Actions bumped and re-pinned to commit SHAs:
actions/checkoutv6.0.3 → v7.0.0,actions/setup-javav5.2.0 → v5.6.0 (in bothci.ymlandrelease.yml). Least-privilege permissions, the JDK 21 + 25 matrix, concurrency, and the GitLab CI mirror are unchanged. - Reproducible-build timestamp updated to the 2.0.0 release date.
- 154 mock-backed unit tests + 20 live smoke tests (skipped without env vars). All green on JDK 21 and 25.
- Dependency maintenance (Dependabot), all verified green on JDK 21 + 25:
- JUnit Jupiter 5.11.4 → 6.1.0 (test scope; JUnit 6 requires Java 17+, satisfied by the Java 21 baseline)
- Jackson Databind 2.18.2 → 2.22.0
- AssertJ 3.27.3 → 3.27.7
- maven-compiler-plugin 3.13.0 → 3.15.0, maven-surefire-plugin 3.5.2 → 3.5.6, maven-source-plugin 3.3.1 → 3.4.0, maven-javadoc-plugin 3.11.2 → 3.12.0, maven-enforcer-plugin 3.5.0 → 3.6.3
- GitHub Actions: actions/checkout v4.2.2 → v6.0.3, actions/setup-java v4.7.1 → v5.2.0 (SHA-pinned)
- OkHttp 4.12.0 → 5.x is not taken yet: OkHttp 5 publishes as a Kotlin-Multiplatform artifact, so a plain
Maven build resolves
com.squareup.okhttp3:okhttp:5.xto a classless KMP root (package okhttp3 does not exist). Migrating requires switching to theokhttp-jvmartifact and adapting to MockWebServer API changes. Tracked separately.
- Removed
AssignmentResource.get(documentId, assignmentId, signerAccessCode). It targeted an undocumented route that returns HTTP 404 for every call. Useclient.signerSelf.getSign(signerAccessCode)(GET /sign), whoseDocumentDetailscarries the signer-facing assignment view. SignerSelfResource.uploadSignature(...)now returnsvoid(wasbyte[]).POST /signaturereturns a JSON envelope, not an artifact; the method now parses that envelope and raisesApiExceptionon an error (including an error envelope returned under HTTP 200) instead of returning the raw JSON bytes.
AssignmentResource.resetExpirationnow accepts anullexpires_atto clear an assignment's expiration and sends{"expires_at": null}instead of failing. AddedclearExpiration(documentId, assignmentId)for that intent.AuthenticationResource.resetPasswordno longer requirestoken; the docs mark it optional (it may be delivered out-of-band).emailandnew_passwordremain required.FieldResource.validateno longer throwsNullPointerExceptionon anullvalue; it forwards{"value": null}to the API.- Binary endpoints (document/page/thumbnail/signature download) now surface the server's error message and
body via
ApiException, instead of a generic "API request failed with status N". SignerResource.createduplicate-email recovery now also handles HTTP 400 for a duplicate (previously only 409). Removed a redundanttoLowerCaseinfindByEmail.
current_signer,page_count, andcreated_byfields onDocumentDetails(signer-facing and public document responses were silently dropping these documented fields).step,notified, andnotification_historyonSigner(the assignment-signer fields), plus a newAssignmentSignerNotificationmodel — exposes sequential-signing order, per-signer notified state, and delivery history.- Opt-in retry:
AssinafyClientOptions.setMaxRetries(int)retries HTTP 429/503 honoringRetry-After.ApiException.getRetryAfterSeconds()surfaces the server's hint regardless. - Per-method Javadoc (HTTP verb + path) on every public resource method (previously missing on the Signer, Field, Template, Webhook, and most Document methods).
docs/EXAMPLES.mdnow documents every method with full request and response JSON payloads.
- Java toolchain: compile target raised from Java 17 to Java 21; CI now runs
mvn verifyon a JDK 21 + 25 matrix (was a single JDK 25 job that never exercised the bytecode floor).DocumentPage/TemplatePagedimensions are nowint(the API returns integers). - GitHub Actions: actions pinned to commit SHAs, build matrix added, least-privilege permissions kept.
Added Dependabot, a tag-triggered
release.yml(GitHub Packages,packages: write), and a.gitlab-ci.ymlfor GitLab→GitHub mirror parity. - pom.xml:
distributionManagement(GitHub Packages), areleaseprofile that attaches-sources/-javadocjars,maven-enforcer-plugin(JDK 21+/Maven 3.8+), reproducible-build timestamp, SCM/developer metadata, and refreshed dependencies (Jackson 2.18.2, JUnit 5.11.4, AssertJ 3.27.3, Surefire 3.5.2). Added a Maven Wrapper (./mvnw). LiveSmokeTestnow defaults to the sandbox base URL (was production) and honorsASSINAFY_BASE_URL; expanded to cover the document and field lifecycles and the assignment expiration round-trip.
- 120 mock-backed unit tests + 16 live smoke tests (skipped without env vars). All green on JDK 21 and 25.
- Authentication resource for the documented login, social-login, API-key, and password flows:
client.auth.login,socialLogin,getApiKey,createApiKey,deleteApiKey,changePassword,requestPasswordReset, andresetPassword. - Workspace tag resource for
GET/POST/PUT/DELETE /accounts/{account_id}/tags. - Document tag helpers for listing, replacing, appending, and detaching tags.
- Signer-facing helpers for
GET /sign, filtered signer document listing, and signer-scoped document artifact download. - Tag parsing on document and template models,
default_document_tagsparsing on template details, document-tag support when creating documents from templates, and sequential-signingstepsupport.
- Maven now emits Java 17 bytecode with
--release 17for broader runtime support. - The client can now be constructed without credentials for public/authentication endpoints; authenticated endpoints still require API credentials at the API layer.
- 108 mock-backed unit tests + 12 live smoke tests (skipped without env vars). All green.
- Public document endpoints (no auth required, used by signer landing pages):
client.documents.getPublic(documentId)→GET /public/documents/{id}client.documents.sendToken(documentId, recipient, channel)→PUT /public/documents/{id}/send-token
- Signer-facing assignment operations (authorised via
signer-access-code):client.assignments.get(documentId, assignmentId, signerAccessCode)client.assignments.sign(documentId, assignmentId, signerAccessCode, entries)client.assignments.decline(documentId, assignmentId, signerAccessCode, reason)
- Signer Self-Service multi-document flows:
client.signerSelf.getCurrentDocument(signerId, signerAccessCode)client.signerSelf.listDocuments(signerId, signerAccessCode)client.signerSelf.signMultiple(signerAccessCode, documentIds)client.signerSelf.declineMultiple(signerAccessCode, documentIds, reason)
- Image content-type detection for
signerSelf.uploadSignature— auto-selectsimage/pngvsimage/jpegfrom the file header (was hard-coded to PNG). LiveSmokeTestJUnit class — runs against the real API whenASSINAFY_API_KEYandASSINAFY_ACCOUNT_IDenv vars are set; skipped otherwise.- New unit-test coverage for
DocumentResource,TemplateResource, andSignerSelfResource.
BaseResourcequery-parameter handling unified —httpGetBinary,httpPostBinary,httpPut, andhttpPostnow all support a typedMap<String, String>for query strings. SignerSelf and Assignment resources no longer hand-concatenate access codes into URLs.- Resource methods that the API documents as returning an empty payload (
data: []) —assignments.sign,assignments.decline,signerSelf.signMultiple,signerSelf.declineMultiple— now have avoidsignature and use newhttpPutVoid/httpPostVoidhelpers, fixing a latent JSON-deserialisation bug that surfaced when those endpoints were called. - Tightened input validation:
assignments.resetExpirationrejects blankexpires_at,signerSelf.uploadSignaturerequires non-empty image bytes and validates thetypeargument. - Resource methods now carry per-method Javadoc citing the exact HTTP verb and path.
- 91 unit tests + 10 live smoke tests (skipped without env vars). All green.
- Initial Java SDK release
AssinafyClientwith documentedDocumentResource,SignerResource,AssignmentResource,FieldResource,WebhookResource, andTemplateResourceuploadAndRequestSignatureshigh-level helper- Full test suite with
MockWebServer - Docker Compose support for running tests in a container
- Support for
X-Pagination-*response headers for paginated endpoints - Signer lookup and reuse by email