Metadata-Version: 2.5
Name: easydocforms
Version: 0.1.0
Summary: Python SDK for the EasyDocForms Partner API — turn blank PDF intake forms into hosted fillable forms and get back completed, pixel-exact PDFs.
Project-URL: Homepage, https://easydocforms.com
Project-URL: Documentation, https://easydocforms.com/docs/api
Project-URL: Source, https://github.com/easydocforms/easydocforms-python
Project-URL: Changelog, https://github.com/easydocforms/easydocforms-python/blob/main/CHANGELOG.md
Author-email: EasyDocForms <support@easydocforms.com>
License-Expression: MIT
License-File: LICENSE
Keywords: api,easydocforms,forms,healthcare,intake,pdf,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Healthcare Industry
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Office/Business
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# easydocforms

[![PyPI version](https://badge.fury.io/py/easydocforms.svg)](https://pypi.org/project/easydocforms/)
[![CI](https://github.com/easydocforms/easydocforms-python/actions/workflows/ci.yml/badge.svg)](https://github.com/easydocforms/easydocforms-python/actions/workflows/ci.yml)

The official Python SDK for the [EasyDocForms](https://easydocforms.com) Partner API.

EasyDocForms turns a blank PDF intake form into a hosted, mobile-friendly fillable form — and returns the completed, pixel-exact PDF plus structured JSON answers. The API wraps the same document-understanding pipeline EasyDocForms runs in production for healthcare intake: import a blank PDF, wait for the template, mint a hosted fill link, hand it to a patient, then retrieve the results.

**Zero dependencies** — standard library only. Fully typed (PEP 561).

- API reference: <https://easydocforms.com/docs/api>

## Install

```sh
pip install easydocforms
```

## Quickstart

API keys are created in the EasyDocForms app under **Settings → Integrations → Partner API** (shown exactly once).

```python
import os
import easydocforms

client = easydocforms.Client(os.environ["EASYDOCFORMS_API_KEY"])
pong = client.ping()
print(f"org {pong['org_id']}, key {pong['key_name']}, scopes {pong['scopes']}")
```

## The full loop

```python
# 1. Import a blank PDF (async — returns immediately).
import_job = client.create_import(
    pdf_url="https://example.com/new-patient-intake.pdf",
    filename="new-patient-intake.pdf",
    blank_form_attestation=True,  # you attest the PDF is a blank template — no PHI
)

# 2. Wait for processing (typically 1–10 minutes). Imports never fail for
# quality reasons: the template is always created, and review_required tells
# your staff what to double-check in the EasyDocForms editor.
import_job = client.wait_for_import(import_job["import_id"])
if import_job["status"] == "failed":
    raise RuntimeError(import_job["error"])

# 3. Mint a hosted fill link and hand it to the patient. No EasyDocForms
# account needed on their side.
link = client.create_fill_link(
    template_id=import_job["template_id"],
    external_ref="visit-8675309",  # your correlation id — must not contain PHI
)
print("send the patient to:", link["url"])

# 4. When the patient submits (see webhooks below), fetch the results.
submission = client.get_submission(submission_id)
submission["answers"]  # {field_id: value, ...}

with open("completed.pdf", "wb") as f:
    f.write(client.download_submission_pdf(submission_id))

# Or get a ~10-minute signed URL that needs no Authorization header — safe to
# hand to a browser or EMR without embedding your API key.
try:
    pdf_link = client.get_submission_pdf_link(submission_id)
except easydocforms.PDFPendingError:
    pass  # frozen artifact not ready yet; use download_submission_pdf instead
```

## Webhooks

Register a delivery URL, store the one-time `whsec_*` secret, and verify every delivery's `X-EDF-Signature` header against the **raw** request body:

```python
result = client.create_webhook(url="https://your-app.example.com/webhooks/easydocforms")
result["secret"]  # shown only once — store it now
```

```python
event = easydocforms.construct_event(
    payload=request.body,  # raw bytes, before any parsing
    header=request.headers[easydocforms.SIGNATURE_HEADER],
    secret=os.environ["EASYDOCFORMS_WEBHOOK_SECRET"],
)

if event["event"] == "submission.created":
    # PHI-minimized: no answers in the payload. Fetch them with your API key
    # via event["data"]["submission_id"].
    ...
```

Verification recomputes an HMAC-SHA256 over the raw body, compares in constant time, and rejects timestamps more than 5 minutes from now (configurable via `tolerance=`). A failed check raises `easydocforms.SignatureVerificationError` — respond 400 and move on.

## Error handling

API failures raise typed subclasses of `easydocforms.APIError`, each carrying `status_code`, the server's message, and a machine-readable `code` on authorization failures:

| Error | When |
|---|---|
| `AuthenticationError` | 401 — missing, invalid, or revoked key |
| `PermissionDeniedError` | 403 — `code` is `SCOPE_REQUIRED` or `PARTNER_API_NOT_ENABLED` |
| `NotFoundError` | 404 — no such resource in your organization |
| `PDFPendingError` | 409 on `get_submission_pdf_link` — fall back to `download_submission_pdf` |
| `RateLimitError` | 429 — back off and retry |

The SDK does not retry automatically.

## PHI boundary

- **Imports are blank forms only.** Every import requires `blank_form_attestation=True`, asserting the PDF contains no patient-identifiable information.
- **`external_ref` must never contain PHI.** It is an opaque correlation id echoed on submissions and webhook events.
- **Webhook payloads are PHI-minimized by design** — ids and retrieve URLs, never patient answers. Answers are only available over the authenticated API.

## License

MIT
