All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
No public API, request shape, or response handling changed.
- Docblocks on the remaining client, configuration, transport, and response accessors, so every public method in the SDK now carries documentation.
- The README's template section now states that a template created through the API receives only
an
Editorrole; signing roles are configured in the web app, andcreateFromTemplate()requires one. - The live integration suite exercises the full assignment lifecycle — estimate, create, resend,
reset expiration, WhatsApp notification log, and signing progress — on every run. It previously
required an opt-in switch and an operator-controlled mailbox. Recipients now use the reserved
example.comdomain, so no mail is delivered. Template detail and cost-estimate coverage likewise no longer depends on a template already existing in the account. /.claude/is gitignored. Locally approved-command history can embed live API keys.
2.1.3 - 2026-08-27
Packaging and tooling release. No public API, request shape, or response handling changed; the
transport behaves exactly as in 2.1.2. The only runtime difference is the User-Agent, which now
reads Assinafy-PHP-SDK/v2.1.3.
- Installation is now a plain
composer require assinafy/php-sdk. The package is published on Packagist, so the VCS/path-repository workaround the documentation carried is no longer needed. Projects that added arepositoriesentry to install an earlier release should remove it so Composer resolves from Packagist.
- PHPMD is no longer part of the developer gate. Its design rules were suppressed heavily
enough that the remaining signal did not justify the dependency:
phpmd/phpmdpulled thirteen transitive packages intorequire-dev(PDepend, six Symfony components,composer/pcre,composer/xdebug-handler), and the findings it still produced were parked in a baseline file. PHPStan, PHPCS, and the unit suite continue to run incomposer checkand in CI. Removed:phpmd.xml,phpmd.baseline.xml, thecomposer phpmdscript, themake phpmdtarget, and the CI step. Consumers are unaffected; the removal touches development tooling only.
2.1.2 - 2026-08-27
Documentation and internal-consistency release. No public API, request shape, or response handling changed; the transport behaves exactly as in 2.1.1.
- Request and response payloads on every public method. All 107 public methods across the
resource classes and
WebhookEventParsernow document the exact request body or query parameters they send and the response shape they return, with concrete examples, so an IDE shows the contract at the call site. Previously six methods carried payload examples. Notable details now written down:AuthResource::generateApiKey()is the only call that returns the API key in full;getApiKey()returns it masked to the last four characters.DocumentResource::verify()answers200withis_valid => falsefor an unknown hash rather than404, so callers must branch on the field and not the status.DocumentResource::activities()returns newest-first, with an integeridand a nullableorigin.FieldResource::validate()andvalidateMultiple()report a failed value as200withsuccess => false, not as an error status.FieldResource::types()repeatsemailin the live list; de-duplicate ontypebefore rendering a picker.WebhookResource::get()returnsnull, not[], when no subscription exists.
- README navigation and a "Sandbox and production differences" section.
accounts()->stats(),users()->stats(), andusers()->notificationPreferences()are served on production but not by the sandbox, which answers a framework404. The section records how to tell a missing route from a missing resource by the error body, and notes that an unauthenticated request makes the same distinction because routing resolves before authentication. - Missing changelog entries for 2.1.0 and 2.1.1, which shipped as tags without being recorded here.
- ISO 8601 expiry validation lives in one place.
AssinafyClient::validateExpiration()andAssignmentResource::assertDateTime()carried byte-identical copies of the same pattern, UTC-offset range check, andDateTimeImmutableround-trip. Both now callSupport\Iso8601::reasonInvalid(), which returns the reason so each caller keeps raising its own exception type —\InvalidArgumentExceptionandValidationExceptionrespectively — with the same messages as before.
- Changelog link references were split across two blocks, one stranded mid-file between the 1.4.0 and 1.3.0 sections. They are now consolidated at the end, with the 2.1.x releases added.
2.1.1 - 2026-08-21
Transport hardening. No resource method signatures changed.
- Response envelope validation. A
2xxHTTP response whose body carries a non-2xxstatusis now raised as anApiExceptionfor that status instead of being returned as success, and astatusthat is not an integer in100–599raises aNetworkException. Adatakey that is present but neithernullnor an array is likewise rejected rather than passed through. - Guards on injected Guzzle clients. A client supplied to
GuzzleHttpClientmay not define defaultAuthorizationorX-Api-Keyheaders, and itsbase_urimust match the configured API base URL. __debugInfo()onConfigurationandGuzzleHttpClientsovar_dump()and exception dumps report the authentication mode and header names rather than credential values.
- Request URIs must be relative to the configured base URL. Absolute URLs, leading slashes,
and
..traversal segments are rejected before the request is built. LogRedactor::summarizeRequestOptions()logs the structure of a request — query keys, header names, JSON keys, body size — instead of its values, so the default transport never writes a payload to the log.
2.1.0 - 2026-08-14
UserResource::notificationPreferences()andupdateNotificationPreferences()(GET/PUT /users/self/notification-preferences) covering the nine owner-facing document email preferences. The update is a merge: omitted keys keep their current value, and the full map is returned. Codes are validated locally againstUserResource::NOTIFICATION_PREFERENCE_CODES.
- Public and signer-facing routes no longer inherit workspace credentials. Requests to the
unauthenticated bootstrap, verification, and signer-session endpoints now omit
X-Api-KeyandAuthorizationeven when issued from a client configured with workspace credentials, so a workspace key is not presented to a route that does not expect one. Explicit per-request headers are left untouched.
2.0.0 - 2026-08-06
This release is a full audit against the current Assinafy API reference and running sandbox. The machine-readable OpenAPI document fetched on 2026-08-05 contains 89 operations on 68 paths. Coverage is 89/89 operations: the two browser-facing OAuth operations are URL builders, while every JSON, multipart, binary, and signer operation has a resource method. Five additional live template-management routes are retained under regression coverage even though they are absent from OpenAPI.
The specification and running API still disagree in a few places. The release documentation records those differences explicitly so a future spec-only audit does not remove working functionality or reintroduce an invalid request shape.
See UPGRADING.md for migration steps.
- Credentials no longer leak into logs.
GuzzleHttpClient::request()logged the entire request options array at debug level, writing plaintext passwords (login(),generateApiKey(),changePassword(),resetPassword()),Authorizationbearer tokens, and responseaccess_tokens to wherever the host application ships its logs. A newHttp\LogRedactormasks them; a regression test asserts a reallogin()call leaks nothing. Rotate any credential that may have been captured in existing logs. - Dependency advisories cleared. Guzzle is now a runtime dependency at
^7.15.2 || ^8.0.2rather than a development-only suggestion. The lowest/highest dependency CI jobs exercise both supported major lines, including their different exception hierarchies.ext-mbstringis also declared because validation uses multibyte-safe string operations. - Remote base URLs require HTTPS. Plain HTTP is accepted only for loopback development
hosts (
localhost/*.localhost,127.0.0.1, and::1); credentials, queries, and fragments are rejected in base URLs.
- Webhook signature verification.
WebhookVerifier::verify()did HMAC-SHA256 against a configuredwebhook_secret, but the API implements no signing:secretappears 0× in the spec, the subscription endpoint has no field to register one, and real deliveries carry no signature header. It could never returntrue— and the README told callers to use it as a rejection guard, which dropped every event. Removed rather than left as a trap.WebhookVerifier→WebhookEventParser;webhookVerifier()→webhookEvents(). Configuration::$webhookSecretandgetWebhookSecret(), along with theAssinafyClient::create()parameter — they existed only to feed the verifier. A legacywebhook_secretkey passed tofromArray()is accepted and ignored.- PHP 7.4 / 8.0 / 8.1 support. All three are EOL. Minimum is now
^8.2; CI covers 8.2–8.5.
AccountResource($client->accounts()) — theAccountstag was entirely unimplemented in 1.x:list,create,get,update,delete,theme,stats,downloadLogo,uploadLogo, anddeleteLogo.list()andcreate()are not account-scoped. A bootstrap client must pass the login Bearer token to those methods, or use a globally Bearer-authenticated client;forAuth()alone intentionally sends no credentials.DocumentResource::rename()—PATCH /documents/{id}. The SDK had noPATCHverb at all, so this was unreachable even through the raw client. Only legal while the document isuploaded/metadata_ready; the API normalises the name (diacritics stripped, max 255).DocumentResource::search()—GET /accounts/{id}/documents/search.AssignmentResource::list()—GET /assignments. Requires anaccountIdquery parameter that is not in the spec (camelCase;account-idandaccount_idare both rejected with400 "Um contexto de conta é necessário").WebhookEventParser::getEventPayload()andgetAccountId()— the envelope'spayloadkey was unreachable from any helper.HttpClientInterface::patch();post(),put(), andpatch()now accept nullable data sonullsends no request body, while an explicit array sends JSON.delete()accepts optional query parameters and an optional JSON body.GuzzleHttpClientaccepts an injectedClientInterface, making the transport unit-testable through a comprehensiveMockHandlersuite across both supported Guzzle majors..github/dependabot.yml, and acomposer validate --strictCI job.- Global Bearer authentication.
Configuration::forBearer()andAssinafyClient::forBearer()applyAuthorization: Bearer ...to every workspace resource. API-key lifecycle and password-change methods accept nullable per-call tokens sonulluses the configured API-key or global-Bearer authentication. - OAuth browser helpers.
AuthResource::socialLoginUrl()andsocialLoginCallbackUrl()represent the two non-JSON browser operations without asking the server-side JSON transport to follow redirects or parse HTML. SignerResource::normalizePhoneNumber()is now a public shared E.164 normalizer. Signer create/update require an explicit leading+and country code, accept common visual separators, and validate 8–15 digits instead of guessing a country for local input.
- Pagination is reachable at last. The API sends none in the body — there is no
metakey on any endpoint and never was — butAbstractResourceclaimed the envelope was{status, message, data, meta?}and justifiedlist()returning the raw envelope "to keep access tometa". Real pagination arrives inX-Pagination-*response headers, whichResponsecaptured and the resource layer then discarded.list()now returns apaginationkey built from those headers. Additive:datais untouched. estimateCost()accepts signers without IDs. The docs state IDs "are not required — only the verification/notification method affects cost", and the API agrees (verified: HTTP 200 with a full breakdown), butnormalizeSigners()threwValidationExceptionbefore any request was made, so the documented "price it before the signers exist" flow was impossible.- Assignment notification channels are independent of verification. OpenAPI permits an
empty array, Email, WhatsApp, or both regardless of
verification_method; the SDK no longer rejects those valid combinations or truncates them to one channel. WebhookEventParser::getEventData()drops its deaddata/typefallbacks — confirmed absent from real deliveries. Behaviour is unchanged (it always fell through toobject). The 1.x unit test asserted on a fabricateddatakey, so the bug tested green.- PHPStan crashed at PHP's default 128M limit; CI and the Makefile now pass
--memory-limit=512M.
- GitHub Actions pinned to immutable commit SHAs with
persist-credentials: false; Dependabot keeps them current. - Docblocks now carry full request and response payloads. Several were flatly wrong —
SignerDocumentResource::list()advertisedstatus/methodfilters the endpoint doesn't declare,TemplateResource::list()advertisedsort(declared on exactly one operation in the whole spec), andFieldResource::update()listed fields thePUTdoesn't accept. - README documents where the spec and the live API disagree, so the next audit doesn't have to rediscover it.
- Signer authentication uses the
signer-access-codequery parameter. The SDK follows the current OpenAPI security scheme consistently.acceptTerms()sends that query parameter with no request body;verifyCode()sends only the verification code in JSON. - Signer-access-code acquisition is inbox-driven. Assignment
signing_urlscontainsigner_idandurl, but do not expose the one-time access code. Deriving a code from a URL path segment produced401in the sandbox.sendToken()delivers the code to the assigned signer's inbox; authenticated signer-read integration checks are therefore separately opt-in throughASSINAFY_SIGNER_IDandASSINAFY_SIGNER_ACCESS_CODE, and are not reported as live successes when those credentials are absent. - Public send-token keeps the working runtime body. OpenAPI currently shows
{email}, while the sandbox requires{recipient, channel}and rejects a recipient who is not already a signer assigned to the target document. The SDK sends the runtime body shape; live tests use an assigned signer. - Authenticated-user responses are normalized. OpenAPI declares
GET /users/selfasdata: AuthUser, while the sandbox returnsdata: {user: AuthUser, accounts: AuthAccount[]}.UserResource::get()returnsdata.userfor the sandbox shape and still accepts the published shape, keeping itsAuthUserreturn contract stable. - Published statistics methods are retained despite a sandbox deployment gap. Both
GET /accounts/{accountId}/statsandGET /users/self/statsreturned an application-level404route-not-deployed response in the sandbox on 2026-08-05. Their SDK methods remain because both operations are published and count toward 89/89 coverage; they are not presented as currently runnable sandbox functionality. - Document-tag operations are published; five template-management operations are not.
Document
listTags(),replaceTags(),appendTags(), anddetachTag()map directly to current OpenAPI operations. The body description calls its strings tag IDs, but the sandbox and SDK use tag names and auto-create missing names. Template create/get/update/delete and page download remain available because the live API supports them and regression tests cover them. - OAuth start and callback are browser operations, not missing SDK functionality. The SDK returns their absolute URLs. The current start route produces the documented redirect; the callback returns browser content rather than a JSON resource.
1.4.1 - 2026-06-05
Audit pass against https://api.assinafy.com.br/v1/docs, verified end-to-end against the
live sandbox (https://sandbox.assinafy.com.br/v1). The docs describe the Template service
as "create, list, download and delete templates", but only the read endpoints were exposed.
Probing the live API confirmed the four management routes exist (a 406/app-level 404
response distinguishes a real route from a framework Página não encontrada routing miss),
so they are now covered. No functionality was removed — DELETE /webhooks/subscriptions
remains absent because the live API returns a routing 404 for it (it does not exist despite
appearing in the docs).
TemplateResourcemanagement endpoints — the SDK now covers the full documented template surface:create(string $filePath)—POST /accounts/{id}/templates(multipart PDF upload; the template renders asynchronously, pollget()untilstatusisReady).update(string $templateId, array $data)—PUT /accounts/{id}/templates/{id}(editablename,document_name,message).delete(string $templateId)—DELETE /accounts/{id}/templates/{id}.downloadPage(string $templateId, string $pageId)—GET /accounts/{id}/templates/{id}/pages/{page_id}/download(raw JPEG body).
DocumentResource::assertUploadable()— the document-upload validation (PDF + 25 MB limit) is now a shared static helper reused byTemplateResource::create()(DRY).- Live
testTemplateManagementLifecycle— create → poll Ready → update → page download → delete → confirm-gone, exercised against the sandbox.
TemplateResourcedocblocks no longer claim template creation/editing is web-app only — that statement contradicted both the docs and the live API.
1.4.0 - 2026-05-27
Complete coverage pass against https://api.assinafy.com.br/v1/docs. A full re-read of
the live documentation surfaced several whole resource families the SDK had never exposed;
each new endpoint below was verified end-to-end against the production API before release.
TagResource($client->tags()) — workspace tag management:GET/POST /accounts/{id}/tags,PUT/DELETE /accounts/{id}/tags/{tag_id}(withforcedetach-and-delete).FieldResource($client->fields()) — field-definition management and validation:POST/GET /accounts/{id}/fields,GET/PUT/DELETE /accounts/{id}/fields/{field_id},POST …/fields/{id}/validate,POST …/fields/validate-multiple(both usable as an authenticated user or, with asigner-access-code, as a signer), and the globalGET /field-typescatalog.SignerDocumentResource($client->signerDocuments()) — signer-facing document endpoints authenticated bysigner-access-code:GET /signers/{id}/document,GET /signers/{id}/documents,PUT /signers/documents/sign-multiple,PUT /signers/documents/decline-multiple, andGET /signers/{id}/documents/{id}/download/{artifact_name}.DocumentResourcedocument tags —listTags(),appendTags(),replaceTags(),detachTag()coveringGET/POST/PUT /accounts/{id}/documents/{id}/tagsandDELETE …/tags/{tag_id}.AssignmentResource::whatsappNotifications()—GET /documents/{id}/assignments/{id}/whatsapp-notifications.AssignmentResourcesequential signing — signer entries now pass through the documentedstepfield; addedNOTIFICATION_EMAIL/NOTIFICATION_WHATSAPPconstants.SignerSessionResourcesigner-facing signing actions —currentDocument()(GET /sign),sign()(POST /documents/{id}/assignments/{id}), anddecline()(PUT /documents/{id}/assignments/{id}/reject).WebhookResourcedispatch + discovery endpoints —eventTypes()(GET /webhooks/event-types),dispatches()(GET /accounts/{id}/webhooks, paginated withevent/delivered/from/tofilters), andretryDispatch()(POST /accounts/{id}/webhooks/{dispatch_id}/retry). Added constants for all 15 subscribable event types.- Query-string parameter on
HttpClientInterface::delete()— supports the tag?force=trueflag. Backward-compatible: the new$queryarg is the third positional and defaults to[]. - 4 new live integration tests covering the tag, field, document-tag, and webhook discovery endpoints (all credit-free).
WebhookResource::deactivate()now calls the dedicatedPUT /accounts/{id}/webhooks/inactivateendpoint (verified live) instead of re-PUTting the subscription withis_active: false. The server preserves the URL / email / events, soactivate()still restores them.DocumentResource::assertArtifact()promoted topublic staticsoSignerDocumentResource::download()validates artifact names through the same list (DRY).
1.3.0 - 2026-05-12
Second pass against https://api.assinafy.com.br/v1/docs plus a full live verification
against the sandbox. The live run caught two issues the unit suite had missed (see
Fixed below — is_active and the non-existent DELETE for webhooks).
Configuration::forPublic()andAssinafyClient::forAuth()— build a client for the unauthenticated surface of the API without having to fabricate an API key / account ID. Use it to callauth()->login(),requestPasswordReset(),resetPassword(),socialLogin()and the public document endpoints (verify,publicInfo,sendToken). Account-scoped resources called on a public client now raise a clearRuntimeExceptioninstead of silently sending a placeholder account ID and getting a 401 from the API.DocumentResource::SEND_TOKEN_CHANNEL_EMAILconstant + allow-list validation onsendToken()— typos like'whatsapp'now raiseValidationExceptionup front instead of being forwarded blindly.WebhookResource::deactivate()/activate()— soft toggle the subscription viaPUT … {is_active: false|true}. Replaces the brokendelete()(see Removed).WebhookResource::register($url, $email, $events, $isActive = true)— new optional fourth arg so callers can create an initially-inactive subscription.- Query-string parameter on
HttpClientInterface::post()/put()— lets resources send query params alongside a JSON body without manually concatenating into the URI. Backward-compatible: existing callers continue to work, the new$queryarg is the fourth positional and defaults to[]. ASSINAFY_BASE_URLenv-var support intests/Integration/LiveApiTest.php— set it toConfiguration::SANDBOX_BASE_URLto run the integration suite against sandbox.- 6 new live integration tests covering thumbnail / page downloads,
verifywith a bogus hash, the assignment lifecycle (estimateCost→create→estimateResendCost→resend→resetExpiration), and the webhook activate/deactivate round-trip. Templates tests skip cleanly when the sandbox account has no templates.
SignerSessionResource::confirmData()now passessigner-access-codethrough the HTTP client's$querychannel instead of building the URI by hand withrawurlencode(). Behavior is identical (still goes on the query string) but it's consistent with the rest of the signer-session methods and robust against future endpoint params.SignerResource::normalizePhone()— removed a dead ternary (($hasPlus ? '+' : '+')) that always evaluated to'+'. The normalized output is unchanged.AbstractResource::extractData()docblock clarifies the list-vs-single envelope convention; everylist()method now declares the{data, meta}shape it returns.TemplateResource::get()docblock explicitly notes thatGET /accounts/{id}/templates/{id}is part of the v1 API even though it's not currently rendered in the public docs UI.WebhookResourceclass docblock points to the live integration suite that exercises the (undocumented) webhook subscription endpoints on every release.
WebhookResource::delete()— the underlyingDELETE /accounts/{id}/webhooks/subscriptionsroute does not exist on the v1 API (verified live: returns 404 Página não encontrada). The method has never worked. Usedeactivate()instead — same outcome, supported by the API.
WebhookResource::register()is_activefield — verified live against sandbox: the API rejects the request withO atributo "is_active" é obrigatório.ifis_activeis omitted. The field stays in the payload and the new fourth parameter$isActivelets callers opt out of immediate activation.- Auth bootstrap chicken-and-egg —
Configuration::__construct()no longer forces callers to invent dummy credentials just to reachauth()->login(). UseAssinafyClient::forAuth().
1.2.0 - 2026-05-11
Full audit against https://api.assinafy.com.br/v1/docs verified against the live API.
AuthResource($client->auth()) covering every authentication endpoint:POST /login,POST /authentication/social-login,POST/GET/DELETE /users/api-keys,PUT /authentication/change-password,PUT /authentication/request-password-reset,PUT /authentication/reset-password.SignerSessionResource($client->signerSession()) covering signer-facing endpoints authenticated with asigner-access-code:GET /signers/self,PUT /signers/accept-terms,POST /verify,PUT /documents/{id}/signers/confirm-data,POST /signature,GET /signature/{type}.DocumentResource:delete($documentId)—DELETE /documents/{id}download($documentId, $artifact)— now correctly hitsGET /documents/{id}/download/{artifact_name}and validates the artifact namedownloadThumbnail($documentId)—GET /documents/{id}/thumbnaildownloadPage($documentId, $pageId)—GET /documents/{id}/pages/{page_id}/downloadactivities($documentId)—GET /documents/{id}/activitiesstatuses()—GET /documents/statusespublicInfo($documentId)—GET /public/documents/{id}sendToken($documentId, $recipient, $channel)—PUT /public/documents/{id}/send-token- Status / artifact-name constants for type safety (
STATUS_*,ARTIFACT_*)
AssignmentResource:METHOD_*andVERIFICATION_*constantscreate()now accepts either string signer IDs or full signer objects and serialises them to the documentedsigners: [{ id, verification_method?, notification_methods? }]shape
HttpClientInterface::postRaw()for binary uploads (signature image bytes).- Full PHPUnit test suite (
tests/Unit,tests/Integration) — 66 unit tests + 6 live tests against the production API.
- Pagination param fix: every
list()method now sendsper-page(with hyphen) as the API expects. Previouslyper_pagewas sent and silently ignored. - Upload size limit lowered from a fictional 50 MB to the documented 25 MB.
DocumentResource::waitUntilReady()now polls for the real status codes (metadata_ready,pending_signature,certificated) and fails fast onfailed,expired,rejected_by_signer,rejected_by_user.DocumentResource::isFullySigned()now checksstatus === 'certificated'(was a fictional'signed').DocumentResource::getSigningProgress()now reads progress fromdocument.assignment.SignerResource::create()signature simplified to(fullName, email?, whatsappPhoneNumber?)— removed unsupportedcpfandmetadatafields.SignerResourcephone numbers are now normalised to E.164 (the+prefix is preserved).GuzzleHttpClientensures thebase_uriends with/so relative request paths resolve correctly per RFC 3986 (previously every request lost the/v1prefix and 404'd).GuzzleHttpClient::uploadFile()no longer overrides the multipart Content-Type header (which stripped the boundary).Configuration::getHeaders()no longer pinsContent-Type: application/jsonglobally — it's set per-request by JSON helpers, leaving uploads and binary calls free to set their own.AssinafyClient::uploadAndRequestSignatures()signature changed to(filePath, signers, ?message, ?expiresAt, waitForReady). It now creates / reuses signers by email and uses the documented assignment payload.Configuration::SDK_VERSION,DEFAULT_BASE_URL,SANDBOX_BASE_URLconstants.
AssignmentResource::cancel()— the underlying endpointPOST /accounts/{id}/signature-requests/{id}/canceldoes not exist on the API (verified with a live 404).AssignmentResource::resendNotification()— the underlying endpointPOST /accounts/{id}/signature-requests/resenddoes not exist (verified with a live 404). Useresend()instead, which hits the documented path.AbstractResource::normalizeId()— alias hack addingdocument_idkeys to API responses. Read the realidfield instead.
- Upload no longer sends bogus
name/metadatamultipart fields (onlyfile). - Every
list()URL now resolves correctly against the v1 base URL.
1.1.1 - 2026-05-06
SignerResource::create— changed payload key fromphonetowhatsapp_phone_numberto match the documented Assinafy API field name. The method signature (?string $phone) is unchanged for backward compatibility; callers pass a phone number as before and the SDK now sends it under the correct field.SignerResource::normalizeSignerResponse— the normalised response now maps the API'swhatsapp_phone_numberfield instead of the legacyphonekey.
1.1.0 - 2026-05-06
Full audit against the Assinafy REST API v1 docs (https://api.assinafy.com.br/v1/docs).
All new endpoints from the official API catalog added without breaking existing method signatures.
TemplateResource(new class) with:list(int $page, int $perPage, array $filters)—GET /accounts/{accountId}/templatesget(string $templateId)—GET /accounts/{accountId}/templates/{templateId}
AssinafyClient::templates()accessor that lazily instantiatesTemplateResource.DocumentResource:createFromTemplate(string $templateId, array $signers, array $options)—POST /accounts/{accountId}/templates/{templateId}/documentsestimateCostFromTemplate(string $templateId, array $signers)—POST /accounts/{accountId}/templates/{templateId}/documents/estimate-costverify(string $hash)—GET /documents/{hash}/verify
AssignmentResource:estimateCost(string $documentId, array $signers, string $method, ?array $entries)—POST /documents/{documentId}/assignments/estimate-costresend(string $documentId, string $assignmentId, string $signerId)—PUT /documents/{documentId}/assignments/{assignmentId}/signers/{signerId}/resendestimateResendCost(string $documentId, string $assignmentId, string $signerId)—POST /documents/{documentId}/assignments/{assignmentId}/signers/{signerId}/estimate-resend-costresetExpiration(string $documentId, string $assignmentId, string $expiresAt)—PUT /documents/{documentId}/assignments/{assignmentId}/reset-expiration
SignerResource:update(string $signerId, array $data)—PUT /accounts/{accountId}/signers/{signerId}delete(string $signerId)—DELETE /accounts/{accountId}/signers/{signerId}
1.0.0 - 2024-12-22
- Initial release of framework-agnostic PHP SDK
- PSR-4 autoloading
- PSR-3 logger interface support
- SDK-specific injectable HTTP client interface (it is not PSR-18)
- Document management (upload, download, status tracking)
- Signer management (create, list, search)
- Assignment management (create, cancel, resend)
- Webhook support (register, verify signatures)
- Comprehensive exception hierarchy
- Docker development environment
- Complete documentation and examples
- PHP 7.4 compatibility (replaced
str_contains()andstr_ends_with())
- HMAC-SHA256 webhook signature verification
- Timing-safe signature comparison
- PHP 7.4: Full support with positional arguments
- PHP 8.0+: Full support with named arguments
- PHP 8.1+: Recommended for best developer experience