Project

anvil-ruby

0.0
There's a lot of open issues
Official Ruby client for the Anvil API. Anvil is a suite of tools for managing document workflows including PDF filling, PDF generation from HTML/Markdown, e-signatures, and webhooks. Built with zero runtime dependencies and designed to be Rails-friendly while remaining framework agnostic.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

>= 1.17
>= 10.0
~> 3.12
~> 1.50
~> 0.22
~> 6.1
~> 3.18
~> 0.9

Runtime

>= 0
 Project Readme

Anvil Ruby

A Ruby gem for the Anvil API - the fastest way to build document workflows.

CI Gem Version Ruby Style Guide Ruby Version

Anvil is a suite of tools for managing document workflows:

  • 📝 PDF Filling - Fill PDF templates with JSON data
  • 📄 PDF Generation - Generate PDFs from HTML/CSS or Markdown
  • ✍️ E-signatures - Collect legally binding e-signatures
  • 🔄 Webhooks - Real-time notifications for document events

Installation

Add this line to your application's Gemfile:

gem 'anvil-ruby'

And then execute:

$ bundle install

Or install it yourself:

$ gem install anvil-ruby

Feature Status

Current coverage: ~30% of Anvil's API. See API_COVERAGE.md for detailed implementation status.

✅ Implemented

  • PDF Operations - Fill templates, generate from HTML/Markdown
  • E-Signatures (Basic) - Create packets, get signing URLs, track status
  • Webhooks - Parse payloads, verify authenticity
  • Core Infrastructure - Rate limiting, error handling, flexible configuration

🚧 Roadmap

Phase 1: Core Features (v0.2.0)

  • Generic GraphQL support for custom queries
  • Complete e-signature features (update, send, void packets)
  • Basic workflow support (create, start workflows)
  • Basic webform support (create forms, handle submissions)

Phase 2: Advanced Features (v0.3.0)

  • Full workflow implementation with data management
  • Full webform/Forge implementation
  • Cast (PDF template) management
  • Webhook management API

Phase 3: AI & Enterprise (v0.4.0)

  • Document AI/OCR capabilities
  • Organization management
  • Embedded builders
  • Advanced utilities

See our GitHub Projects for detailed progress tracking.

Quick Start

Configuration

Configure your API key (get one at Anvil Settings):

Rails (config/initializers/anvil.rb)

Anvil.configure do |config|
  config.api_key = Rails.application.credentials.anvil[:api_key]
  config.environment = Rails.env.production? ? :production : :development
end

Environment Variable

export ANVIL_API_KEY="your_api_key_here"

Direct Assignment

require 'anvil'
Anvil.api_key = "your_api_key_here"

Usage

PDF Filling

Fill PDF templates with your data:

# Fill a PDF template
pdf = Anvil::PDF.fill(
  template_id: "your_template_id",
  data: {
    name: "John Doe",
    email: "john@example.com",
    date: Date.today.strftime("%B %d, %Y")
  }
)

# Save the filled PDF
pdf.save_as("contract.pdf")

# Get as base64 (for database storage)
base64_pdf = pdf.to_base64

PDF Generation

Generate from HTML/CSS

pdf = Anvil::PDF.generate_from_html(
  html: "<h1>Invoice #123</h1><p>Amount: $100</p>",
  css: "h1 { color: blue; }",
  title: "Invoice"
)

pdf.save_as("invoice.pdf")

Generate from Markdown

pdf = Anvil::PDF.generate_from_markdown(
  <<~MD
    # Report

    ## Summary
    This is a **markdown** document with:
    - Bullet points
    - *Italic text*
    - [Links](https://anvil.com)
  MD
)

pdf.save_as("report.pdf")

E-Signatures

Create and manage e-signature packets:

# Create a signature packet
packet = Anvil::Signature.create(
  name: "Employment Agreement",
  signers: [
    {
      name: "John Doe",
      email: "john@example.com",
      role: "employee"
    },
    {
      name: "Jane Smith",
      email: "jane@company.com",
      role: "manager"
    }
  ],
  files: [
    { type: :pdf, id: "template_id_here" }
  ]
)

# Get signing URL for a signer
signer = packet.signers.first
signing_url = signer.signing_url

# Check status
packet.reload!
if packet.complete?
  puts "All signatures collected!"
end

Webhooks

Handle webhook events from Anvil:

Rails Controller

class AnvilWebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create
    webhook = Anvil::Webhook.new(
      payload: request.body.read,
      token: params[:token]
    )

    if webhook.valid?
      case webhook.action
      when 'signerComplete'
        handle_signer_complete(webhook.data)
      when 'etchPacketComplete'
        handle_packet_complete(webhook.data)
      end

      head :no_content
    else
      head :unauthorized
    end
  end

  private

  def handle_signer_complete(data)
    # Process signer completion
    SignerCompleteJob.perform_later(data)
  end

  def handle_packet_complete(data)
    # All signatures collected
    PacketCompleteJob.perform_later(data)
  end
end

Sinatra/Rack

post '/webhooks/anvil' do
  webhook = Anvil::Webhook.new(
    payload: request.body.read,
    token: params[:token]
  )

  halt 401 unless webhook.valid?

  # Process webhook
  case webhook.action
  when 'signerComplete'
    # Handle signer completion
  end

  status 204
end

Advanced Usage

Multi-tenant Applications

Use different API keys per request:

# Override API key for specific operations
pdf = Anvil::PDF.fill(
  template_id: "template_123",
  data: { name: "John" },
  api_key: current_tenant.anvil_api_key
)

# Or create a custom client
client = Anvil::Client.new(api_key: tenant.api_key)
pdf = Anvil::PDF.new(client: client).fill(...)

Error Handling

The gem provides specific error types for different scenarios:

begin
  pdf = Anvil::PDF.fill(template_id: "123", data: {})
rescue Anvil::ValidationError => e
  # Invalid data or parameters
  puts "Validation failed: #{e.message}"
  puts "Errors: #{e.errors}"
rescue Anvil::AuthenticationError => e
  # Invalid or missing API key
  puts "Auth failed: #{e.message}"
rescue Anvil::RateLimitError => e
  # Rate limit exceeded
  puts "Rate limited. Retry after: #{e.retry_after} seconds"
rescue Anvil::NotFoundError => e
  # Resource not found
  puts "Not found: #{e.message}"
rescue Anvil::NetworkError => e
  # Network issues
  puts "Network error: #{e.message}"
rescue Anvil::Error => e
  # Generic Anvil error
  puts "Error: #{e.message}"
end

Rate Limiting

The gem automatically handles rate limiting with exponential backoff:

# Configure custom retry behavior
client = Anvil::Client.new
client.rate_limiter = Anvil::RateLimiter.new(
  max_retries: 5,
  base_delay: 2.0
)

Development Mode

Enable development mode for watermarked PDFs and debug output:

Anvil.configure do |config|
  config.api_key = "your_dev_key"
  config.environment = :development  # Watermarks PDFs, verbose logging
end

Configuration Options

Anvil.configure do |config|
  # Required
  config.api_key = "your_api_key"

  # Optional
  config.environment = :production  # :development or :production
  config.base_url = "https://app.useanvil.com/api/v1"  # API endpoint
  config.timeout = 120  # Read timeout in seconds
  config.open_timeout = 30  # Connection timeout
  config.webhook_token = "your_webhook_token"  # For webhook verification
end

Examples

See the examples directory for complete working examples:

Development

After checking out the repo, run:

bundle install
bundle exec rspec  # Run tests
bundle exec rubocop  # Check code style

To install this gem onto your local machine:

bundle exec rake install

Testing

The gem uses RSpec for testing:

# Run all tests
bundle exec rspec

# Run specific test file
bundle exec rspec spec/anvil/pdf_spec.rb

# Run with coverage
bundle exec rspec --format documentation

CI/CD with GitHub Actions

This project uses GitHub Actions for continuous integration and automated gem publishing.

Automated Workflows

  • CI Pipeline - Runs tests, linting, and security checks on every push and PR
  • Gem Publishing - Automatically publishes to RubyGems.org when you create a version tag
  • Dependency Updates - Dependabot keeps dependencies up-to-date weekly

Quick Start

  1. Fork the repository
  2. Add your RUBYGEM_API_KEY secret to GitHub (Settings → Secrets)
  3. Push your changes - CI will run automatically
  4. Create a version tag to publish: git tag v0.2.0 && git push --tags

See .github/workflows/README.md for complete documentation on:

  • Setting up secrets and authentication
  • Running workflows manually
  • Debugging CI failures
  • Security best practices
  • Customizing workflows

Philosophy

This gem embraces Ruby's philosophy of developer happiness:

  • Zero runtime dependencies - Uses only Ruby's standard library
  • Rails-friendly - Works great with Rails but doesn't require it
  • Idiomatic Ruby - Follows Ruby conventions (predicates, bang methods, blocks)
  • Progressive disclosure - Simple things are simple, complex things are possible

Contributing

  1. Fork it (https://github.com/nickMarz/Ruby-Anvil/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Please make sure to:

  • Add tests for new features
  • Follow Ruby style guide (run rubocop)
  • Update documentation
  • Ensure all CI checks pass (tests, linting, security)
  • Check the GitHub Actions tab to monitor your PR's build status

License

The gem is available as open source under the terms of the MIT License.

Support

Acknowledgments

Built with ❤️ by Ruby developers, for Ruby developers. Inspired by the elegance of Rails and the philosophy of Matz.

Special thanks to DHH and Matz for making Ruby a joy to work with.