Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
10 changes: 6 additions & 4 deletions docs/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ Consulte também [`SDK_CONTRACT.md`](SDK_CONTRACT.md) (mapeamento HTTP → exce
`BulkRequirement.create` pode responder **HTTP 200** com slots com erro em `atomic:results`. O SDK **não** lança exceção nesse caso.

```python
response = client.notarial.bulk_requirements.create(
from clicksign.resources.notarial.bulk_requirement import BulkRequirement

response = BulkRequirement.create(
envelope_id,
block=lambda ops: ops.add_agree(
signer_id=signer.id,
Expand Down Expand Up @@ -110,11 +112,11 @@ Receita completa: [`examples/03-webhooks.md`](examples/03-webhooks.md).

```python
# correto
client.notarial.documents.create(envelope.id, filename="a.pdf", content_base64="...")
client.notarial.signers.create(envelope.id, name="...", email="...")
Document.create(envelope.id, filename="a.pdf", content_base64="...")
Signer.create(envelope.id, name="...", email="...")

# incorreto — envelope_id vira atributo do JSON por engano
client.notarial.documents.create(envelope_id=envelope.id, filename="a.pdf")
Document.create(envelope_id=envelope.id, filename="a.pdf")
```

---
Expand Down
111 changes: 22 additions & 89 deletions docs/WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,8 @@ Guia do ciclo de vida de um envelope na API 3.0 (Envelope): criação, documento

## Configuração inicial

Recomendado para código novo: `ClicksignClient` (dependências explícitas). Alternativa legada: `configure()` + classes de resource.

```python
import os

from clicksign import ClicksignClient

client = ClicksignClient(
api_key=os.environ["CLICKSIGN_API_KEY"],
environment="sandbox", # ou "production"
# base_url="https://..." # opcional: sobrescreve environment
)
```

Equivalente global:

```python
import clicksign
from clicksign.resources.notarial.envelope import Envelope
from clicksign.resources.notarial.document import Document
Expand All @@ -41,13 +26,11 @@ clicksign.configure(api_key=os.environ["CLICKSIGN_API_KEY"], environment="sandbo
O envelope é o contêiner do processo. Começa em `draft`.

```python
envelope = client.notarial.envelopes.create(
envelope = Envelope.create(
name="Contrato de prestação de serviços",
locale="pt-BR",
auto_close=True,
remind_interval=3,
default_subject="Documentos aguardando sua assinatura",
default_message="Por favor, revise e assine os documentos em anexo.",
)

print(envelope.id) # uuid do envelope
Expand All @@ -69,7 +52,7 @@ import base64
pdf_bytes = open("contrato.pdf", "rb").read()
pdf_base64 = "data:application/pdf;base64," + base64.b64encode(pdf_bytes).decode()

document = client.notarial.documents.create(
document = Document.create(
envelope.id,
filename="contrato.pdf",
content_base64=pdf_base64,
Expand All @@ -79,18 +62,6 @@ print(document.id)
print(document.status) # draft
```

Via classe (mesmo contrato):

```python
from clicksign.resources.notarial.document import Document

document = Document.create(
envelope.id,
filename="contrato.pdf",
content_base64=pdf_base64,
)
```

**Outros modos de criação** (mutuamente exclusivos na API — consulte [`SPEC.md`](SPEC.md)):

```python
Expand All @@ -106,7 +77,7 @@ Document.create(envelope.id, filename="contrato.docx", template_key="uuid-do-tem
## 3. Adicionar o signatário

```python
signer = client.notarial.signers.create(
signer = Signer.create(
envelope.id,
name="Maria Silva",
email="maria.silva@example.com",
Expand All @@ -126,23 +97,13 @@ print(signer.envelope_id)

Os requisitos definem **o que** o signatário deve fazer em cada documento.

Relacionamentos padrão documento + signatário:

```python
rels = {
"document": {"data": {"type": "documents", "id": document.id}},
"signer": {"data": {"type": "signers", "id": signer.id}},
}
```

### 4.1 Concordância (`agree`)

```python
from clicksign.resources.notarial.requirement import Requirement

agree = Requirement.create(
envelope.id,
relationships=rels,
signer_id=signer.id,
document_id=document.id,
action="agree",
role="sign",
)
Expand All @@ -155,7 +116,8 @@ print(agree.id, agree.action)
```python
evidence = Requirement.create(
envelope.id,
relationships=rels,
signer_id=signer.id,
document_id=document.id,
action="provide_evidence",
auth="email",
)
Expand All @@ -179,8 +141,6 @@ print(envelope.status) # running
Alternativa explícita (POST `/activate`):

```python
from clicksign.resources.notarial.envelope import Envelope

envelope = Envelope.activate(envelope.id)
```

Expand Down Expand Up @@ -215,51 +175,29 @@ signer.notify(
```python
import base64
import os

from clicksign import ClicksignClient
import clicksign
from clicksign.resources.notarial.envelope import Envelope
from clicksign.resources.notarial.document import Document
from clicksign.resources.notarial.signer import Signer
from clicksign.resources.notarial.requirement import Requirement

client = ClicksignClient(
api_key=os.environ["CLICKSIGN_API_KEY"],
environment="sandbox",
)
clicksign.configure(api_key=os.environ["CLICKSIGN_API_KEY"], environment="sandbox")

# 1. Envelope
envelope = client.notarial.envelopes.create(
name="Contrato ACME",
locale="pt-BR",
auto_close=True,
)
envelope = Envelope.create(name="Contrato ACME", locale="pt-BR", auto_close=True)

# 2. Documento
pdf_b64 = "data:application/pdf;base64," + base64.b64encode(
open("contrato.pdf", "rb").read()
).decode()
document = client.notarial.documents.create(
envelope.id,
filename="contrato.pdf",
content_base64=pdf_b64,
)
document = Document.create(envelope.id, filename="contrato.pdf", content_base64=pdf_b64)

# 3. Signatário
signer = client.notarial.signers.create(
envelope.id,
name="Maria Silva",
email="maria@example.com",
)
signer = Signer.create(envelope.id, name="Maria Silva", email="maria@example.com")

# 4. Requisitos
rels = {
"document": {"data": {"type": "documents", "id": document.id}},
"signer": {"data": {"type": "signers", "id": signer.id}},
}
Requirement.create(envelope.id, relationships=rels, action="agree", role="sign")
Requirement.create(
envelope.id,
relationships=rels,
action="provide_evidence",
auth="email",
)
Requirement.create(envelope.id, signer_id=signer.id, document_id=document.id, action="agree", role="sign")
Requirement.create(envelope.id, signer_id=signer.id, document_id=document.id, action="provide_evidence", auth="email")

# 5. Ativar
envelope.update(status="running")
Expand All @@ -277,15 +215,13 @@ print(f"Envelope {envelope.id} ativo e signatário notificado.")
Uma única requisição HTTP para vários requisitos (`BulkRequirement`):

```python
response = client.notarial.bulk_requirements.create(
from clicksign.resources.notarial.bulk_requirement import BulkRequirement

response = BulkRequirement.create(
envelope.id,
block=lambda ops: (
ops.add_agree(signer_id=signer.id, document_id=document.id, role="sign"),
ops.add_provide_evidence(
signer_id=signer.id,
document_id=document.id,
auth="email",
),
ops.add_provide_evidence(signer_id=signer.id, document_id=document.id, auth="email"),
),
)

Expand All @@ -305,9 +241,6 @@ Detalhes: [examples/02-bulk-requirements.md](examples/02-bulk-requirements.md).
### Eventos e estado (API)

```python
from clicksign.resources.notarial.document import Document
from clicksign.resources.notarial.envelope import Envelope

for event in Envelope.list_events(envelope.id):
print(event.name, event.created)

Expand All @@ -317,7 +250,7 @@ for event in Document.list_events(document.id, envelope_id=envelope.id):
for s in Envelope.list_signers(envelope.id):
print(s.name, s.email)

envelope = client.notarial.envelopes.retrieve(envelope.id)
envelope = Envelope.retrieve(envelope.id)
print(envelope.status) # running, closed, cancelled, ...
```

Expand Down
21 changes: 2 additions & 19 deletions docs/examples/02-bulk-requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@ Cria ou remove vários requisitos de assinatura em **uma** requisição (`POST /

```python
import os

from clicksign import ClicksignClient
import clicksign
from clicksign.resources.notarial.bulk_requirement import BulkRequirement

client = ClicksignClient(
api_key=os.environ["CLICKSIGN_API_KEY"],
environment="sandbox",
)
clicksign.configure(api_key=os.environ["CLICKSIGN_API_KEY"], environment="sandbox")

# envelope, document e signer já criados (status draft)
response = BulkRequirement.create(
Expand All @@ -36,19 +32,6 @@ response = BulkRequirement.create(
)
```

Via facade (mesmo contrato):

```python
response = client.notarial.bulk_requirements.create(
envelope.id,
block=lambda ops: ops.add_agree(
signer_id=signer.id,
document_id=document.id,
role="sign",
),
)
```

---

## Tratar sucesso parcial (`atomic:results`)
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ build-backend = "hatchling.build"
name = "clicksign"
dynamic = ["version"]
description = "Official Python SDK for the Clicksign e-signature API (v3)"
license = "MIT"
requires-python = ">=3.10"
dependencies = []

Expand Down
9 changes: 8 additions & 1 deletion src/clicksign/resources/notarial/requirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,21 @@ def filter(cls, **kwargs: Unpack[RequirementFilterParams]) -> QueryProxy[Require
def create( # type: ignore[override]
cls,
envelope_id: str,
signer_id: str | None = None,
document_id: str | None = None,
relationships: dict[str, Any] | None = None,
**attrs: Unpack[RequirementCreateParams],
) -> Requirement:
from ...json_api.serializer import serialize_create

rels: dict[str, Any] = dict(relationships or {})
if signer_id:
rels["signer"] = {"data": {"type": "signers", "id": signer_id}}
if document_id:
rels["document"] = {"data": {"type": "documents", "id": document_id}}
client = cls._get_client()
path = f"/envelopes/{envelope_id}/requirements"
body = serialize_create(cls._get_resource_type(), dict(attrs), relationships or None)
body = serialize_create(cls._get_resource_type(), dict(attrs), rels or None)
response = client.post(path, body)
instances, _ = cls._parse_response(response)
inst = instances[0]
Expand Down
Loading