assinafy 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.en.md +23 -1
- data/README.md +33 -0
- data/docs/API_REFERENCE.md +146 -10
- data/lib/assinafy/client.rb +4 -0
- data/lib/assinafy/resources/account_resource.rb +1 -0
- data/lib/assinafy/resources/assignment_resource.rb +9 -10
- data/lib/assinafy/resources/contract_resource.rb +450 -0
- data/lib/assinafy/resources/document_resource.rb +3 -0
- data/lib/assinafy/resources/user_resource.rb +18 -6
- data/lib/assinafy/resources/webhook_resource.rb +35 -5
- data/lib/assinafy/version.rb +1 -1
- data/lib/assinafy.rb +1 -0
- data/sig/assinafy.rbs +18 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1d6db456898234647465325a55c4580054a8ab93f39aff3d6ca067ffe6be9e49
|
|
4
|
+
data.tar.gz: 55577984ae91aec1ad66715a9b34a8ae10e991f015d6f4e3fe01f5bebf2b9503
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 93a92268707f3afa45e7f16c837ab3ded90b7ab8859060ff48a0d7eeb81a244dde3b53ada834852de4f83df7e72f738bbffc1468e8ae7de24dfffba56709edb6
|
|
7
|
+
data.tar.gz: 384b02e9dd54fb89ea78dc61b6f7a507e8e317431d30996b0dcfd16f4a6d3251a03821d0f6d59d28541b2425a24cbd98062c68b2945f0ef2fb252af8569a7ad4
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the `assinafy` Ruby gem are documented here.
|
|
4
4
|
|
|
5
|
+
## [2.1.0] - 2026-10-28
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `ContractResource` and `client.contracts` cover listing, reading, updating, relating, renewing and terminating
|
|
10
|
+
the contract terms tracked on documents, including nullable fields and optimistic `updated_at` preconditions.
|
|
11
|
+
- User notification preferences support `ContractRenewalNotice`, `ContractExpiring`, `ContractExpired` and
|
|
12
|
+
`ContractRenewed`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- New or changed webhook endpoint URLs must be absolute HTTPS URLs; invalid URLs raise `ValidationError` before a
|
|
17
|
+
request.
|
|
18
|
+
- Contract, document, assignment, webhook and reminder-cost response fields reflect the current API contract.
|
|
19
|
+
|
|
5
20
|
## [2.0.1] - 2026-10-19
|
|
6
21
|
|
|
7
22
|
### Fixed
|
data/README.en.md
CHANGED
|
@@ -691,7 +691,7 @@ See [Errors](#errors) for the full hierarchy.
|
|
|
691
691
|
|
|
692
692
|
## Resource reference
|
|
693
693
|
|
|
694
|
-
`Assinafy::Client` exposes
|
|
694
|
+
`Assinafy::Client` exposes fourteen accessors — thirteen API resources plus the local `webhook_verifier` helper:
|
|
695
695
|
|
|
696
696
|
| Accessor | What it covers |
|
|
697
697
|
| --------------------------- | ------------------------------------------------------------------ |
|
|
@@ -700,6 +700,7 @@ See [Errors](#errors) for the full hierarchy.
|
|
|
700
700
|
| `client.accounts` | Account CRUD, theme, KPI stats, brand logo |
|
|
701
701
|
| `client.users` | User profile, notification preferences, cross-account KPIs |
|
|
702
702
|
| `client.documents` | Upload, list, search, rename, download, delete, verify, tags |
|
|
703
|
+
| `client.contracts` | Contract terms, amendments, renewals, terminations |
|
|
703
704
|
| `client.signers` | Workspace signer CRUD + signer self-service endpoints |
|
|
704
705
|
| `client.signer_documents` | Signer-authenticated multi-document operations + search |
|
|
705
706
|
| `client.assignments` | List/create/sign/decline/resend/estimate assignments |
|
|
@@ -781,6 +782,27 @@ client.documents.signing_progress('document-id') # => { signed: 1, total: 2, p
|
|
|
781
782
|
An Array `tags` filter is sent as comma-separated tag IDs. The tag arrays of `replace_tags`/`append_tags` take tag
|
|
782
783
|
IDs.
|
|
783
784
|
|
|
785
|
+
### Contracts
|
|
786
|
+
|
|
787
|
+
```ruby
|
|
788
|
+
contract = client.contracts.update(
|
|
789
|
+
'document-id',
|
|
790
|
+
status: 'active', counterparty_name: 'Example Ltda', value: 120_000, currency: 'BRL',
|
|
791
|
+
start_date: '2026-11-01', end_date: '2027-10-31', auto_renew: true,
|
|
792
|
+
renewal_term_months: 12, notice_period_days: 30
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
client.contracts.get('document-id')
|
|
796
|
+
client.contracts.list(status: ['active'], ending_within_days: 30)
|
|
797
|
+
client.contracts.related('document-id')
|
|
798
|
+
client.contracts.renew('document-id', term: contract['term'], updated_at: contract['updated_at'])
|
|
799
|
+
client.contracts.terminate('document-id', reason: 'Ended by the parties')
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
`update` is partial and preserves explicit `nil` values as JSON `null`. Link an amendment document with
|
|
803
|
+
`parent_document_id` and `relation_type`. Reads require OAuth scope `documents:read`; writes require
|
|
804
|
+
`documents:write` and a plan with contract tracking. `get` and `related` return `nil` when no tracked terms exist.
|
|
805
|
+
|
|
784
806
|
### Signers (workspace CRUD)
|
|
785
807
|
|
|
786
808
|
```ruby
|
data/README.md
CHANGED
|
@@ -966,6 +966,38 @@ verificacao['agreement_code'] # código impresso no certificado do documento
|
|
|
966
966
|
Ela devolve o resultado da Assinafy; não valida independentemente a assinatura do PDF nem a
|
|
967
967
|
cadeia de certificação.
|
|
968
968
|
|
|
969
|
+
### Acompanhar o contrato
|
|
970
|
+
|
|
971
|
+
Quando o plano da conta inclui acompanhamento de contratos, registre os termos no documento e
|
|
972
|
+
use o `updated_at` devolvido pela API como precondição para evitar sobrescrever uma alteração
|
|
973
|
+
concorrente:
|
|
974
|
+
|
|
975
|
+
```ruby
|
|
976
|
+
contrato = client.contracts.update(
|
|
977
|
+
documento['id'],
|
|
978
|
+
status: 'active',
|
|
979
|
+
counterparty_name: 'Empresa Exemplo Ltda',
|
|
980
|
+
value: 120_000,
|
|
981
|
+
currency: 'BRL',
|
|
982
|
+
start_date: '2026-11-01',
|
|
983
|
+
end_date: '2027-10-31',
|
|
984
|
+
auto_renew: true,
|
|
985
|
+
renewal_term_months: 12,
|
|
986
|
+
notice_period_days: 30
|
|
987
|
+
)
|
|
988
|
+
|
|
989
|
+
client.contracts.get(documento['id'])
|
|
990
|
+
client.contracts.list(status: ['active'], ending_within_days: 30)
|
|
991
|
+
client.contracts.related(documento['id'])
|
|
992
|
+
client.contracts.renew(documento['id'], term: contrato['term'], updated_at: contrato['updated_at'])
|
|
993
|
+
client.contracts.terminate(documento['id'], reason: 'Encerrado pelas partes')
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
`update` é parcial e preserva `nil` como JSON `null` para limpar campos. Um documento de aditivo
|
|
997
|
+
pode ser ligado com `parent_document_id` e `relation_type`. As leituras usam o escopo OAuth
|
|
998
|
+
`documents:read`; alterações usam `documents:write` e exigem que o plano tenha acompanhamento de
|
|
999
|
+
contratos. `get` e `related` devolvem `nil` quando o documento não tem termos acompanhados.
|
|
1000
|
+
|
|
969
1001
|
---
|
|
970
1002
|
|
|
971
1003
|
## 12. Organizar e limpar
|
|
@@ -1072,6 +1104,7 @@ end
|
|
|
1072
1104
|
| `client.accounts` | workspaces, tema, KPIs, logo |
|
|
1073
1105
|
| `client.users` | perfil próprio, KPIs, preferências de notificação |
|
|
1074
1106
|
| `client.documents` | upload, listagem, download, ciclo de vida, tags, verificação |
|
|
1107
|
+
| `client.contracts` | termos, aditivos, renovações e encerramentos de contratos |
|
|
1075
1108
|
| `client.signers` | CRUD de signatários e todo o autoatendimento do signatário |
|
|
1076
1109
|
| `client.signer_documents` | documentos do signatário, assinar/recusar em lote |
|
|
1077
1110
|
| `client.assignments` | pedidos de assinatura, custos, reenvios, assinatura, recusa |
|
data/docs/API_REFERENCE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Assinafy Ruby SDK API Reference
|
|
2
2
|
|
|
3
|
-
> Contract source: Assinafy API v1 OpenAPI 3.1.0, retrieved 2026-10-
|
|
4
|
-
> `https://api.assinafy.com.br/v1/docs/openapi.json` (
|
|
3
|
+
> Contract source: Assinafy API v1 OpenAPI 3.1.0, retrieved 2026-10-28 from
|
|
4
|
+
> `https://api.assinafy.com.br/v1/docs/openapi.json` (90 paths, 118 operations, 49 schemas).
|
|
5
5
|
> `scripts/check_api_contract.rb` validates contract compatibility weekly.
|
|
6
6
|
|
|
7
7
|
This is the SDK-facing contract reference. Paths below are wire paths; Ruby methods return the unwrapped
|
|
@@ -50,6 +50,12 @@ Required parameters and object properties are marked with `*`.
|
|
|
50
50
|
| GET | `/v1/accounts/{accountId}` | `AccountResource#get` | Bearer token or `X-Api-Key` | path `accountId*`: string | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`Account`](#account) } |
|
|
51
51
|
| PUT | `/v1/accounts/{accountId}` | `AccountResource#update` | Bearer token or `X-Api-Key` | path `accountId*`: string | required; `application/json` object { `name`: string; `notification_sender_type`: string } | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`Account`](#account) } |
|
|
52
52
|
| DELETE | `/v1/accounts/{accountId}` | `AccountResource#delete` | Bearer token or `X-Api-Key` | path `accountId*`: string | optional; `application/json` object { `force`: boolean } | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: Array<any> } |
|
|
53
|
+
| GET | `/v1/accounts/{accountId}/contracts` | `ContractResource#list` | Bearer token or `X-Api-Key`; OAuth `documents:read` | path `accountId*`: string<br>query `status`: comma-separated array of `draft`, `active`, `expired`, `terminated`<br>query `counterparty`, `search`, `end_date_from`, `end_date_to`, `ending_within_days`, `external_system`, `external_id`, `sort`, `expand`, `page`, `per-page` | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: Array<[`DocumentContract`](#documentcontract)> }; pagination headers become `{ data:, meta: }` |
|
|
54
|
+
| GET | `/v1/accounts/{accountId}/documents/{documentId}/contract` | `ContractResource#get` | Bearer token or `X-Api-Key`; OAuth `documents:read` | path `accountId*`: string<br>path `documentId*`: string | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`DocumentContract`](#documentcontract) or [`DocumentContractAmendment`](#documentcontractamendment) }; SDK returns `nil` on `404` |
|
|
55
|
+
| PUT | `/v1/accounts/{accountId}/documents/{documentId}/contract` | `ContractResource#update` | Bearer token or `X-Api-Key`; OAuth `documents:write` | path `accountId*`: string<br>path `documentId*`: string | required non-empty [`DocumentContractInput`](#documentcontractinput); explicit `nil` is sent as JSON null | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`DocumentContract`](#documentcontract) or [`DocumentContractAmendment`](#documentcontractamendment) }; `403` without contract tracking, `409` on stale `updated_at` |
|
|
56
|
+
| GET | `/v1/accounts/{accountId}/documents/{documentId}/contract/related` | `ContractResource#related` | Bearer token or `X-Api-Key`; OAuth `documents:read` | path `accountId*`: string<br>path `documentId*`: string | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: root [`DocumentContract`](#documentcontract) with `amendments` }; SDK returns `nil` on `404` |
|
|
57
|
+
| POST | `/v1/accounts/{accountId}/documents/{documentId}/contract/renew` | `ContractResource#renew` | Bearer token or `X-Api-Key`; OAuth `documents:write` | path `accountId*`: string<br>path `documentId*`: string | optional; `application/json` object { `end_date`: string (date, nullable); `value`: number (nullable); `term`: integer; `updated_at`: string (date-time) }; a valid request requires `term` or `end_date` | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`DocumentContract`](#documentcontract) }; `403` without contract tracking, `409` on stale `updated_at` |
|
|
58
|
+
| POST | `/v1/accounts/{accountId}/documents/{documentId}/contract/terminate` | `ContractResource#terminate` | Bearer token or `X-Api-Key`; OAuth `documents:write` | path `accountId*`: string<br>path `documentId*`: string | optional; `application/json` object { `reason`: string (nullable, max 500); `updated_at`: string (date-time) } | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`DocumentContract`](#documentcontract) }; `403` without contract tracking, `409` on stale `updated_at` |
|
|
53
59
|
| GET | `/v1/accounts/{accountId}/documents` | `DocumentResource#list` | Bearer token or `X-Api-Key` | path `accountId*`: string<br>query `status`: string<br>query `method`: string<br>query `search`: string<br>query `tags`: string<br>query `sort`: string<br>query `page`: integer<br>query `per-page`: integer | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: Array<[`Document`](#document)> } |
|
|
54
60
|
| POST | `/v1/accounts/{accountId}/documents` | `DocumentResource#upload` | Bearer token or `X-Api-Key` | path `accountId*`: string | required; `multipart/form-data` object { `file*`: string (binary) } | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`Document`](#document) } |
|
|
55
61
|
| GET | `/v1/accounts/{accountId}/documents/search` | `DocumentResource#search` | Bearer token or `X-Api-Key` | path `accountId*`: string<br>query `search`: string<br>query `status`: string<br>query `page`: integer<br>query `per-page`: integer | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: Array<[`Document`](#document)> } |
|
|
@@ -111,7 +117,7 @@ Required parameters and object properties are marked with `*`.
|
|
|
111
117
|
| POST | `/v1/documents/{documentId}/assignments/{assignmentId}` | `AssignmentResource#sign` | `signer-access-code` query parameter | path `documentId*`: string<br>path `assignmentId*`: string | required; `application/json` Array<object { `itemId*`: string; `fieldId*`: string; `pageId*`: string; `value*`: string }> | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: object } |
|
|
112
118
|
| PUT | `/v1/documents/{documentId}/assignments/{assignmentId}/reject` | `AssignmentResource#decline` | `signer-access-code` query parameter | path `documentId*`: string<br>path `assignmentId*`: string | required; `application/json` object { `decline_reason*`: string } | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: Array<any> } |
|
|
113
119
|
| PUT | `/v1/documents/{documentId}/assignments/{assignmentId}/reset-expiration` | `AssignmentResource#reset_expiration` | Bearer token or `X-Api-Key` | path `documentId*`: string<br>path `assignmentId*`: string | required; `application/json` object { `expires_at*`: string (date-time) or `null` to clear } | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`Assignment`](#assignment) } |
|
|
114
|
-
| POST | `/v1/documents/{documentId}/assignments/{assignmentId}/signers/{signerId}/estimate-resend-cost` | `AssignmentResource#estimate_resend_cost` | Bearer token or `X-Api-Key` | path `documentId*`: string<br>path `assignmentId*`: string<br>path `signerId*`: string | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`
|
|
120
|
+
| POST | `/v1/documents/{documentId}/assignments/{assignmentId}/signers/{signerId}/estimate-resend-cost` | `AssignmentResource#estimate_resend_cost` | Bearer token or `X-Api-Key` | path `documentId*`: string<br>path `assignmentId*`: string<br>path `signerId*`: string | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`ResendCostEstimate`](#resendcostestimate) } |
|
|
115
121
|
| GET | `/v1/documents/{documentId}/assignments/{assignmentId}/signers/{signerId}/identity-verification` | `AssignmentResource#identity_review` | Bearer token or `X-Api-Key` | path `documentId*`, `assignmentId*`, `signerId*`: string | None | `200` envelope data [`SignerIdentityReview`](#signeridentityreview); `404` when no verification exists |
|
|
116
122
|
| GET | `/v1/documents/{documentId}/assignments/{assignmentId}/signers/{signerId}/identity-verification/images/{name}` | `AssignmentResource#identity_review_image` | Bearer token or `X-Api-Key` | path `documentId*`, `assignmentId*`, `signerId*`: string; `name*`: enum `document_front`, `document_back`, `document_pdf`, `face` | None | `200` binary `image/jpeg`, `image/png` or `application/pdf`, `Cache-Control: no-store`; `404` unless evidence exists and review is InReview |
|
|
117
123
|
| POST | `/v1/documents/{documentId}/assignments/{assignmentId}/signers/{signerId}/identity-verification/review` | `AssignmentResource#decide_identity_review` | Bearer token or `X-Api-Key` | path `documentId*`, `assignmentId*`, `signerId*`: string | required; `application/json` { `decision*`: enum `Approved`, `Rejected`; `note`: string or null (max 2000 characters) } | `200` envelope data [`SignerIdentityReview`](#signeridentityreview); `400` unless InReview and document is pending_signature or pending_identity_review |
|
|
@@ -151,8 +157,8 @@ Required parameters and object properties are marked with `*`.
|
|
|
151
157
|
| PUT | `/v1/users/self/mfa/totp/confirm` | `AuthResource#confirm_totp_enrollment` | Bearer token or `X-Api-Key` | None | required; `application/json` object { `id*`: string (SDK `method_id:`); `code*`: string; `password`: string (password); `reauth_code`: string } — `password` or `reauth_code` only when replacing a confirmed method | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: object { `recovery_codes`: Array<string> (shown once) } } |
|
|
152
158
|
| POST | `/v1/users/self/mfa/recovery-codes` | `AuthResource#regenerate_recovery_codes` | Bearer token or `X-Api-Key` | None | required; `application/json` object { `password`: string (password); `code`: string } — one of the two is required (checked locally) | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: object { `recovery_codes`: Array<string> } } |
|
|
153
159
|
| DELETE | `/v1/users/self/mfa/{customId}` | `AuthResource#delete_mfa_method` | Bearer token or `X-Api-Key` | path `customId*`: string | required; `application/json` object { `password`: string (password); `code`: string } — one of the two is required (checked locally) | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: object { `is_mfa_enabled`: boolean } } |
|
|
154
|
-
| GET | `/v1/users/self/notification-preferences` | `UserResource#notification_preferences` |
|
|
155
|
-
| PUT | `/v1/users/self/notification-preferences` | `UserResource#update_notification_preferences` |
|
|
160
|
+
| GET | `/v1/users/self/notification-preferences` | `UserResource#notification_preferences` | User bearer token or `X-Api-Key`; unavailable to third-party OAuth apps | None | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`NotificationPreferences`](#notificationpreferences) } |
|
|
161
|
+
| PUT | `/v1/users/self/notification-preferences` | `UserResource#update_notification_preferences` | User bearer token or `X-Api-Key`; unavailable to third-party OAuth apps | None | required; `application/json` [`NotificationPreferences`](#notificationpreferences) | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: [`NotificationPreferences`](#notificationpreferences) } |
|
|
156
162
|
| GET | `/v1/users/self/stats` | `UserResource#stats` | Bearer token or `X-Api-Key` | query `granularity`: string<br>query `month`: string | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: Array<[`DocumentStatsRow`](#documentstatsrow)> } |
|
|
157
163
|
| POST | `/v1/verify` | `SignerResource#verify_email` | `signer-access-code` query parameter | None | required; `application/json` object { `verification-code*`: string } | `200` `application/json` [`Envelope`](#envelope) |
|
|
158
164
|
| GET | `/v1/webhooks/event-types` | `WebhookResource#list_event_types` | Bearer token or `X-Api-Key` | None | None | `200` `application/json` [`Envelope`](#envelope) plus object { `data`: Array<[`WebhookEventType`](#webhookeventtype)> } |
|
|
@@ -249,6 +255,21 @@ locally together with the `.pdf` extension and `%PDF-` header.
|
|
|
249
255
|
with commas; matching documents carry every listed tag. `DocumentResource#replace_tags` and `#append_tags` accept
|
|
250
256
|
arrays of tag IDs. Use a tag ID with `DocumentResource#detach_tag`.
|
|
251
257
|
|
|
258
|
+
### Contract tracking
|
|
259
|
+
|
|
260
|
+
Contract terms belong to an existing document; setting terms never uploads, sends, or signs the document.
|
|
261
|
+
`ContractResource#update` creates or partially changes the terms. Explicit `nil` values remain JSON null so they
|
|
262
|
+
clear nullable fields. To record a signed amendment, renewal, or termination instrument, upload that document and
|
|
263
|
+
set its `parent_document_id`, `relation_type`, and changed terms; the delta applies after the instrument is
|
|
264
|
+
certificated. `#get` reads the terms or an instrument, and `#related` always reads the root family.
|
|
265
|
+
|
|
266
|
+
The hourly lifecycle moves active contracts through notice, expiry, and automatic renewal. `#renew` starts the
|
|
267
|
+
next term immediately; sending the current `term` makes a lost-response retry safe. `#terminate` closes the
|
|
268
|
+
contract and records its optional reason. Writers can send the `updated_at` they read as an optimistic-lock
|
|
269
|
+
precondition; the API refuses a stale write with `409`. Reads remain available without the contract-tracking
|
|
270
|
+
plan, while writes answer `403` when the plan lacks the feature. OAuth reads require `documents:read`, and writes
|
|
271
|
+
require `documents:write`.
|
|
272
|
+
|
|
252
273
|
### KPI statistics
|
|
253
274
|
|
|
254
275
|
`UserResource#stats` and `AccountResource#stats` validate `granularity` (`monthly` or `daily`) and `month`
|
|
@@ -357,7 +378,9 @@ if lost, delete and recreate the endpoint. `#list_endpoints(url: registered_url)
|
|
|
357
378
|
`#delete_endpoint_by_url(registered_url)` deletes that endpoint, returning `nil`. `WebhookResource#create_endpoint`
|
|
358
379
|
and `#update_endpoint` accept only `url`, `email`, `events`, `name`, `is_active`, and `signing_enabled`.
|
|
359
380
|
`#register`, `#get`, and `#inactivate` (`/webhooks/subscriptions`, `/webhooks/inactivate`) act on the account's
|
|
360
|
-
oldest endpoint; `#register` accepts only `url`, `email`, `events`, and `is_active`.
|
|
381
|
+
oldest endpoint; `#register` accepts only `url`, `email`, `events`, and `is_active`. New or changed endpoint URLs
|
|
382
|
+
must be absolute HTTPS URLs; the SDK rejects other schemes locally, while the API also refuses private, loopback,
|
|
383
|
+
link-local, and other reserved destination addresses.
|
|
361
384
|
|
|
362
385
|
Each delivery is a JSON `POST` carrying `webhook-id` (stable across attempts of one event to one endpoint; use it to
|
|
363
386
|
deduplicate), `webhook-timestamp` (Unix seconds), and, when signing is enabled, `webhook-signature`. Any `2xx` is
|
|
@@ -385,7 +408,7 @@ return-value, and usage details.
|
|
|
385
408
|
| `Assinafy::Client.from_config`, `.from_hash` | Construct a client from string- or symbol-keyed configuration. | [`client.rb`](../lib/assinafy/client.rb) |
|
|
386
409
|
| `Client#faraday_connection` | Return the configured Faraday connection for advanced integration. | [`client.rb`](../lib/assinafy/client.rb) |
|
|
387
410
|
| `Client#upload_and_request_signatures` | Upload, optionally wait, create signers, and create a virtual assignment. | [`client.rb`](../lib/assinafy/client.rb) |
|
|
388
|
-
| `Client#auth`, `#oauth`, `#accounts`, `#users`, `#documents`, `#signers`, `#signer_documents`, `#assignments`, `#webhooks`, `#templates`, `#fields`, `#tags`, `#webhook_verifier` | Return the client's resource and helper instances. | [`client.rb`](../lib/assinafy/client.rb) |
|
|
411
|
+
| `Client#auth`, `#oauth`, `#accounts`, `#users`, `#documents`, `#contracts`, `#signers`, `#signer_documents`, `#assignments`, `#webhooks`, `#templates`, `#fields`, `#tags`, `#webhook_verifier` | Return the client's resource and helper instances. | [`client.rb`](../lib/assinafy/client.rb) |
|
|
389
412
|
| `Assinafy::Configuration.new`, `.from_hash` | Build configuration directly or from string/symbol keys. | [`configuration.rb`](../lib/assinafy/configuration.rb) |
|
|
390
413
|
| `Configuration#auth_headers` | Return the selected API-key, bearer, or empty authentication header set. | [`configuration.rb`](../lib/assinafy/configuration.rb) |
|
|
391
414
|
| Configuration readers/writers: `api_key`, `token`, `account_id`, `base_url`, `webhook_secret`, `timeout`, `logger` | Read or update configuration values; `base_url=` and `timeout=` validate like the constructor. Construct a new client to apply changes. | [`configuration.rb`](../lib/assinafy/configuration.rb) |
|
|
@@ -439,6 +462,8 @@ A workspace account (organization).
|
|
|
439
462
|
| `notification_sender_type` | string | No | No | enum: `["User", "Account"]` |
|
|
440
463
|
| `roles` | Array<string> | No | No | — |
|
|
441
464
|
| `is_delete_allowed` | boolean | No | No | — |
|
|
465
|
+
| `limits` | object | No | No | Current-plan limits; present only on the single-account response. More keys may be added. |
|
|
466
|
+
| `limits.webhook_endpoints` | integer | No | No | Maximum webhook endpoints: 1, or 3 with the Multiple Webhooks feature. |
|
|
442
467
|
| `created_at` | string (date-time) | No | No | — |
|
|
443
468
|
|
|
444
469
|
### AccountTheme
|
|
@@ -466,7 +491,9 @@ A request for signers to sign a document.
|
|
|
466
491
|
| --- | --- | --- | --- | --- |
|
|
467
492
|
| `resource` | string | No | No | — |
|
|
468
493
|
| `id` | string | No | No | — |
|
|
469
|
-
| `
|
|
494
|
+
| `document_id` | string | No | Yes | Document this assignment belongs to; null only after the document is deleted. |
|
|
495
|
+
| `created_at` | string (date-time) | No | No | — |
|
|
496
|
+
| `sender_email` | string (email) | No | Yes | Null once the sender has deleted their account. |
|
|
470
497
|
| `method` | string | No | No | enum: `["virtual", "collect"]` |
|
|
471
498
|
| `expires_at` | string (date-time) | No | Yes | — |
|
|
472
499
|
| `message` | string | No | Yes | — |
|
|
@@ -608,12 +635,14 @@ A document and its current lifecycle state.
|
|
|
608
635
|
| `artifacts` | object | No | No | Artifact download URLs keyed by name. Always `original`, plus `thumbnail` once one exists. A certificated document also carries `certificated`, `certificate-page` and `bundle`, and `pades` when it was signed with a digital certificate — the PAdES version holds the signers' ICP-Brasil signatures, which certification flattens out of the certificated PDF. |
|
|
609
636
|
| `is_closed` | boolean | No | No | — |
|
|
610
637
|
| `signing_url` | string | No | No | — |
|
|
638
|
+
| `verification_code` | string | No | Yes | Code printed on the certificate page; null until certificated. |
|
|
639
|
+
| `verification_url` | string (uri) | No | Yes | Public verification page; null until certificated. |
|
|
611
640
|
| `decline_reason` | string | No | Yes | — |
|
|
612
641
|
| `declined_by` | any JSON value | No | Yes | — |
|
|
613
642
|
| `tags` | Array<object> | No | No | — |
|
|
614
643
|
| `tags[].id` | string | No | No | — |
|
|
615
644
|
| `tags[].name` | string | No | No | — |
|
|
616
|
-
| `assignment` | object | No | Yes |
|
|
645
|
+
| `assignment` | object | No | Yes | Included by list/details/signer-list/template-generation responses; search and upload omit it. Only rename follows `?expand=assignment`. |
|
|
617
646
|
| `assignment.resource` | string | No | No | — |
|
|
618
647
|
| `assignment.id` | string | No | No | — |
|
|
619
648
|
| `assignment.sender_email` | string (email) | No | No | — |
|
|
@@ -626,6 +655,7 @@ A document and its current lifecycle state.
|
|
|
626
655
|
| `assignment.summary` | [`AssignmentSummary`](#assignmentsummary) | No | No | — |
|
|
627
656
|
| `assignment.signing_urls` | Array<[`SigningUrl`](#signingurl)> | No | No | — |
|
|
628
657
|
| `pages` | Array<[`DocumentPage`](#documentpage)> | No | No | — |
|
|
658
|
+
| `has_contract` | boolean | No | No | Only on workspace `GET /documents/{documentId}` when contract tracking is available. A missing key means unknown. |
|
|
629
659
|
| `created_at` | string (date-time) | No | No | — |
|
|
630
660
|
| `updated_at` | string (date-time) | No | No | — |
|
|
631
661
|
|
|
@@ -644,6 +674,85 @@ A document activity event.
|
|
|
644
674
|
| `origin.user-agent` | string | No | No | — |
|
|
645
675
|
| `created_at` | string (date-time) | No | No | — |
|
|
646
676
|
|
|
677
|
+
### DocumentContract
|
|
678
|
+
|
|
679
|
+
Contract terms tracked on a document. The single-contract and related-family reads can include amendments; the
|
|
680
|
+
workspace list can include the document when `expand=document` is requested.
|
|
681
|
+
|
|
682
|
+
| Property | Type | Required | Nullable | Constraints / description |
|
|
683
|
+
| --- | --- | --- | --- | --- |
|
|
684
|
+
| `resource` | string | No | No | `document_contract` in single-resource responses. |
|
|
685
|
+
| `document_id` | string | No | No | Document that owns the terms. |
|
|
686
|
+
| `status` | string | No | No | enum: `["draft", "active", "expired", "terminated"]` |
|
|
687
|
+
| `counterparty_name` | string | No | Yes | — |
|
|
688
|
+
| `counterparty_taxpayer_id` | string | No | Yes | Normalized CPF/CNPJ, digits only. |
|
|
689
|
+
| `value` | number | No | Yes | At most two decimal places. |
|
|
690
|
+
| `currency` | string | No | Yes | ISO 4217 code; defaults to BRL when a value is set. |
|
|
691
|
+
| `start_date` | string (date) | No | Yes | — |
|
|
692
|
+
| `end_date` | string (date) | No | Yes | — |
|
|
693
|
+
| `auto_renew` | boolean | No | No | — |
|
|
694
|
+
| `renewal_term_months` | integer | No | Yes | — |
|
|
695
|
+
| `notice_period_days` | integer | No | Yes | — |
|
|
696
|
+
| `term` | integer | No | No | Renewal term number, starting at 1. |
|
|
697
|
+
| `owner_id` | string | No | Yes | Active workspace member responsible for lifecycle email. |
|
|
698
|
+
| `metadata` | object | No | Yes | Up to 50 caller-defined string values. |
|
|
699
|
+
| `external_references` | Array<object> | No | No | IDs in outside systems, sorted by system and ID. |
|
|
700
|
+
| `external_references[].system` | string | No | No | Outside system name. |
|
|
701
|
+
| `external_references[].id` | string | No | No | ID in that system. |
|
|
702
|
+
| `document` | [`Document`](#document) | No | No | Only on list results with `expand=document`. |
|
|
703
|
+
| `amendments` | Array<[`DocumentContractAmendment`](#documentcontractamendment)> | No | No | Only on single-contract/family reads. |
|
|
704
|
+
| `created_at` | string (date-time) | No | No | — |
|
|
705
|
+
| `updated_at` | string (date-time) | No | No | Moves on every direct or lifecycle change. |
|
|
706
|
+
|
|
707
|
+
### DocumentContractAmendment
|
|
708
|
+
|
|
709
|
+
A document linked as an amendment, renewal, or termination instrument. Its terms are a delta applied to the
|
|
710
|
+
parent after this document is certificated.
|
|
711
|
+
|
|
712
|
+
| Property | Type | Required | Nullable | Constraints / description |
|
|
713
|
+
| --- | --- | --- | --- | --- |
|
|
714
|
+
| `resource` | string | No | No | `document_contract_amendment` in single-resource responses. |
|
|
715
|
+
| `document_id` | string | No | No | The instrument document. |
|
|
716
|
+
| `document_name` | string | No | No | Instrument document name. |
|
|
717
|
+
| `parent_document_id` | string | No | No | Root contract document. |
|
|
718
|
+
| `relation_type` | string | No | No | enum: `["amendment", "renewal", "termination"]` |
|
|
719
|
+
| `number` | integer | No | Yes | Applied position; null while pending. |
|
|
720
|
+
| `status` | string | No | No | enum: `["pending", "applied"]` |
|
|
721
|
+
| `document_status` | string | No | No | Current lifecycle status of the instrument document. |
|
|
722
|
+
| `apply_error` | string | No | Yes | Why a signed instrument has not applied yet. |
|
|
723
|
+
| `currency` | string | No | Yes | Currency used by amount values in `terms` and `changes`. |
|
|
724
|
+
| `terms` | object | No | Yes | Only contract terms changed by this instrument. |
|
|
725
|
+
| `changes` | object | No | Yes | Changed fields as `{ from:, to: }`, previewed while pending and fixed once applied. |
|
|
726
|
+
| `applied_at` | string (date-time) | No | Yes | — |
|
|
727
|
+
| `created_at` | string (date-time) | No | No | — |
|
|
728
|
+
| `updated_at` | string (date-time) | No | No | — |
|
|
729
|
+
|
|
730
|
+
### DocumentContractInput
|
|
731
|
+
|
|
732
|
+
Partial body for setting terms or linking an instrument. Only sent fields change. JSON null clears a nullable
|
|
733
|
+
term; for a pending instrument it removes that term from the delta. `external_references` replaces the whole list.
|
|
734
|
+
|
|
735
|
+
| Property | Type | Required | Nullable | Constraints / description |
|
|
736
|
+
| --- | --- | --- | --- | --- |
|
|
737
|
+
| `status` | string | No | No | enum: `["draft", "active", "expired", "terminated"]`; closed statuses are creation-only. |
|
|
738
|
+
| `counterparty_name` | string | No | Yes | Maximum 255 characters. |
|
|
739
|
+
| `counterparty_taxpayer_id` | string | No | Yes | Valid formatted or unformatted CPF/CNPJ. |
|
|
740
|
+
| `value` | number | No | Yes | 0–9,999,999,999,999.99; at most two decimal places. |
|
|
741
|
+
| `currency` | string | No | Yes | Three-letter code, stored uppercase. |
|
|
742
|
+
| `start_date` | string (date) | No | Yes | `YYYY-MM-DD`, year 1000 or later. |
|
|
743
|
+
| `end_date` | string (date) | No | Yes | On or after `start_date`. |
|
|
744
|
+
| `auto_renew` | boolean | No | Yes | Null resets to false. |
|
|
745
|
+
| `renewal_term_months` | integer | No | Yes | 1–600; defaults to 12 when unset. |
|
|
746
|
+
| `notice_period_days` | integer | No | Yes | 0–3650. |
|
|
747
|
+
| `owner_id` | string | No | Yes | Active member of the workspace. |
|
|
748
|
+
| `metadata` | object | No | Yes | Up to 50 keys of 1–40 letters, digits, `_`, `-`, or `.`, each holding at most 500 characters. |
|
|
749
|
+
| `external_references` | Array<object> | No | Yes | At most 20; null or an empty array clears the list. |
|
|
750
|
+
| `external_references[].system` | string | Yes | No | Up to 64 letters, digits, `_`, `-`, or `.`; stored lowercase. |
|
|
751
|
+
| `external_references[].id` | string | Yes | No | Up to 255 characters. |
|
|
752
|
+
| `parent_document_id` | string | No | Yes | Links to a contract family; null unlinks a pending instrument. |
|
|
753
|
+
| `relation_type` | string | No | No | Required for the first link; enum: `["amendment", "renewal", "termination"]`. |
|
|
754
|
+
| `updated_at` | string (date-time) | No | Yes | Optional optimistic-lock value; a stale value returns `409`. Null is last-write-wins. |
|
|
755
|
+
|
|
647
756
|
### DocumentPage
|
|
648
757
|
|
|
649
758
|
| Property | Type | Required | Nullable | Constraints / description |
|
|
@@ -795,6 +904,10 @@ Owner-facing document notifications, keyed by notification type. `true` means th
|
|
|
795
904
|
| `DocumentProcessingFailed` | boolean | No | No | An uploaded document could not be processed. |
|
|
796
905
|
| `TemplateProcessingFailed` | boolean | No | No | A template could not be processed. |
|
|
797
906
|
| `SignerWhatsappFailed` | boolean | No | No | A WhatsApp notification to a signer could not be delivered. |
|
|
907
|
+
| `ContractRenewalNotice` | boolean | No | No | A tracked contract entered its notice period. |
|
|
908
|
+
| `ContractExpiring` | boolean | No | No | A tracked contract ends in 45 or 30 days. |
|
|
909
|
+
| `ContractExpired` | boolean | No | No | A tracked contract ended without renewal. |
|
|
910
|
+
| `ContractRenewed` | boolean | No | No | A tracked contract was automatically renewed into its next term. |
|
|
798
911
|
|
|
799
912
|
### OAuthRevokeRequest
|
|
800
913
|
|
|
@@ -825,6 +938,20 @@ Body of `POST /v1/oauth/token`. Sent form-encoded per RFC 6749, or as JSON.
|
|
|
825
938
|
| `subject_token_type` | string | No | No | enum: `["urn:ietf:params:oauth:token-type:access_token"]`; Token-exchange grant only. Required; only `urn:ietf:params:oauth:token-type:access_token` is supported. |
|
|
826
939
|
| `requested_token_type` | string | No | No | enum: `["urn:ietf:params:oauth:token-type:access_token"]`; Token-exchange grant only. Optional; when present it must agree with the only type this server issues. |
|
|
827
940
|
|
|
941
|
+
### ResendCostEstimate
|
|
942
|
+
|
|
943
|
+
Credits needed to resend one signer's notification. A reminder consumes no document allowance.
|
|
944
|
+
|
|
945
|
+
| Property | Type | Required | Nullable | Constraints / description |
|
|
946
|
+
| --- | --- | --- | --- | --- |
|
|
947
|
+
| `total` | number | No | No | Sum of the breakdown costs. |
|
|
948
|
+
| `breakdown` | Array<object> | No | No | Per-charge resend costs. |
|
|
949
|
+
| `breakdown[].code` | string | No | No | For example `NotificationWhatsappResend`. |
|
|
950
|
+
| `breakdown[].name` | string | No | No | Human-readable charge name. |
|
|
951
|
+
| `breakdown[].cost` | number | No | No | Credits charged for this item. |
|
|
952
|
+
| `credit_balance` | number | No | Yes | Available credits; null when credits are unlimited. |
|
|
953
|
+
| `has_sufficient_credits` | boolean | No | No | Whether the workspace can pay for the resend. |
|
|
954
|
+
|
|
828
955
|
### Signer
|
|
829
956
|
|
|
830
957
|
A signing party belonging to a workspace account.
|
|
@@ -987,6 +1114,10 @@ A single webhook delivery-history entry.
|
|
|
987
1114
|
| `endpoint` | string | No | Yes | URL that received the request. |
|
|
988
1115
|
| `payload` | object | No | Yes | JSON payload sent to the endpoint. |
|
|
989
1116
|
| `delivered` | boolean | No | No | Whether delivery succeeded. |
|
|
1117
|
+
| `webhook_id` | string | No | Yes | Stable `webhook-id` header across attempts of this event to this endpoint. |
|
|
1118
|
+
| `attempt` | integer | No | Yes | Attempt number, starting at 1. |
|
|
1119
|
+
| `kind` | string | No | No | enum: `["scheduled", "probe", "manual"]` |
|
|
1120
|
+
| `will_retry` | boolean | No | No | Whether another attempt follows this entry. |
|
|
990
1121
|
| `http_status` | integer | No | Yes | HTTP status returned (null if connection failed). |
|
|
991
1122
|
| `response_body` | string | No | Yes | Endpoint response body, truncated to 2000 chars. |
|
|
992
1123
|
| `error` | string | No | Yes | Delivery error message, if any. |
|
|
@@ -999,12 +1130,15 @@ A URL that receives the account's webhook events. Every active endpoint subscrib
|
|
|
999
1130
|
|
|
1000
1131
|
| Property | Type | Required | Nullable | Constraints / description |
|
|
1001
1132
|
| --- | --- | --- | --- | --- |
|
|
1133
|
+
| `resource` | string | No | No | Always `activity_event_subscription` in single-resource responses. |
|
|
1002
1134
|
| `id` | string | No | No | Endpoint ID. |
|
|
1003
1135
|
| `name` | string | No | Yes | Label to tell endpoints apart. |
|
|
1004
|
-
| `url` | string (uri) | No | No | URL that receives the events
|
|
1136
|
+
| `url` | string (uri) | No | No | HTTPS URL that receives the events. |
|
|
1005
1137
|
| `email` | string (email) | No | No | Contact email for delivery-failure notices. |
|
|
1006
1138
|
| `events` | Array<string> | No | No | Event types delivered to this endpoint. |
|
|
1007
1139
|
| `is_active` | boolean | No | No | Whether events are delivered to this endpoint. |
|
|
1140
|
+
| `is_held` | boolean | No | No | Whether deliveries are paused after repeated failures. |
|
|
1141
|
+
| `held_since` | integer | No | Yes | Unix timestamp when the pause began; null while deliveries run. |
|
|
1008
1142
|
| `signing_enabled` | boolean | No | No | Whether deliveries carry a `webhook-signature` header. |
|
|
1009
1143
|
| `created_at` | string (date-time) | No | No | — |
|
|
1010
1144
|
| `updated_at` | string (date-time) | No | No | — |
|
|
@@ -1054,6 +1188,8 @@ An account's webhook subscription configuration: the account's oldest webhook en
|
|
|
1054
1188
|
| --- | --- | --- | --- | --- |
|
|
1055
1189
|
| `events` | Array<string> | No | No | Event types subscribed for delivery. |
|
|
1056
1190
|
| `is_active` | boolean | No | No | Whether webhook delivery is active. |
|
|
1191
|
+
| `is_held` | boolean | No | No | Whether deliveries are paused after repeated failures. |
|
|
1192
|
+
| `held_since` | integer | No | Yes | Unix timestamp when the pause began; null while deliveries run. |
|
|
1057
1193
|
| `url` | string | No | Yes | Webhook endpoint URL. |
|
|
1058
1194
|
| `email` | string | No | Yes | Contact email for delivery notices. |
|
|
1059
1195
|
| `updated_at` | string (date-time) | No | Yes | — |
|
data/lib/assinafy/client.rb
CHANGED
|
@@ -33,6 +33,8 @@ module Assinafy
|
|
|
33
33
|
attr_reader :users
|
|
34
34
|
# @return [Resources::DocumentResource]
|
|
35
35
|
attr_reader :documents
|
|
36
|
+
# @return [Resources::ContractResource]
|
|
37
|
+
attr_reader :contracts
|
|
36
38
|
# @return [Resources::SignerResource]
|
|
37
39
|
attr_reader :signers
|
|
38
40
|
# @return [Resources::SignerDocumentResource]
|
|
@@ -84,6 +86,7 @@ module Assinafy
|
|
|
84
86
|
@accounts = Resources::AccountResource.new(@connection, account_id, @logger)
|
|
85
87
|
@users = Resources::UserResource.new(@connection, nil, @logger)
|
|
86
88
|
@documents = Resources::DocumentResource.new(@connection, account_id, @logger)
|
|
89
|
+
@contracts = Resources::ContractResource.new(@connection, account_id, @logger)
|
|
87
90
|
@signers = Resources::SignerResource.new(@connection, account_id, @logger)
|
|
88
91
|
@signer_documents = Resources::SignerDocumentResource.new(@connection, nil, @logger)
|
|
89
92
|
@assignments = Resources::AssignmentResource.new(@connection, account_id, @logger)
|
|
@@ -190,6 +193,7 @@ module Assinafy
|
|
|
190
193
|
# # },
|
|
191
194
|
# # assignment: {
|
|
192
195
|
# # "resource" => "assignment", "id" => "assignment-id",
|
|
196
|
+
# # "document_id" => "document-id", "created_at" => "2026-06-05T21:21:19Z",
|
|
193
197
|
# # "sender_email" => "sender@example.com", "method" => "virtual",
|
|
194
198
|
# # "expires_at" => nil, "message" => "Please review and sign",
|
|
195
199
|
# # "signers" => [{ "id" => "signer-id", "full_name" => "Example Signer",
|
|
@@ -303,6 +303,8 @@ module Assinafy
|
|
|
303
303
|
# data: [
|
|
304
304
|
# {
|
|
305
305
|
# 'id' => 'assignment-id',
|
|
306
|
+
# 'document_id' => 'document-id',
|
|
307
|
+
# 'created_at' => '2026-06-05T21:21:19Z',
|
|
306
308
|
# 'sender_email' => 'sender@example.com',
|
|
307
309
|
# 'method' => 'virtual',
|
|
308
310
|
# 'expires_at' => nil,
|
|
@@ -342,6 +344,7 @@ module Assinafy
|
|
|
342
344
|
# # "message" => "Please sign" }
|
|
343
345
|
# # => {
|
|
344
346
|
# # "resource" => "assignment", "id" => "assignment-id",
|
|
347
|
+
# # "document_id" => "document-id", "created_at" => "2026-06-05T21:21:19Z",
|
|
345
348
|
# # "sender_email" => "sender@example.com", "method" => "virtual",
|
|
346
349
|
# # "expires_at" => nil, "message" => "Please sign",
|
|
347
350
|
# # "signers" => [{ "id" => "signer-id", "full_name" => "Example Signer",
|
|
@@ -475,9 +478,7 @@ module Assinafy
|
|
|
475
478
|
# @param document_id [String]
|
|
476
479
|
# @param assignment_id [String]
|
|
477
480
|
# @param signer_id [String]
|
|
478
|
-
# @return [Hash] cost
|
|
479
|
-
# total_credits, breakdown[], document_balance, credit_balance, has_sufficient_resources,
|
|
480
|
-
# blocking_reason, message)
|
|
481
|
+
# @return [Hash] resend cost (`total`, `breakdown`, `credit_balance`, `has_sufficient_credits`)
|
|
481
482
|
# @raise [Assinafy::ApiError] on an unsuccessful API response
|
|
482
483
|
# @raise [Assinafy::NetworkError] on transport or TLS failure
|
|
483
484
|
# @raise [Assinafy::ValidationError] on invalid required input
|
|
@@ -486,13 +487,11 @@ module Assinafy
|
|
|
486
487
|
# resource.estimate_resend_cost("document-id", "assignment-id", "signer-id")
|
|
487
488
|
# # (no request body)
|
|
488
489
|
# # => {
|
|
489
|
-
# # "
|
|
490
|
-
# # "
|
|
491
|
-
# #
|
|
492
|
-
# #
|
|
493
|
-
# #
|
|
494
|
-
# # "document_balance" => 10, "credit_balance" => 100,
|
|
495
|
-
# # "has_sufficient_resources" => true, "blocking_reason" => nil, "message" => nil
|
|
490
|
+
# # "total" => 0.45,
|
|
491
|
+
# # "breakdown" => [{ "code" => "NotificationWhatsappResend",
|
|
492
|
+
# # "name" => "Whatsapp Notification Resend", "cost" => 0.45 }],
|
|
493
|
+
# # "credit_balance" => 100,
|
|
494
|
+
# # "has_sufficient_credits" => true
|
|
496
495
|
# # }
|
|
497
496
|
def estimate_resend_cost(document_id, assignment_id, signer_id)
|
|
498
497
|
doc_id = require_id(document_id, 'Document ID')
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Assinafy
|
|
4
|
+
module Resources
|
|
5
|
+
# Contract terms tracked on workspace documents, including amendments,
|
|
6
|
+
# renewals, terminations, and lifecycle queries.
|
|
7
|
+
class ContractResource < BaseResource
|
|
8
|
+
STATUSES = %w[draft active expired terminated].freeze
|
|
9
|
+
RELATION_TYPES = %w[amendment renewal termination].freeze
|
|
10
|
+
SORTS = %w[end_date -end_date updated_at -updated_at].freeze
|
|
11
|
+
LIST_FIELDS = %w[
|
|
12
|
+
status counterparty search end_date_from end_date_to ending_within_days
|
|
13
|
+
external_system external_id sort expand page per-page
|
|
14
|
+
].freeze
|
|
15
|
+
UPDATE_FIELDS = %w[
|
|
16
|
+
status counterparty_name counterparty_taxpayer_id value currency start_date end_date
|
|
17
|
+
auto_renew renewal_term_months notice_period_days owner_id metadata external_references
|
|
18
|
+
parent_document_id relation_type updated_at
|
|
19
|
+
].freeze
|
|
20
|
+
RENEW_FIELDS = %w[end_date value term updated_at].freeze
|
|
21
|
+
TERMINATE_FIELDS = %w[reason updated_at].freeze
|
|
22
|
+
|
|
23
|
+
# List tracked contracts in a workspace, most recently updated first.
|
|
24
|
+
#
|
|
25
|
+
# @param params [Hash] optional filters: `status`, `counterparty`, `search`,
|
|
26
|
+
# `end_date_from`, `end_date_to`, `ending_within_days`, `external_system`,
|
|
27
|
+
# `external_id`, `sort`, `expand`, `page`, and `per_page`
|
|
28
|
+
# @param account_id_override [String, nil]
|
|
29
|
+
# @return [Hash{Symbol=>Array,Hash}] `{ data: [...], meta: { ... } }`
|
|
30
|
+
# @raise [Assinafy::ApiError] on an unsuccessful API response
|
|
31
|
+
# @raise [Assinafy::NetworkError] on transport or TLS failure
|
|
32
|
+
# @raise [Assinafy::ValidationError] on invalid filters or account ID
|
|
33
|
+
# @see GET /accounts/{accountId}/contracts
|
|
34
|
+
# @example List active contracts ending in the next 30 days
|
|
35
|
+
# client.contracts.list(status: ['active'], ending_within_days: 30, expand: 'document')
|
|
36
|
+
# # Request: GET /accounts/{accountId}/contracts?status=active&ending_within_days=30&expand=document
|
|
37
|
+
# # => {
|
|
38
|
+
# # data: [{
|
|
39
|
+
# # 'resource' => 'document_contract', 'document_id' => 'document-id',
|
|
40
|
+
# # 'status' => 'active', 'counterparty_name' => 'Example Ltda',
|
|
41
|
+
# # 'counterparty_taxpayer_id' => '11222333000181', 'value' => 120000.50,
|
|
42
|
+
# # 'currency' => 'BRL', 'start_date' => '2026-11-01', 'end_date' => '2027-10-31',
|
|
43
|
+
# # 'auto_renew' => true, 'renewal_term_months' => 12, 'notice_period_days' => 30,
|
|
44
|
+
# # 'term' => 1, 'owner_id' => 'user-id', 'metadata' => { 'crm_stage' => 'won' },
|
|
45
|
+
# # 'external_references' => [{ 'system' => 'hubspot', 'id' => '98765' }],
|
|
46
|
+
# # 'document' => { 'id' => 'document-id', 'name' => 'agreement.pdf', 'status' => 'certificated' },
|
|
47
|
+
# # 'created_at' => '2026-10-23T12:00:00Z', 'updated_at' => '2026-10-23T12:00:00Z'
|
|
48
|
+
# # }],
|
|
49
|
+
# # meta: { current_page: 1, per_page: 20, total: 1, last_page: 1 }
|
|
50
|
+
# # }
|
|
51
|
+
def list(params = {}, account_id_override = nil)
|
|
52
|
+
acc_id = account_id(account_id_override)
|
|
53
|
+
query = contract_list_params(params)
|
|
54
|
+
|
|
55
|
+
call_list('Failed to list contracts') do
|
|
56
|
+
http_get("accounts/#{acc_id}/contracts", query)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Fetch the contract terms or pending/applied amendment linked to a document.
|
|
61
|
+
# Returns `nil` when the document has no tracked contract terms.
|
|
62
|
+
#
|
|
63
|
+
# @param document_id [String]
|
|
64
|
+
# @param account_id_override [String, nil]
|
|
65
|
+
# @return [Hash, nil] a DocumentContract or DocumentContractAmendment
|
|
66
|
+
# @raise [Assinafy::ApiError] on an unsuccessful response other than 404
|
|
67
|
+
# @raise [Assinafy::NetworkError] on transport or TLS failure
|
|
68
|
+
# @raise [Assinafy::ValidationError] on an invalid ID
|
|
69
|
+
# @see GET /accounts/{accountId}/documents/{documentId}/contract
|
|
70
|
+
# @example Read a contract and its amendments
|
|
71
|
+
# client.contracts.get('document-id')
|
|
72
|
+
# # Request: GET /accounts/{accountId}/documents/document-id/contract
|
|
73
|
+
# # => {
|
|
74
|
+
# # 'resource' => 'document_contract', 'document_id' => 'document-id', 'status' => 'active',
|
|
75
|
+
# # 'counterparty_name' => 'Example Ltda', 'counterparty_taxpayer_id' => '11222333000181',
|
|
76
|
+
# # 'value' => 130000, 'currency' => 'BRL', 'start_date' => '2026-11-01',
|
|
77
|
+
# # 'end_date' => '2028-06-30', 'auto_renew' => true, 'renewal_term_months' => 12,
|
|
78
|
+
# # 'notice_period_days' => 30, 'term' => 2, 'owner_id' => 'user-id',
|
|
79
|
+
# # 'metadata' => { 'crm_stage' => 'won' },
|
|
80
|
+
# # 'external_references' => [{ 'system' => 'hubspot', 'id' => '98765' }],
|
|
81
|
+
# # 'amendments' => [{
|
|
82
|
+
# # 'resource' => 'document_contract_amendment', 'document_id' => 'amendment-id',
|
|
83
|
+
# # 'document_name' => 'Amendment 1.pdf', 'parent_document_id' => 'document-id',
|
|
84
|
+
# # 'relation_type' => 'amendment', 'number' => 1, 'status' => 'applied',
|
|
85
|
+
# # 'document_status' => 'certificated', 'apply_error' => nil, 'currency' => 'BRL',
|
|
86
|
+
# # 'terms' => { 'value' => 130000, 'end_date' => '2028-06-30' },
|
|
87
|
+
# # 'changes' => { 'value' => { 'from' => 120000.50, 'to' => 130000 } },
|
|
88
|
+
# # 'applied_at' => '2027-05-12T18:20:00Z',
|
|
89
|
+
# # 'created_at' => '2027-05-10T15:00:00Z', 'updated_at' => '2027-05-12T18:20:00Z'
|
|
90
|
+
# # }],
|
|
91
|
+
# # 'created_at' => '2026-10-23T12:00:00Z', 'updated_at' => '2027-05-12T18:20:00Z'
|
|
92
|
+
# # }
|
|
93
|
+
def get(document_id, account_id_override = nil)
|
|
94
|
+
call_optional('Failed to fetch contract') do
|
|
95
|
+
http_get(contract_path(document_id, account_id_override))
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Create or partially update contract terms, or link a document as an
|
|
100
|
+
# amendment, renewal, or termination instrument. Explicit `nil` values
|
|
101
|
+
# are sent as JSON null because the API uses them to clear terms.
|
|
102
|
+
#
|
|
103
|
+
# @param document_id [String]
|
|
104
|
+
# @param payload [Hash] at least one DocumentContractInput property
|
|
105
|
+
# @param account_id_override [String, nil]
|
|
106
|
+
# @return [Hash] the saved DocumentContract or DocumentContractAmendment
|
|
107
|
+
# @raise [Assinafy::ApiError] including 403 without the plan and 409 on a stale `updated_at`
|
|
108
|
+
# @raise [Assinafy::NetworkError] on transport or TLS failure
|
|
109
|
+
# @raise [Assinafy::ValidationError] on invalid IDs or payload fields
|
|
110
|
+
# @see PUT /accounts/{accountId}/documents/{documentId}/contract
|
|
111
|
+
# @example Set a document's contract terms
|
|
112
|
+
# client.contracts.update(
|
|
113
|
+
# 'document-id', status: 'active', counterparty_name: 'Example Ltda',
|
|
114
|
+
# value: 120000.50, currency: 'BRL', start_date: '2026-11-01', end_date: '2027-10-31',
|
|
115
|
+
# auto_renew: true, renewal_term_months: 12, notice_period_days: 30,
|
|
116
|
+
# metadata: { crm_stage: 'won' },
|
|
117
|
+
# external_references: [{ system: 'hubspot', id: '98765' }]
|
|
118
|
+
# )
|
|
119
|
+
# # Request body:
|
|
120
|
+
# # { "status":"active", "counterparty_name":"Example Ltda", "value":120000.5,
|
|
121
|
+
# # "currency":"BRL", "start_date":"2026-11-01", "end_date":"2027-10-31",
|
|
122
|
+
# # "auto_renew":true, "renewal_term_months":12, "notice_period_days":30,
|
|
123
|
+
# # "metadata":{"crm_stage":"won"},
|
|
124
|
+
# # "external_references":[{"system":"hubspot","id":"98765"}] }
|
|
125
|
+
# # => { 'resource' => 'document_contract', 'document_id' => 'document-id', 'status' => 'active',
|
|
126
|
+
# # 'counterparty_name' => 'Example Ltda', 'counterparty_taxpayer_id' => nil,
|
|
127
|
+
# # 'value' => 120000.50, 'currency' => 'BRL', 'start_date' => '2026-11-01',
|
|
128
|
+
# # 'end_date' => '2027-10-31', 'auto_renew' => true, 'renewal_term_months' => 12,
|
|
129
|
+
# # 'notice_period_days' => 30, 'term' => 1, 'owner_id' => nil,
|
|
130
|
+
# # 'metadata' => { 'crm_stage' => 'won' },
|
|
131
|
+
# # 'external_references' => [{ 'system' => 'hubspot', 'id' => '98765' }],
|
|
132
|
+
# # 'created_at' => '2026-10-23T12:00:00Z', 'updated_at' => '2026-10-23T12:00:00Z' }
|
|
133
|
+
# @example Link a signed amendment and preserve nulls
|
|
134
|
+
# client.contracts.update(
|
|
135
|
+
# 'amendment-id', parent_document_id: 'document-id', relation_type: 'amendment',
|
|
136
|
+
# value: 130000, counterparty_name: nil
|
|
137
|
+
# )
|
|
138
|
+
# # Request body includes "counterparty_name": null.
|
|
139
|
+
def update(document_id, payload, account_id_override = nil)
|
|
140
|
+
body = contract_payload(payload, UPDATE_FIELDS, 'Contract payload')
|
|
141
|
+
raise ValidationError.new('Contract payload must change at least one field') if body.empty?
|
|
142
|
+
|
|
143
|
+
validate_contract_input!(body)
|
|
144
|
+
|
|
145
|
+
call('Failed to update contract') do
|
|
146
|
+
http_put(contract_path(document_id, account_id_override), body)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Fetch the root contract and every related amendment, renewal, and
|
|
151
|
+
# termination document. Returns `nil` when the document has no family.
|
|
152
|
+
#
|
|
153
|
+
# @param document_id [String]
|
|
154
|
+
# @param account_id_override [String, nil]
|
|
155
|
+
# @return [Hash, nil] root DocumentContract with `amendments`
|
|
156
|
+
# @raise [Assinafy::ApiError] on an unsuccessful response other than 404
|
|
157
|
+
# @raise [Assinafy::NetworkError] on transport or TLS failure
|
|
158
|
+
# @raise [Assinafy::ValidationError] on an invalid ID
|
|
159
|
+
# @see GET /accounts/{accountId}/documents/{documentId}/contract/related
|
|
160
|
+
# @example Read a family from one of its amendments
|
|
161
|
+
# client.contracts.related('amendment-id')
|
|
162
|
+
# # Request: GET /accounts/{accountId}/documents/amendment-id/contract/related
|
|
163
|
+
# # => { 'resource' => 'document_contract', 'document_id' => 'document-id', 'status' => 'active',
|
|
164
|
+
# # 'counterparty_name' => 'Example Ltda', 'counterparty_taxpayer_id' => '11222333000181',
|
|
165
|
+
# # 'value' => 130000, 'currency' => 'BRL', 'start_date' => '2026-11-01',
|
|
166
|
+
# # 'end_date' => '2028-06-30', 'auto_renew' => true, 'renewal_term_months' => 12,
|
|
167
|
+
# # 'notice_period_days' => 30, 'term' => 2, 'owner_id' => 'user-id',
|
|
168
|
+
# # 'metadata' => nil, 'external_references' => [],
|
|
169
|
+
# # 'amendments' => [{ 'document_id' => 'amendment-id', 'relation_type' => 'amendment',
|
|
170
|
+
# # 'number' => 1, 'status' => 'applied', 'terms' => { 'value' => 130000 } }],
|
|
171
|
+
# # 'created_at' => '2026-10-23T12:00:00Z', 'updated_at' => '2027-05-12T18:20:00Z' }
|
|
172
|
+
def related(document_id, account_id_override = nil)
|
|
173
|
+
call_optional('Failed to fetch related contracts') do
|
|
174
|
+
http_get("#{contract_path(document_id, account_id_override)}/related")
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Start the contract's next term. Send the current `term` for an
|
|
179
|
+
# idempotent term-based renewal, or an explicit later `end_date`.
|
|
180
|
+
# Explicit `value: nil` clears the contract value.
|
|
181
|
+
#
|
|
182
|
+
# @param document_id [String]
|
|
183
|
+
# @param payload [Hash] `term` or `end_date`, plus optional `value` and `updated_at`
|
|
184
|
+
# @param account_id_override [String, nil]
|
|
185
|
+
# @return [Hash] the renewed DocumentContract
|
|
186
|
+
# @raise [Assinafy::ApiError] including 403 without the plan and 409 on a stale `updated_at`
|
|
187
|
+
# @raise [Assinafy::NetworkError] on transport or TLS failure
|
|
188
|
+
# @raise [Assinafy::ValidationError] on invalid IDs or payload fields
|
|
189
|
+
# @see POST /accounts/{accountId}/documents/{documentId}/contract/renew
|
|
190
|
+
# @example Renew from the current term
|
|
191
|
+
# client.contracts.renew('document-id', term: 1, value: 126000)
|
|
192
|
+
# # Request body: { "term": 1, "value": 126000 }
|
|
193
|
+
# # => { 'resource' => 'document_contract', 'document_id' => 'document-id', 'status' => 'active',
|
|
194
|
+
# # 'counterparty_name' => 'Example Ltda', 'counterparty_taxpayer_id' => '11222333000181',
|
|
195
|
+
# # 'value' => 126000, 'currency' => 'BRL', 'start_date' => '2026-11-01',
|
|
196
|
+
# # 'end_date' => '2028-10-31', 'auto_renew' => true, 'renewal_term_months' => 12,
|
|
197
|
+
# # 'notice_period_days' => 30, 'term' => 2, 'owner_id' => 'user-id',
|
|
198
|
+
# # 'metadata' => nil, 'external_references' => [],
|
|
199
|
+
# # 'created_at' => '2026-10-23T12:00:00Z', 'updated_at' => '2027-10-15T09:30:00Z' }
|
|
200
|
+
def renew(document_id, payload, account_id_override = nil)
|
|
201
|
+
body = contract_payload(payload, RENEW_FIELDS, 'Contract renewal payload')
|
|
202
|
+
validate_renewal!(body)
|
|
203
|
+
|
|
204
|
+
call('Failed to renew contract') do
|
|
205
|
+
http_post("#{contract_path(document_id, account_id_override)}/renew", body)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Terminate a contract and stop lifecycle alerts. The optional reason is
|
|
210
|
+
# recorded in the document activity event.
|
|
211
|
+
#
|
|
212
|
+
# @param document_id [String]
|
|
213
|
+
# @param payload [Hash] optional `reason` and optimistic-lock `updated_at`
|
|
214
|
+
# @param account_id_override [String, nil]
|
|
215
|
+
# @return [Hash] the terminated DocumentContract
|
|
216
|
+
# @raise [Assinafy::ApiError] including 403 without the plan and 409 on a stale `updated_at`
|
|
217
|
+
# @raise [Assinafy::NetworkError] on transport or TLS failure
|
|
218
|
+
# @raise [Assinafy::ValidationError] on invalid IDs or payload fields
|
|
219
|
+
# @see POST /accounts/{accountId}/documents/{documentId}/contract/terminate
|
|
220
|
+
# @example Terminate with a reason
|
|
221
|
+
# client.contracts.terminate('document-id', reason: 'Supplier replaced')
|
|
222
|
+
# # Request body: { "reason": "Supplier replaced" }
|
|
223
|
+
# # => { 'resource' => 'document_contract', 'document_id' => 'document-id', 'status' => 'terminated',
|
|
224
|
+
# # 'counterparty_name' => 'Example Ltda', 'counterparty_taxpayer_id' => '11222333000181',
|
|
225
|
+
# # 'value' => 120000.50, 'currency' => 'BRL', 'start_date' => '2026-11-01',
|
|
226
|
+
# # 'end_date' => '2027-10-31', 'auto_renew' => true, 'renewal_term_months' => 12,
|
|
227
|
+
# # 'notice_period_days' => 30, 'term' => 1, 'owner_id' => 'user-id',
|
|
228
|
+
# # 'metadata' => nil, 'external_references' => [],
|
|
229
|
+
# # 'created_at' => '2026-10-23T12:00:00Z', 'updated_at' => '2027-03-02T14:00:00Z' }
|
|
230
|
+
def terminate(document_id, payload = {}, account_id_override = nil)
|
|
231
|
+
body = contract_payload(payload, TERMINATE_FIELDS, 'Contract termination payload')
|
|
232
|
+
validate_optional_string!(body, 'reason', 'Termination reason', max_length: 500)
|
|
233
|
+
validate_optional_string!(body, 'updated_at', 'updated_at')
|
|
234
|
+
|
|
235
|
+
call('Failed to terminate contract') do
|
|
236
|
+
http_post("#{contract_path(document_id, account_id_override)}/terminate", body)
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
private
|
|
241
|
+
|
|
242
|
+
def contract_path(document_id, account_id_override)
|
|
243
|
+
doc_id = require_id(document_id, 'Document ID')
|
|
244
|
+
"accounts/#{account_id(account_id_override)}/documents/#{doc_id}/contract"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def contract_list_params(params)
|
|
248
|
+
query = query_params(require_payload(params, 'Contract filters'))
|
|
249
|
+
reject_unknown_fields!(query, LIST_FIELDS, 'contract filters')
|
|
250
|
+
validate_status_filter!(query)
|
|
251
|
+
%w[counterparty search external_system external_id].each do |field|
|
|
252
|
+
validate_optional_string!(query, field, field)
|
|
253
|
+
end
|
|
254
|
+
%w[end_date_from end_date_to].each { |field| validate_optional_date!(query, field) }
|
|
255
|
+
if query['end_date_from'] && query['end_date_to'] && query['end_date_from'] > query['end_date_to']
|
|
256
|
+
raise ValidationError.new('end_date_from must not be after end_date_to')
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
if query.key?('ending_within_days')
|
|
260
|
+
validate_integer!(query['ending_within_days'], 'ending_within_days', 0, 3650)
|
|
261
|
+
end
|
|
262
|
+
validate_integer!(query['page'], 'page', 1) if query.key?('page')
|
|
263
|
+
validate_integer!(query['per-page'], 'per_page', 1, 100) if query.key?('per-page')
|
|
264
|
+
validate_enum!(query, 'sort', SORTS)
|
|
265
|
+
validate_enum!(query, 'expand', %w[document])
|
|
266
|
+
query
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def validate_status_filter!(query)
|
|
270
|
+
return unless query.key?('status')
|
|
271
|
+
|
|
272
|
+
statuses = query['status'].is_a?(Array) ? query['status'] : query['status'].to_s.split(',')
|
|
273
|
+
unless !statuses.empty? && statuses.all? { |status| status.is_a?(String) && STATUSES.include?(status) }
|
|
274
|
+
raise ValidationError.new("status must contain only: #{STATUSES.join(', ')}")
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
query['status'] = statuses.join(',')
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def contract_payload(payload, allowed, name)
|
|
281
|
+
raw = require_payload(payload, name)
|
|
282
|
+
body = body_params(raw)
|
|
283
|
+
raw.each { |key, value| body[key.to_s] = nil if value.nil? }
|
|
284
|
+
reject_unknown_fields!(body, allowed, name.downcase)
|
|
285
|
+
body
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def validate_contract_input!(body)
|
|
289
|
+
validate_enum!(body, 'status', STATUSES)
|
|
290
|
+
validate_enum!(body, 'relation_type', RELATION_TYPES)
|
|
291
|
+
validate_contract_strings!(body)
|
|
292
|
+
validate_contract_dates!(body)
|
|
293
|
+
validate_contract_numbers!(body)
|
|
294
|
+
validate_contract_collections!(body)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def validate_contract_strings!(body)
|
|
298
|
+
validate_optional_string!(body, 'counterparty_name', 'counterparty_name', max_length: 255)
|
|
299
|
+
validate_optional_string!(body, 'counterparty_taxpayer_id', 'counterparty_taxpayer_id')
|
|
300
|
+
validate_optional_string!(body, 'owner_id', 'owner_id')
|
|
301
|
+
validate_optional_timestamp!(body, 'updated_at')
|
|
302
|
+
validate_parent_document_id!(body)
|
|
303
|
+
validate_currency!(body)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def validate_contract_dates!(body)
|
|
307
|
+
%w[start_date end_date].each { |field| validate_optional_date!(body, field, minimum_year: 1000) }
|
|
308
|
+
if body['start_date'] && body['end_date'] && body['start_date'] > body['end_date']
|
|
309
|
+
raise ValidationError.new('end_date must not be before start_date')
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def validate_contract_numbers!(body)
|
|
314
|
+
if body.key?('value') && !body['value'].nil?
|
|
315
|
+
validate_number!(body['value'], 'value', 0, 9_999_999_999_999.99)
|
|
316
|
+
end
|
|
317
|
+
validate_boolean!(body, 'auto_renew')
|
|
318
|
+
validate_integer!(body['renewal_term_months'], 'renewal_term_months', 1, 600) if body['renewal_term_months']
|
|
319
|
+
validate_integer!(body['notice_period_days'], 'notice_period_days', 0, 3650) if body['notice_period_days']
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def validate_contract_collections!(body)
|
|
323
|
+
validate_metadata!(body['metadata']) if body.key?('metadata') && !body['metadata'].nil?
|
|
324
|
+
if body.key?('external_references') && !body['external_references'].nil?
|
|
325
|
+
validate_external_references!(body['external_references'])
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def validate_renewal!(body)
|
|
330
|
+
unless body['term'] || body['end_date']
|
|
331
|
+
raise ValidationError.new('Contract renewal requires term or end_date')
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
validate_optional_date!(body, 'end_date')
|
|
335
|
+
validate_integer!(body['term'], 'term', 1) if body.key?('term')
|
|
336
|
+
validate_number!(body['value'], 'value', 0, 9_999_999_999_999.99) if body.key?('value') && !body['value'].nil?
|
|
337
|
+
validate_optional_timestamp!(body, 'updated_at')
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def validate_parent_document_id!(body)
|
|
341
|
+
return unless body.key?('parent_document_id') && !body['parent_document_id'].nil?
|
|
342
|
+
|
|
343
|
+
require_id(body['parent_document_id'], 'parent_document_id')
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def validate_currency!(body)
|
|
347
|
+
return unless body.key?('currency') && !body['currency'].nil?
|
|
348
|
+
|
|
349
|
+
currency = require_string(body['currency'], 'currency')
|
|
350
|
+
raise ValidationError.new('currency must be a three-letter code') unless currency.match?(/\A[A-Za-z]{3}\z/)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def validate_optional_date!(body, field, minimum_year: nil)
|
|
354
|
+
return unless body.key?(field) && !body[field].nil?
|
|
355
|
+
|
|
356
|
+
value = require_string(body[field], field)
|
|
357
|
+
date = Date.iso8601(value)
|
|
358
|
+
raise ArgumentError unless value.match?(/\A\d{4}-\d{2}-\d{2}\z/) && (!minimum_year || date.year >= minimum_year)
|
|
359
|
+
rescue ArgumentError
|
|
360
|
+
raise ValidationError.new("#{field} must be YYYY-MM-DD")
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def validate_optional_string!(body, field, name, max_length: nil)
|
|
364
|
+
return unless body.key?(field) && !body[field].nil?
|
|
365
|
+
|
|
366
|
+
require_string(body[field], name, max_length: max_length)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def validate_enum!(body, field, allowed)
|
|
370
|
+
return unless body.key?(field)
|
|
371
|
+
|
|
372
|
+
value = require_string(body[field], field)
|
|
373
|
+
return if allowed.include?(value)
|
|
374
|
+
|
|
375
|
+
raise ValidationError.new("#{field} must be one of: #{allowed.join(', ')}")
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def validate_boolean!(body, field)
|
|
379
|
+
require_boolean(body[field], field) if body.key?(field) && !body[field].nil?
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def validate_optional_timestamp!(body, field)
|
|
383
|
+
return unless body.key?(field) && !body[field].nil?
|
|
384
|
+
|
|
385
|
+
value = require_string(body[field], field)
|
|
386
|
+
DateTime.iso8601(value)
|
|
387
|
+
return if value.match?(/\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})\z/)
|
|
388
|
+
|
|
389
|
+
raise ArgumentError
|
|
390
|
+
rescue ArgumentError
|
|
391
|
+
raise ValidationError.new("#{field} must be an ISO 8601 timestamp with a timezone")
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def validate_integer!(value, name, minimum, maximum = nil)
|
|
395
|
+
valid = value.is_a?(Integer) && value >= minimum && (maximum.nil? || value <= maximum)
|
|
396
|
+
return if valid
|
|
397
|
+
|
|
398
|
+
range = maximum ? "between #{minimum} and #{maximum}" : "at least #{minimum}"
|
|
399
|
+
raise ValidationError.new("#{name} must be an Integer #{range}")
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def validate_number!(value, name, minimum, maximum = nil)
|
|
403
|
+
number = value.is_a?(Integer) || value.is_a?(Float)
|
|
404
|
+
finite = number && (!value.respond_to?(:finite?) || value.finite?)
|
|
405
|
+
valid = finite && value.round(2) == value && value >= minimum && (maximum.nil? || value <= maximum)
|
|
406
|
+
return if valid
|
|
407
|
+
|
|
408
|
+
raise ValidationError.new("#{name} must be a finite number in the documented range")
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def validate_metadata!(metadata)
|
|
412
|
+
unless metadata.is_a?(Hash) && metadata.length <= 50
|
|
413
|
+
raise ValidationError.new('metadata must be a Hash with at most 50 entries')
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
metadata.each do |key, value|
|
|
417
|
+
valid_key = key.to_s.match?(/\A[A-Za-z0-9_.-]{1,40}\z/)
|
|
418
|
+
valid_value = value.is_a?(String) && value.length <= 500
|
|
419
|
+
next if valid_key && valid_value
|
|
420
|
+
|
|
421
|
+
raise ValidationError.new('metadata keys or values are invalid')
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def validate_external_references!(references)
|
|
426
|
+
unless references.is_a?(Array) && references.length <= 20
|
|
427
|
+
raise ValidationError.new('external_references must be an Array with at most 20 entries')
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
references.each do |reference|
|
|
431
|
+
unless reference.is_a?(Hash)
|
|
432
|
+
raise ValidationError.new('Each external reference must be a Hash')
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
ref = reference.transform_keys(&:to_s)
|
|
436
|
+
system = require_string(ref['system'], 'External reference system', max_length: 64)
|
|
437
|
+
require_string(ref['id'], 'External reference ID', max_length: 255)
|
|
438
|
+
unless system.match?(/\A[A-Za-z0-9_.-]+\z/)
|
|
439
|
+
raise ValidationError.new('External reference system contains invalid characters')
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def reject_unknown_fields!(body, allowed, name)
|
|
445
|
+
unknown = body.keys - allowed
|
|
446
|
+
raise ValidationError.new("Unknown #{name}: #{unknown.join(', ')}") unless unknown.empty?
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
end
|
|
@@ -40,6 +40,8 @@ module Assinafy
|
|
|
40
40
|
# },
|
|
41
41
|
# 'is_closed' => false,
|
|
42
42
|
# 'signing_url' => 'https://app-sandbox.assinafy.com.br/sign/document-id',
|
|
43
|
+
# 'verification_code' => nil,
|
|
44
|
+
# 'verification_url' => nil,
|
|
43
45
|
# 'decline_reason' => nil,
|
|
44
46
|
# 'declined_by' => nil,
|
|
45
47
|
# 'tags' => [],
|
|
@@ -233,6 +235,7 @@ module Assinafy
|
|
|
233
235
|
# 'declined_by' => nil,
|
|
234
236
|
# 'tags' => [],
|
|
235
237
|
# 'assignment' => nil,
|
|
238
|
+
# 'has_contract' => false,
|
|
236
239
|
# 'pages' => [
|
|
237
240
|
# { 'id' => 'page-id', 'number' => 1, 'height' => 1651,
|
|
238
241
|
# 'width' => 1275, 'download_url' => 'https://sandbox.assinafy.com.br/v1/documents/.../download' }
|
|
@@ -16,6 +16,10 @@ module Assinafy
|
|
|
16
16
|
DocumentProcessingFailed
|
|
17
17
|
TemplateProcessingFailed
|
|
18
18
|
SignerWhatsappFailed
|
|
19
|
+
ContractRenewalNotice
|
|
20
|
+
ContractExpiring
|
|
21
|
+
ContractExpired
|
|
22
|
+
ContractRenewed
|
|
19
23
|
].freeze
|
|
20
24
|
|
|
21
25
|
# Fetch the authenticated user's profile. The current OpenAPI response is
|
|
@@ -117,11 +121,11 @@ module Assinafy
|
|
|
117
121
|
end
|
|
118
122
|
end
|
|
119
123
|
|
|
120
|
-
# Fetch all owner-facing document email preferences. All
|
|
124
|
+
# Fetch all owner-facing document and contract email preferences. All thirteen keys are
|
|
121
125
|
# returned and default to true. Account and security email is not
|
|
122
126
|
# configurable through this endpoint.
|
|
123
127
|
#
|
|
124
|
-
# @return [Hash{String=>Boolean}] all
|
|
128
|
+
# @return [Hash{String=>Boolean}] all thirteen documented preference codes
|
|
125
129
|
# @raise [Assinafy::ApiError] on an unsuccessful API response
|
|
126
130
|
# @raise [Assinafy::NetworkError] on transport or TLS failure
|
|
127
131
|
# @raise [Assinafy::ValidationError] on invalid required input
|
|
@@ -139,7 +143,11 @@ module Assinafy
|
|
|
139
143
|
# 'DocumentExpirationReset' => true,
|
|
140
144
|
# 'DocumentProcessingFailed' => true,
|
|
141
145
|
# 'TemplateProcessingFailed' => true,
|
|
142
|
-
# 'SignerWhatsappFailed' => true
|
|
146
|
+
# 'SignerWhatsappFailed' => true,
|
|
147
|
+
# 'ContractRenewalNotice' => true,
|
|
148
|
+
# 'ContractExpiring' => true,
|
|
149
|
+
# 'ContractExpired' => true,
|
|
150
|
+
# 'ContractRenewed' => true
|
|
143
151
|
# }
|
|
144
152
|
def notification_preferences
|
|
145
153
|
call('Failed to fetch notification preferences') do
|
|
@@ -147,10 +155,10 @@ module Assinafy
|
|
|
147
155
|
end
|
|
148
156
|
end
|
|
149
157
|
|
|
150
|
-
# Merge selected owner-facing document email preferences.
|
|
158
|
+
# Merge selected owner-facing document and contract email preferences.
|
|
151
159
|
#
|
|
152
160
|
# Omitted keys keep their current values. The API returns the full
|
|
153
|
-
#
|
|
161
|
+
# thirteen-key map shown by {#notification_preferences}.
|
|
154
162
|
#
|
|
155
163
|
# @param preferences [Hash{String,Symbol=>Boolean}] non-empty partial map
|
|
156
164
|
# @return [Hash{String=>Boolean}] the full updated map
|
|
@@ -174,7 +182,11 @@ module Assinafy
|
|
|
174
182
|
# 'DocumentExpirationReset' => true,
|
|
175
183
|
# 'DocumentProcessingFailed' => true,
|
|
176
184
|
# 'TemplateProcessingFailed' => true,
|
|
177
|
-
# 'SignerWhatsappFailed' => true
|
|
185
|
+
# 'SignerWhatsappFailed' => true,
|
|
186
|
+
# 'ContractRenewalNotice' => true,
|
|
187
|
+
# 'ContractExpiring' => true,
|
|
188
|
+
# 'ContractExpired' => true,
|
|
189
|
+
# 'ContractRenewed' => true
|
|
178
190
|
# }
|
|
179
191
|
def update_notification_preferences(preferences)
|
|
180
192
|
preferences = require_payload(preferences, 'Notification preferences')
|
|
@@ -28,7 +28,8 @@ module Assinafy
|
|
|
28
28
|
# @option payload [Array<String>] :events event-type IDs (see {#list_event_types})
|
|
29
29
|
# @option payload [Boolean] :is_active default `true` when omitted
|
|
30
30
|
# @param account_id_override [String, nil]
|
|
31
|
-
# @return [Hash] the subscription object: { events:, is_active:, url:, email:,
|
|
31
|
+
# @return [Hash] the subscription object: { events:, is_active:, is_held:, held_since:, url:, email:,
|
|
32
|
+
# updated_at: }
|
|
32
33
|
# @raise [Assinafy::ApiError] on an unsuccessful API response
|
|
33
34
|
# @raise [Assinafy::NetworkError] on transport or TLS failure
|
|
34
35
|
# @raise [Assinafy::ValidationError] on invalid required input
|
|
@@ -51,6 +52,8 @@ module Assinafy
|
|
|
51
52
|
# # {
|
|
52
53
|
# # 'events' => ["document_ready", "document_prepared"],
|
|
53
54
|
# # 'is_active' => true,
|
|
55
|
+
# # 'is_held' => false,
|
|
56
|
+
# # 'held_since' => nil,
|
|
54
57
|
# # 'url' => "https://example.com/webhook",
|
|
55
58
|
# # 'email' => "ops@example.com",
|
|
56
59
|
# # 'updated_at' => "2026-06-05T21:13:24Z"
|
|
@@ -84,6 +87,8 @@ module Assinafy
|
|
|
84
87
|
# # {
|
|
85
88
|
# # 'events' => ["document_ready", "signer_signed_document"],
|
|
86
89
|
# # 'is_active' => false,
|
|
90
|
+
# # 'is_held' => false,
|
|
91
|
+
# # 'held_since' => nil,
|
|
87
92
|
# # 'url' => "https://example.com/sdk-smoke-webhook",
|
|
88
93
|
# # 'email' => "webhook@example.com",
|
|
89
94
|
# # 'updated_at' => "2026-06-05T21:13:24Z"
|
|
@@ -113,6 +118,8 @@ module Assinafy
|
|
|
113
118
|
# # {
|
|
114
119
|
# # 'events' => ["document_ready", "document_prepared"],
|
|
115
120
|
# # 'is_active' => false,
|
|
121
|
+
# # 'is_held' => false,
|
|
122
|
+
# # 'held_since' => nil,
|
|
116
123
|
# # 'url' => "https://example.com/webhook",
|
|
117
124
|
# # 'email' => "ops@example.com",
|
|
118
125
|
# # 'updated_at' => "2026-06-05T21:13:24Z"
|
|
@@ -178,6 +185,10 @@ module Assinafy
|
|
|
178
185
|
# # endpoint: "https://example.com/webhook",
|
|
179
186
|
# # payload: { 'id' => 15431, event: "signature_requested", object: {}, subject: {}, payload: {} },
|
|
180
187
|
# # delivered: false,
|
|
188
|
+
# # webhook_id: "webhook-id-placeholder",
|
|
189
|
+
# # attempt: 2,
|
|
190
|
+
# # kind: "scheduled",
|
|
191
|
+
# # will_retry: false,
|
|
181
192
|
# # http_status: 404,
|
|
182
193
|
# # response_body: "{\"success\":false,...}",
|
|
183
194
|
# # error: "Client error: `POST https://example.com/webhook` resulted in a 404 ...",
|
|
@@ -217,6 +228,10 @@ module Assinafy
|
|
|
217
228
|
# # endpoint: "https://example.com/webhook",
|
|
218
229
|
# # payload: { 'id' => 15431, event: "signature_requested", object: {}, subject: {} },
|
|
219
230
|
# # delivered: true,
|
|
231
|
+
# # webhook_id: "webhook-id-placeholder",
|
|
232
|
+
# # attempt: 1,
|
|
233
|
+
# # kind: "manual",
|
|
234
|
+
# # will_retry: false,
|
|
220
235
|
# # http_status: 200,
|
|
221
236
|
# # response_body: "OK",
|
|
222
237
|
# # error: nil,
|
|
@@ -247,12 +262,15 @@ module Assinafy
|
|
|
247
262
|
# # => unwrapped data payload returned:
|
|
248
263
|
# # [
|
|
249
264
|
# # {
|
|
265
|
+
# # 'resource' => "activity_event_subscription",
|
|
250
266
|
# # 'id' => "webhook-endpoint-id",
|
|
251
267
|
# # 'name' => "ERP",
|
|
252
268
|
# # 'url' => "https://example.com/webhooks/assinafy",
|
|
253
269
|
# # 'email' => "ops@example.com",
|
|
254
270
|
# # 'events' => ["document_ready", "signer_signed_document"],
|
|
255
271
|
# # 'is_active' => true,
|
|
272
|
+
# # 'is_held' => false,
|
|
273
|
+
# # 'held_since' => nil,
|
|
256
274
|
# # 'signing_enabled' => true,
|
|
257
275
|
# # 'created_at' => "2026-10-01T12:00:00Z",
|
|
258
276
|
# # 'updated_at' => "2026-10-01T12:00:00Z"
|
|
@@ -274,7 +292,7 @@ module Assinafy
|
|
|
274
292
|
# especially for OAuth apps, which cannot call {#endpoint_secret}.
|
|
275
293
|
#
|
|
276
294
|
# @param payload [Hash]
|
|
277
|
-
# @option payload [String] :url
|
|
295
|
+
# @option payload [String] :url HTTPS URL that receives the events (required)
|
|
278
296
|
# @option payload [String] :email contact email for delivery-failure notices (required)
|
|
279
297
|
# @option payload [Array<String>] :events event-type IDs (see {#list_event_types}) (required)
|
|
280
298
|
# @option payload [String] :name label to tell endpoints apart
|
|
@@ -304,8 +322,10 @@ module Assinafy
|
|
|
304
322
|
# # "signing_enabled": true
|
|
305
323
|
# # }
|
|
306
324
|
# # => unwrapped data payload returned (same shape as {#list_endpoints} entries):
|
|
307
|
-
# # { '
|
|
308
|
-
# # '
|
|
325
|
+
# # { 'resource' => "activity_event_subscription", 'id' => "webhook-endpoint-id", 'name' => "ERP",
|
|
326
|
+
# # 'url' => "https://example.com/webhooks/assinafy", 'email' => "ops@example.com",
|
|
327
|
+
# # 'events' => [...], 'is_active' => true, 'is_held' => false, 'held_since' => nil,
|
|
328
|
+
# # 'signing_enabled' => true,
|
|
309
329
|
# # 'created_at' => "2026-10-01T12:00:00Z", 'updated_at' => "2026-10-01T12:00:00Z",
|
|
310
330
|
# # 'secret' => 'whsec_secret-placeholder' } # secret appears only on creation with signing enabled
|
|
311
331
|
def create_endpoint(payload, account_id_override = nil)
|
|
@@ -481,7 +501,7 @@ module Assinafy
|
|
|
481
501
|
end
|
|
482
502
|
|
|
483
503
|
def validate_webhook_fields!(body)
|
|
484
|
-
|
|
504
|
+
validate_webhook_url!(body[:url]) if body.key?(:url)
|
|
485
505
|
Utils.require_email(body[:email]) if body.key?(:email)
|
|
486
506
|
require_string(body[:name], 'Webhook name') if body.key?(:name)
|
|
487
507
|
%i[is_active signing_enabled].each { |key| require_boolean(body[key], key.to_s) if body.key?(key) }
|
|
@@ -492,6 +512,16 @@ module Assinafy
|
|
|
492
512
|
|
|
493
513
|
raise ValidationError.new('Webhook events must be non-empty Strings')
|
|
494
514
|
end
|
|
515
|
+
|
|
516
|
+
def validate_webhook_url!(value)
|
|
517
|
+
url = require_string(value, 'Webhook URL')
|
|
518
|
+
uri = URI.parse(url)
|
|
519
|
+
return if uri.is_a?(URI::HTTPS) && uri.host && !uri.host.empty?
|
|
520
|
+
|
|
521
|
+
raise ValidationError.new('Webhook URL must be an absolute HTTPS URL')
|
|
522
|
+
rescue URI::InvalidURIError
|
|
523
|
+
raise ValidationError.new('Webhook URL must be an absolute HTTPS URL')
|
|
524
|
+
end
|
|
495
525
|
end
|
|
496
526
|
end
|
|
497
527
|
end
|
data/lib/assinafy/version.rb
CHANGED
data/lib/assinafy.rb
CHANGED
|
@@ -17,6 +17,7 @@ require_relative 'assinafy/resources/oauth_resource'
|
|
|
17
17
|
require_relative 'assinafy/resources/account_resource'
|
|
18
18
|
require_relative 'assinafy/resources/user_resource'
|
|
19
19
|
require_relative 'assinafy/resources/document_resource'
|
|
20
|
+
require_relative 'assinafy/resources/contract_resource'
|
|
20
21
|
require_relative 'assinafy/resources/signer_resource'
|
|
21
22
|
require_relative 'assinafy/resources/signer_document_resource'
|
|
22
23
|
require_relative 'assinafy/resources/assignment_resource'
|
data/sig/assinafy.rbs
CHANGED
|
@@ -283,6 +283,23 @@ module Assinafy
|
|
|
283
283
|
def signing_progress: (String document_id) -> signing_progress
|
|
284
284
|
end
|
|
285
285
|
|
|
286
|
+
class ContractResource < BaseResource
|
|
287
|
+
STATUSES: Array[String]
|
|
288
|
+
RELATION_TYPES: Array[String]
|
|
289
|
+
SORTS: Array[String]
|
|
290
|
+
LIST_FIELDS: Array[String]
|
|
291
|
+
UPDATE_FIELDS: Array[String]
|
|
292
|
+
RENEW_FIELDS: Array[String]
|
|
293
|
+
TERMINATE_FIELDS: Array[String]
|
|
294
|
+
|
|
295
|
+
def list: (?params params, ?String? account_id_override) -> list_result
|
|
296
|
+
def get: (String document_id, ?String? account_id_override) -> api_object?
|
|
297
|
+
def update: (String document_id, params payload, ?String? account_id_override) -> api_object
|
|
298
|
+
def related: (String document_id, ?String? account_id_override) -> api_object?
|
|
299
|
+
def renew: (String document_id, params payload, ?String? account_id_override) -> api_object
|
|
300
|
+
def terminate: (String document_id, ?params payload, ?String? account_id_override) -> api_object
|
|
301
|
+
end
|
|
302
|
+
|
|
286
303
|
class SignerResource < BaseResource
|
|
287
304
|
EMAIL_REGEX: Regexp
|
|
288
305
|
SIGNATURE_TYPES: Array[String]
|
|
@@ -445,6 +462,7 @@ module Assinafy
|
|
|
445
462
|
attr_reader accounts: Resources::AccountResource
|
|
446
463
|
attr_reader users: Resources::UserResource
|
|
447
464
|
attr_reader documents: Resources::DocumentResource
|
|
465
|
+
attr_reader contracts: Resources::ContractResource
|
|
448
466
|
attr_reader signers: Resources::SignerResource
|
|
449
467
|
attr_reader signer_documents: Resources::SignerDocumentResource
|
|
450
468
|
attr_reader assignments: Resources::AssignmentResource
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: assinafy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Assinafy SDK Contributors
|
|
@@ -134,8 +134,8 @@ dependencies:
|
|
|
134
134
|
- !ruby/object:Gem::Version
|
|
135
135
|
version: '3.0'
|
|
136
136
|
description: Ruby SDK for Assinafy. Covers the documented authentication, OAuth 2.1,
|
|
137
|
-
account, user, document, signer, assignment, webhook, template, tag, and
|
|
138
|
-
plus the high-level upload_and_request_signatures helper.
|
|
137
|
+
account, user, document, contract, signer, assignment, webhook, template, tag, and
|
|
138
|
+
field APIs, plus the high-level upload_and_request_signatures helper.
|
|
139
139
|
email:
|
|
140
140
|
- sdk@assinafy.com.br
|
|
141
141
|
executables: []
|
|
@@ -157,6 +157,7 @@ files:
|
|
|
157
157
|
- lib/assinafy/resources/assignment_resource.rb
|
|
158
158
|
- lib/assinafy/resources/auth_resource.rb
|
|
159
159
|
- lib/assinafy/resources/base_resource.rb
|
|
160
|
+
- lib/assinafy/resources/contract_resource.rb
|
|
160
161
|
- lib/assinafy/resources/document_resource.rb
|
|
161
162
|
- lib/assinafy/resources/field_resource.rb
|
|
162
163
|
- lib/assinafy/resources/oauth_resource.rb
|