- Live sandbox tests run locally; GitHub Actions runs the mock-backed verification and release builds.
- Mutating requests are sent once, including uploads, signatures, updates, and deletes. Redirects and automatic HTTP 408/503 replay no longer repeat a request that can produce side effects.
- OAuth authorization rejects malformed HTTPS callbacks, callbacks containing user information or fragments, and invalid individual scope tokens before sending a request.
OAuthTokens.getIssuedTokenType()exposes the optionalissued_token_typeresponse field.
- Resource overloads include their HTTP method and path in Javadoc.
- OAuth examples include refresh, revocation, userinfo, and protected-resource request/response payloads.
- OAuth token and revoke requests are sent as
application/x-www-form-urlencoded, as the OAuth guide specifies, instead of JSON. client.oauthsends each token and revoke request once. OkHttp no longer re-sends one on its own — after a connection failure, a408, a503withRetry-After: 0, or a redirect, whichclient.oauthno longer follows — because a re-sent refresh replays a refresh token the first attempt may already have retired, which ends the user's connection. A dropped call now raisesNetworkException, and such a responseApiExceptionwith its status.refreshToken(...)throwsValidationExceptionwhen a successful response carries no new refresh token — missing, blank, or the one sent — instead of returning a token set with nothing safe to store. Migration: handle it likeinvalid_grantand ask the user to reconnect.authorizationUrl(...)rejects asetResource(...)value other than the client's API origin, the valueexchangeAuthorizationCode(...)sends; a mismatch always failed at the token endpoint withinvalid_target.- Docs: a refresh token is valid for 30 days and every refresh returns a new one with a fresh 30 days; a connection only expires after 30 days without a refresh.
- Docs: after a refresh, the examples call the API through a client built with the renewed access token and revoke the most recently saved refresh token. Never re-send a refresh token after a failure that may have reached the server: re-read storage, continue only if it holds a newer token, and otherwise ask the user to reconnect. Only a DNS failure, a refused connection, or a failed TLS handshake is safe to retry.
DocumentVerification.getAgreementCode()exposesagreement_code, the agreement code printed on the document certificate.
- The SDK's own HTTPS client now requires TLS 1.2 or newer; TLS 1.0 and 1.1 are refused. Clients supplied by the caller are unchanged.
- OAuth authorization requests can include
webhooks:writeto configure and deactivate a workspace webhook subscription.
- Require at least one signer on every assignment body and always send the
signerskey. The published contract markssignersas required only forvirtual, but the API prices per signer in both modes and answers a signer-less body with400 "Pelo menos um signatários precisa ser informado."buildPayloadcarved out collect estimates and then omitted the key entirely, so such an estimate could never be priced. - Drop the
collectEstimateWithoutSignerslive test. It caught that 400 and treated it as "not deployed to this environment", which is what kept the contract misreading alive.
client.oauthcovers the OAuth 2.1 authorization-code flow the API now publishes, for applications acting in other people's workspaces with those users' permission:exchangeAuthorizationCode(...),refreshToken(...),revoke(...),userInfo(), andprotectedResourceMetadata(). An access token is used like any other bearer credential, throughAssinafyClientOptions.setToken(...).oauth.authorizationUrl(OAuthAuthorizationRequest)builds the browser-facing authorization URL. It fixesresponse_type=codeandcode_challenge_method=S256, derives the PKCE challenge from the verifier so the two cannot disagree, and defaults the RFC 8707 resource indicator to the origin of the client's base URL.OAuthResource.generateCodeVerifier()andgenerateState()produce the per-attempt secrets fromSecureRandom.OAuthTokens,OAuthUserInfo, andOAuthProtectedResourcemodel the three flat responses those endpoints return. RFC 6749, OpenID Connect, and RFC 8615 each require a bare object, so they are not wrapped in the platform's{status,message,data}envelope and the SDK reads them directly.ApiException.getOAuthError()carries the RFC 6749 error code of a failure —invalid_grant,invalid_client,invalid_target,unsupported_grant_type,insufficient_scope— read from a flat{error, error_description}body or from aWWW-Authenticatechallenge. On aninsufficient_scoperejection,getRequiredScope()names the scope to reconnect with, which is the difference between a permission the user has not granted and a workspace the token cannot reach.
- An error body that carries
error_descriptioninstead ofmessagesurfaces that text as the exception message, rather than the genericAPI request failed with status Nthat discarded the server's explanation. - Redirect URIs and PKCE code verifiers are validated before a request is sent: a non-HTTPS or fragment-bearing
redirect URI, and a verifier outside the RFC 7636 grammar of 43 to 128 unreserved characters, raise
ValidationExceptionlocally rather than costing a round trip and aninvalid_grant.
README.mdis the complete integration guide in Portuguese, following one document from installation to a signed, downloaded PDF, andREADME.en.mdis the same guide in English. Both gain an OAuth stage, the verification and notification method tables with their coupling rules and per-signer costs, the ICP-Brasil A1/A3 certificate requirements, the document artifact table, and the environment table.docs/API_REFERENCE.mdcovers the four new operations, the OAuth request and response payloads, the scope catalog, and the flow's client-side rules;docs/EXAMPLES.mdadds a six-step runnable OAuth walkthrough.
- 237 mock-backed unit tests + 36 live sandbox tests, green on JDK 25. Sixteen new tests pin the OAuth request bodies, the flat response parsing, the S256 authorization URL, the two error-code sources, and the host-root metadata path. The two new live tests skip while OAuth remains undeployed to the sandbox.
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.mddocuments setter-style client configuration and installation guidance for Java integrations.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. Creating a signer whose email already exists raisesApiException. Callsigners.findOrCreate(payload[, accountId])to reuse a matching signer;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 body placement fails authentication.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 both validation methods send an explicitnullvalue.- 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). Permanent 400/401 failures carry no retry hint, even whenX-Rate-Limit-Resetis present.
- 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 and 409 for a duplicate. 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 public resource 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