easydocforms

Gem Version CI

The official Ruby SDK for the EasyDocForms 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 runtime dependencies — standard library only (net/http, json, openssl).

Install

# Gemfile
gem "easydocforms"

Quickstart

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

require "easydocforms"

client = EasyDocForms::Client.new(ENV["EASYDOCFORMS_API_KEY"])
pong = client.ping
puts "org #{pong[:org_id]}, key #{pong[:key_name]}, scopes #{pong[:scopes].join(", ")}"

The full loop

# 1. Import a blank PDF (async — returns immediately).
import = 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 = client.wait_for_import(import[:import_id])
raise import[:error] if import[:status] == "failed"

# 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[:template_id],
  external_ref: "visit-8675309" # your correlation id — must not contain PHI
)
puts "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, ... }

File.binwrite("completed.pdf", 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.
begin
  pdf_link = client.get_submission_pdf_link(submission_id)
rescue EasyDocForms::PDFPendingError
  # The frozen artifact isn't ready yet; use download_submission_pdf instead.
end

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:

result = client.create_webhook(url: "https://your-app.example.com/webhooks/easydocforms")
result[:secret] # shown only once — store it now
event = EasyDocForms::Webhook.construct_event(
  payload: request.body.read,
  header:  request.headers[EasyDocForms::Webhook::HEADER],
  secret:  ENV["EASYDOCFORMS_WEBHOOK_SECRET"]
)

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

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.

Rails

rails generate easydocforms:webhook

creates a verified receiver controller at app/controllers/easydocforms_webhooks_controller.rb and prints the route + credentials setup.

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
PermissionError 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