assinafy 2.0.0 → 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 +23 -0
- data/README.en.md +33 -8
- data/README.md +39 -3
- data/docs/API_REFERENCE.md +155 -13
- data/lib/assinafy/client.rb +4 -0
- data/lib/assinafy/resources/account_resource.rb +1 -0
- data/lib/assinafy/resources/assignment_resource.rb +22 -11
- 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 +19 -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,29 @@
|
|
|
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
|
+
|
|
20
|
+
## [2.0.1] - 2026-10-19
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Assignment creation and cost estimation reject `DigitalCertificate` signers unless the assignment method is
|
|
25
|
+
`virtual`, before an unsignable collect assignment can invite the signer or consume credits. The local
|
|
26
|
+
`ValidationError` exposes the stable code `digital_certificate_requires_virtual` in `errors[:code]`.
|
|
27
|
+
|
|
5
28
|
## [2.0.0] - 2026-10-16
|
|
6
29
|
|
|
7
30
|
### Added
|
data/README.en.md
CHANGED
|
@@ -394,9 +394,11 @@ the missing side is inferred from the other, and when both are omitted both defa
|
|
|
394
394
|
- The certificate charge appears in the cost breakdown under the code `SignatureDigitalCertificate`. Resending a
|
|
395
395
|
notification charges the notification again.
|
|
396
396
|
- `DigitalCertificate` needs the Digital Certificate account feature (Standard and Pro plans), a CPF or CNPJ in the
|
|
397
|
-
signer's `government_id`, and the signer alone in its signing step. A
|
|
398
|
-
|
|
399
|
-
|
|
397
|
+
signer's `government_id`, a `method: 'virtual'` assignment, and the signer alone in its signing step. A collect
|
|
398
|
+
assignment, including one inferred from a template with positioned signer fields, cannot use certificate
|
|
399
|
+
signing. Direct assignment creation and estimation reject it with `ValidationError` code
|
|
400
|
+
`digital_certificate_requires_virtual`. A CPF requires that person's certificate (an e-CPF, or an e-CNPJ naming
|
|
401
|
+
them as legal representative); a CNPJ requires an e-CNPJ for that company, from any of its representatives.
|
|
400
402
|
- `AssignmentResource::VERIFICATION_METHODS` and `::NOTIFICATION_METHODS` publish the enums. `build_payload`
|
|
401
403
|
validates values, pairings, and `step` locally, so a typo fails before the request is sent.
|
|
402
404
|
|
|
@@ -433,10 +435,11 @@ assignment = client.assignments.create(
|
|
|
433
435
|
assignment['signing_urls'] # => [{ 'signer_id' => 'signer-id', 'url' => 'https://.../sign/...' }, ...]
|
|
434
436
|
```
|
|
435
437
|
|
|
436
|
-
`virtual` has no positioned fields
|
|
437
|
-
[Assignments](#assignments)). Both require a non-empty `signers` array. Steps are
|
|
438
|
-
contiguous from 1; equal steps sign in parallel, and each step is notified when the
|
|
439
|
-
certificate signer is alone in its step. Deadlines are zoned ISO 8601 and at least one
|
|
438
|
+
`virtual` has no positioned fields and is required for `DigitalCertificate`; `collect` adds `entries` of
|
|
439
|
+
positioned fields (see [Assignments](#assignments)). Both require a non-empty `signers` array. Steps are
|
|
440
|
+
all-or-none, positive, and contiguous from 1; equal steps sign in parallel, and each step is notified when the
|
|
441
|
+
previous one finishes. A certificate signer is alone in its step. Deadlines are zoned ISO 8601 and at least one
|
|
442
|
+
hour ahead.
|
|
440
443
|
|
|
441
444
|
For a template, bind one distinct existing signer to each role and create the document and assignment together:
|
|
442
445
|
|
|
@@ -688,7 +691,7 @@ See [Errors](#errors) for the full hierarchy.
|
|
|
688
691
|
|
|
689
692
|
## Resource reference
|
|
690
693
|
|
|
691
|
-
`Assinafy::Client` exposes
|
|
694
|
+
`Assinafy::Client` exposes fourteen accessors — thirteen API resources plus the local `webhook_verifier` helper:
|
|
692
695
|
|
|
693
696
|
| Accessor | What it covers |
|
|
694
697
|
| --------------------------- | ------------------------------------------------------------------ |
|
|
@@ -697,6 +700,7 @@ See [Errors](#errors) for the full hierarchy.
|
|
|
697
700
|
| `client.accounts` | Account CRUD, theme, KPI stats, brand logo |
|
|
698
701
|
| `client.users` | User profile, notification preferences, cross-account KPIs |
|
|
699
702
|
| `client.documents` | Upload, list, search, rename, download, delete, verify, tags |
|
|
703
|
+
| `client.contracts` | Contract terms, amendments, renewals, terminations |
|
|
700
704
|
| `client.signers` | Workspace signer CRUD + signer self-service endpoints |
|
|
701
705
|
| `client.signer_documents` | Signer-authenticated multi-document operations + search |
|
|
702
706
|
| `client.assignments` | List/create/sign/decline/resend/estimate assignments |
|
|
@@ -778,6 +782,27 @@ client.documents.signing_progress('document-id') # => { signed: 1, total: 2, p
|
|
|
778
782
|
An Array `tags` filter is sent as comma-separated tag IDs. The tag arrays of `replace_tags`/`append_tags` take tag
|
|
779
783
|
IDs.
|
|
780
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
|
+
|
|
781
806
|
### Signers (workspace CRUD)
|
|
782
807
|
|
|
783
808
|
```ruby
|
data/README.md
CHANGED
|
@@ -531,9 +531,12 @@ dois, ambos assumem `Email`.
|
|
|
531
531
|
### Certificado digital ICP-Brasil (A1/A3)
|
|
532
532
|
|
|
533
533
|
Exige o recurso **Certificado Digital** na conta (planos Standard e Pro), CPF ou CNPJ em
|
|
534
|
-
`government_id` do signatário, e que cada signatário por certificado esteja
|
|
535
|
-
passo**. Um
|
|
536
|
-
|
|
534
|
+
`government_id` do signatário, assignment com `method: 'virtual'` e que cada signatário por certificado esteja
|
|
535
|
+
**sozinho no seu passo**. Um assignment `collect`, inclusive quando inferido de um template com campos de
|
|
536
|
+
signatário posicionados, não aceita certificado digital. A criação e a estimativa diretas levantam
|
|
537
|
+
`ValidationError` com o código estável `digital_certificate_requires_virtual`. Um CPF exige o certificado daquela
|
|
538
|
+
pessoa (e-CPF, ou e-CNPJ que a nomeie como representante legal); um CNPJ exige um e-CNPJ da empresa, de qualquer
|
|
539
|
+
um de seus representantes.
|
|
537
540
|
|
|
538
541
|
---
|
|
539
542
|
|
|
@@ -963,6 +966,38 @@ verificacao['agreement_code'] # código impresso no certificado do documento
|
|
|
963
966
|
Ela devolve o resultado da Assinafy; não valida independentemente a assinatura do PDF nem a
|
|
964
967
|
cadeia de certificação.
|
|
965
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
|
+
|
|
966
1001
|
---
|
|
967
1002
|
|
|
968
1003
|
## 12. Organizar e limpar
|
|
@@ -1069,6 +1104,7 @@ end
|
|
|
1069
1104
|
| `client.accounts` | workspaces, tema, KPIs, logo |
|
|
1070
1105
|
| `client.users` | perfil próprio, KPIs, preferências de notificação |
|
|
1071
1106
|
| `client.documents` | upload, listagem, download, ciclo de vida, tags, verificação |
|
|
1107
|
+
| `client.contracts` | termos, aditivos, renovações e encerramentos de contratos |
|
|
1072
1108
|
| `client.signers` | CRUD de signatários e todo o autoatendimento do signatário |
|
|
1073
1109
|
| `client.signer_documents` | documentos do signatário, assinar/recusar em lote |
|
|
1074
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`
|
|
@@ -308,9 +329,15 @@ on top of its notification, shown in cost estimates under the breakdown code `Si
|
|
|
308
329
|
|
|
309
330
|
`DigitalCertificate` has the signer sign with their own ICP-Brasil certificate (A1 or A3) through the Web PKI
|
|
310
331
|
browser extension, producing a qualified PAdES signature. It requires the Digital Certificate account feature, a
|
|
311
|
-
CPF or CNPJ in the signer's `government_id` (accepted by both `SignerResource#create` and `#update`),
|
|
312
|
-
signer is alone in its signing step.
|
|
313
|
-
|
|
332
|
+
CPF or CNPJ in the signer's `government_id` (accepted by both `SignerResource#create` and `#update`), a
|
|
333
|
+
`method: 'virtual'` assignment, and that the signer is alone in its signing step. `AssignmentResource#create` and
|
|
334
|
+
`#estimate_cost` reject any other assignment method with `ValidationError`; its stable
|
|
335
|
+
`errors[:code]` is `digital_certificate_requires_virtual`. A CPF requires that person's certificate (an e-CPF, or
|
|
336
|
+
an e-CNPJ naming them as legal representative); a CNPJ requires an e-CNPJ for that company, from any of its
|
|
337
|
+
representatives.
|
|
338
|
+
|
|
339
|
+
A template with positioned signer fields creates a collect assignment, so it cannot be used with a
|
|
340
|
+
`DigitalCertificate` signer. Use a template without positioned signer fields for certificate signing.
|
|
314
341
|
|
|
315
342
|
Certificate signers complete the signature in Assinafy's hosted signing flow, reached through the assignment's
|
|
316
343
|
`signing_urls`. The SDK does not wrap the two-step Web PKI handshake (`/signers/certificate/start`,
|
|
@@ -351,7 +378,9 @@ if lost, delete and recreate the endpoint. `#list_endpoints(url: registered_url)
|
|
|
351
378
|
`#delete_endpoint_by_url(registered_url)` deletes that endpoint, returning `nil`. `WebhookResource#create_endpoint`
|
|
352
379
|
and `#update_endpoint` accept only `url`, `email`, `events`, `name`, `is_active`, and `signing_enabled`.
|
|
353
380
|
`#register`, `#get`, and `#inactivate` (`/webhooks/subscriptions`, `/webhooks/inactivate`) act on the account's
|
|
354
|
-
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.
|
|
355
384
|
|
|
356
385
|
Each delivery is a JSON `POST` carrying `webhook-id` (stable across attempts of one event to one endpoint; use it to
|
|
357
386
|
deduplicate), `webhook-timestamp` (Unix seconds), and, when signing is enabled, `webhook-signature`. Any `2xx` is
|
|
@@ -379,7 +408,7 @@ return-value, and usage details.
|
|
|
379
408
|
| `Assinafy::Client.from_config`, `.from_hash` | Construct a client from string- or symbol-keyed configuration. | [`client.rb`](../lib/assinafy/client.rb) |
|
|
380
409
|
| `Client#faraday_connection` | Return the configured Faraday connection for advanced integration. | [`client.rb`](../lib/assinafy/client.rb) |
|
|
381
410
|
| `Client#upload_and_request_signatures` | Upload, optionally wait, create signers, and create a virtual assignment. | [`client.rb`](../lib/assinafy/client.rb) |
|
|
382
|
-
| `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) |
|
|
383
412
|
| `Assinafy::Configuration.new`, `.from_hash` | Build configuration directly or from string/symbol keys. | [`configuration.rb`](../lib/assinafy/configuration.rb) |
|
|
384
413
|
| `Configuration#auth_headers` | Return the selected API-key, bearer, or empty authentication header set. | [`configuration.rb`](../lib/assinafy/configuration.rb) |
|
|
385
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) |
|
|
@@ -433,6 +462,8 @@ A workspace account (organization).
|
|
|
433
462
|
| `notification_sender_type` | string | No | No | enum: `["User", "Account"]` |
|
|
434
463
|
| `roles` | Array<string> | No | No | — |
|
|
435
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. |
|
|
436
467
|
| `created_at` | string (date-time) | No | No | — |
|
|
437
468
|
|
|
438
469
|
### AccountTheme
|
|
@@ -460,7 +491,9 @@ A request for signers to sign a document.
|
|
|
460
491
|
| --- | --- | --- | --- | --- |
|
|
461
492
|
| `resource` | string | No | No | — |
|
|
462
493
|
| `id` | string | No | No | — |
|
|
463
|
-
| `
|
|
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. |
|
|
464
497
|
| `method` | string | No | No | enum: `["virtual", "collect"]` |
|
|
465
498
|
| `expires_at` | string (date-time) | No | Yes | — |
|
|
466
499
|
| `message` | string | No | Yes | — |
|
|
@@ -602,12 +635,14 @@ A document and its current lifecycle state.
|
|
|
602
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. |
|
|
603
636
|
| `is_closed` | boolean | No | No | — |
|
|
604
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. |
|
|
605
640
|
| `decline_reason` | string | No | Yes | — |
|
|
606
641
|
| `declined_by` | any JSON value | No | Yes | — |
|
|
607
642
|
| `tags` | Array<object> | No | No | — |
|
|
608
643
|
| `tags[].id` | string | No | No | — |
|
|
609
644
|
| `tags[].name` | string | No | No | — |
|
|
610
|
-
| `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`. |
|
|
611
646
|
| `assignment.resource` | string | No | No | — |
|
|
612
647
|
| `assignment.id` | string | No | No | — |
|
|
613
648
|
| `assignment.sender_email` | string (email) | No | No | — |
|
|
@@ -620,6 +655,7 @@ A document and its current lifecycle state.
|
|
|
620
655
|
| `assignment.summary` | [`AssignmentSummary`](#assignmentsummary) | No | No | — |
|
|
621
656
|
| `assignment.signing_urls` | Array<[`SigningUrl`](#signingurl)> | No | No | — |
|
|
622
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. |
|
|
623
659
|
| `created_at` | string (date-time) | No | No | — |
|
|
624
660
|
| `updated_at` | string (date-time) | No | No | — |
|
|
625
661
|
|
|
@@ -638,6 +674,85 @@ A document activity event.
|
|
|
638
674
|
| `origin.user-agent` | string | No | No | — |
|
|
639
675
|
| `created_at` | string (date-time) | No | No | — |
|
|
640
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
|
+
|
|
641
756
|
### DocumentPage
|
|
642
757
|
|
|
643
758
|
| Property | Type | Required | Nullable | Constraints / description |
|
|
@@ -789,6 +904,10 @@ Owner-facing document notifications, keyed by notification type. `true` means th
|
|
|
789
904
|
| `DocumentProcessingFailed` | boolean | No | No | An uploaded document could not be processed. |
|
|
790
905
|
| `TemplateProcessingFailed` | boolean | No | No | A template could not be processed. |
|
|
791
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. |
|
|
792
911
|
|
|
793
912
|
### OAuthRevokeRequest
|
|
794
913
|
|
|
@@ -819,6 +938,20 @@ Body of `POST /v1/oauth/token`. Sent form-encoded per RFC 6749, or as JSON.
|
|
|
819
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. |
|
|
820
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. |
|
|
821
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
|
+
|
|
822
955
|
### Signer
|
|
823
956
|
|
|
824
957
|
A signing party belonging to a workspace account.
|
|
@@ -981,6 +1114,10 @@ A single webhook delivery-history entry.
|
|
|
981
1114
|
| `endpoint` | string | No | Yes | URL that received the request. |
|
|
982
1115
|
| `payload` | object | No | Yes | JSON payload sent to the endpoint. |
|
|
983
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. |
|
|
984
1121
|
| `http_status` | integer | No | Yes | HTTP status returned (null if connection failed). |
|
|
985
1122
|
| `response_body` | string | No | Yes | Endpoint response body, truncated to 2000 chars. |
|
|
986
1123
|
| `error` | string | No | Yes | Delivery error message, if any. |
|
|
@@ -993,12 +1130,15 @@ A URL that receives the account's webhook events. Every active endpoint subscrib
|
|
|
993
1130
|
|
|
994
1131
|
| Property | Type | Required | Nullable | Constraints / description |
|
|
995
1132
|
| --- | --- | --- | --- | --- |
|
|
1133
|
+
| `resource` | string | No | No | Always `activity_event_subscription` in single-resource responses. |
|
|
996
1134
|
| `id` | string | No | No | Endpoint ID. |
|
|
997
1135
|
| `name` | string | No | Yes | Label to tell endpoints apart. |
|
|
998
|
-
| `url` | string (uri) | No | No | URL that receives the events
|
|
1136
|
+
| `url` | string (uri) | No | No | HTTPS URL that receives the events. |
|
|
999
1137
|
| `email` | string (email) | No | No | Contact email for delivery-failure notices. |
|
|
1000
1138
|
| `events` | Array<string> | No | No | Event types delivered to this endpoint. |
|
|
1001
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. |
|
|
1002
1142
|
| `signing_enabled` | boolean | No | No | Whether deliveries carry a `webhook-signature` header. |
|
|
1003
1143
|
| `created_at` | string (date-time) | No | No | — |
|
|
1004
1144
|
| `updated_at` | string (date-time) | No | No | — |
|
|
@@ -1048,6 +1188,8 @@ An account's webhook subscription configuration: the account's oldest webhook en
|
|
|
1048
1188
|
| --- | --- | --- | --- | --- |
|
|
1049
1189
|
| `events` | Array<string> | No | No | Event types subscribed for delivery. |
|
|
1050
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. |
|
|
1051
1193
|
| `url` | string | No | Yes | Webhook endpoint URL. |
|
|
1052
1194
|
| `email` | string | No | Yes | Contact email for delivery notices. |
|
|
1053
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",
|
|
@@ -11,6 +11,7 @@ module Assinafy
|
|
|
11
11
|
class AssignmentResource < BaseResource
|
|
12
12
|
OPTIONAL_FIELDS = %i[message expires_at copy_receivers].freeze
|
|
13
13
|
METHODS = %w[virtual collect].freeze
|
|
14
|
+
DIGITAL_CERTIFICATE_REQUIRES_VIRTUAL = 'digital_certificate_requires_virtual'
|
|
14
15
|
|
|
15
16
|
# How a signer's identity is verified before they may sign, set per signer
|
|
16
17
|
# on the assignment. When omitted it is inferred from
|
|
@@ -24,7 +25,7 @@ module Assinafy
|
|
|
24
25
|
# certificate (A1 or A3), producing a qualified PAdES signature.
|
|
25
26
|
# Requires the Digital Certificate feature, a CPF or CNPJ in the
|
|
26
27
|
# signer's `government_id`, and that the signer is alone in its signing
|
|
27
|
-
# step. Charged 0.5 credits per signer on top of its notification
|
|
28
|
+
# step on a virtual assignment. Charged 0.5 credits per signer on top of its notification
|
|
28
29
|
# (breakdown code `SignatureDigitalCertificate`). A CPF requires that person's
|
|
29
30
|
# certificate (an e-CPF, or an e-CNPJ naming them as legal
|
|
30
31
|
# representative); a CNPJ requires an e-CNPJ for that company, from any
|
|
@@ -124,6 +125,7 @@ module Assinafy
|
|
|
124
125
|
|
|
125
126
|
result = { method: method }
|
|
126
127
|
normalised = signers.map { |ref| normalise_signer_ref(ref, options) }
|
|
128
|
+
validate_certificate_method!(method, normalised)
|
|
127
129
|
validate_signing_steps!(normalised)
|
|
128
130
|
result[:signers] = normalised unless normalised.empty?
|
|
129
131
|
OPTIONAL_FIELDS.each { |key| result[key] = p[key] if p[key] }
|
|
@@ -254,6 +256,16 @@ module Assinafy
|
|
|
254
256
|
raise ValidationError.new('notification_methods must match the verification_method')
|
|
255
257
|
end
|
|
256
258
|
|
|
259
|
+
def validate_certificate_method!(method, signers)
|
|
260
|
+
return if method == 'virtual'
|
|
261
|
+
return unless signers.any? { |signer| signer[:verification_method] == 'DigitalCertificate' }
|
|
262
|
+
|
|
263
|
+
raise ValidationError.new(
|
|
264
|
+
'DigitalCertificate signers require method: virtual',
|
|
265
|
+
{ code: DIGITAL_CERTIFICATE_REQUIRES_VIRTUAL, method: method }
|
|
266
|
+
)
|
|
267
|
+
end
|
|
268
|
+
|
|
257
269
|
def validate_signing_steps!(signers)
|
|
258
270
|
steps = signers.map { |signer| signer[:step] }
|
|
259
271
|
contiguous = steps.compact.uniq.sort.each_with_index.all? { |step, index| step == index + 1 }
|
|
@@ -291,6 +303,8 @@ module Assinafy
|
|
|
291
303
|
# data: [
|
|
292
304
|
# {
|
|
293
305
|
# 'id' => 'assignment-id',
|
|
306
|
+
# 'document_id' => 'document-id',
|
|
307
|
+
# 'created_at' => '2026-06-05T21:21:19Z',
|
|
294
308
|
# 'sender_email' => 'sender@example.com',
|
|
295
309
|
# 'method' => 'virtual',
|
|
296
310
|
# 'expires_at' => nil,
|
|
@@ -330,6 +344,7 @@ module Assinafy
|
|
|
330
344
|
# # "message" => "Please sign" }
|
|
331
345
|
# # => {
|
|
332
346
|
# # "resource" => "assignment", "id" => "assignment-id",
|
|
347
|
+
# # "document_id" => "document-id", "created_at" => "2026-06-05T21:21:19Z",
|
|
333
348
|
# # "sender_email" => "sender@example.com", "method" => "virtual",
|
|
334
349
|
# # "expires_at" => nil, "message" => "Please sign",
|
|
335
350
|
# # "signers" => [{ "id" => "signer-id", "full_name" => "Example Signer",
|
|
@@ -463,9 +478,7 @@ module Assinafy
|
|
|
463
478
|
# @param document_id [String]
|
|
464
479
|
# @param assignment_id [String]
|
|
465
480
|
# @param signer_id [String]
|
|
466
|
-
# @return [Hash] cost
|
|
467
|
-
# total_credits, breakdown[], document_balance, credit_balance, has_sufficient_resources,
|
|
468
|
-
# blocking_reason, message)
|
|
481
|
+
# @return [Hash] resend cost (`total`, `breakdown`, `credit_balance`, `has_sufficient_credits`)
|
|
469
482
|
# @raise [Assinafy::ApiError] on an unsuccessful API response
|
|
470
483
|
# @raise [Assinafy::NetworkError] on transport or TLS failure
|
|
471
484
|
# @raise [Assinafy::ValidationError] on invalid required input
|
|
@@ -474,13 +487,11 @@ module Assinafy
|
|
|
474
487
|
# resource.estimate_resend_cost("document-id", "assignment-id", "signer-id")
|
|
475
488
|
# # (no request body)
|
|
476
489
|
# # => {
|
|
477
|
-
# # "
|
|
478
|
-
# # "
|
|
479
|
-
# #
|
|
480
|
-
# #
|
|
481
|
-
# #
|
|
482
|
-
# # "document_balance" => 10, "credit_balance" => 100,
|
|
483
|
-
# # "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
|
|
484
495
|
# # }
|
|
485
496
|
def estimate_resend_cost(document_id, assignment_id, signer_id)
|
|
486
497
|
doc_id = require_id(document_id, 'Document ID')
|