0.0
The project is in a healthy, maintained state
A framework-agnostic Ruby SDK for Printavo supporting the v2 GraphQL API. Provides both a resource-oriented interface (client.orders.all) and rich domain models (Printavo::Order), plus raw GraphQL access and Rack-compatible webhook verification. Built to bridge Printavo with external operational systems.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

Runtime

 Project Readme

printavo-ruby

CI Coverage Status Gem Version License: MIT

A framework-agnostic Ruby SDK for the Printavo GraphQL API (v2).

I use Printavo every day at Texas Embroidery Ranch. This gem was created to bridge Printavo with other operational systems—CRM, marketing, finance, and automation—so that print shops can build integrated workflows without writing raw GraphQL by hand.

Features

  • Full Printavo v2 GraphQL API support
  • Resource-oriented interface: client.customers.all, client.orders.find(id)
  • Raw GraphQL access: client.graphql.query("{ ... }")
  • Rich domain models: order.status, order.status?(:in_production), order.customer
  • Rack-compatible webhook signature verification
  • Multi-client support — no globals required
  • Ruby 3.0+ required

Installation

Add to your Gemfile:

gem "printavo-ruby"

or

bundle add printavo-ruby

or install directly:

gem install printavo-ruby

Authentication

Printavo authenticates via your account email and API token (found at My Account → API Token on printavo.com).

require "printavo"

client = Printavo::Client.new(
  email: ENV["PRINTAVO_EMAIL"],
  token: ENV["PRINTAVO_TOKEN"]
)

Rails Initializer

# config/initializers/printavo.rb
PRINTAVO = Printavo::Client.new(
  email: ENV["PRINTAVO_EMAIL"],
  token: ENV["PRINTAVO_TOKEN"]
)

Usage

Customers

# List customers (25 per page by default)
customers = client.customers.all
customers.each { |c| puts "#{c.full_name}#{c.email}" }

# Paginate
page_2 = client.customers.all(first: 25, after: cursor)

# Find a specific customer
customer = client.customers.find("12345")
puts customer.full_name   # => "Jane Smith"
puts customer.company     # => "Acme Shirts"

Orders

# List orders
orders = client.orders.all
orders.each { |o| puts "#{o.nickname}: #{o.status}" }

# Find an order
order = client.orders.find("99")
puts order.status                    # => "In Production"
puts order.status_key                # => :in_production
puts order.status?(:in_production)   # => true
puts order.total_price               # => "1250.00"
puts order.customer.full_name        # => "Bob Johnson"

Jobs (Line Items)

# List jobs for an order
jobs = client.jobs.all(order_id: "99")
jobs.each { |j| puts "#{j.name} x#{j.quantity} @ #{j.price}" }

# Find a specific job
job = client.jobs.find("77")
puts job.taxable?   # => true

Pagination

All list resources support each_page and all_pages in addition to all.

# Iterate page by page (cursor-based, memory-efficient)
client.customers.each_page(first: 50) do |records|
  records.each { |c| puts c.full_name }
end

# Collect every record across all pages into one array
all_orders = client.orders.all_pages

# Jobs require order_id
client.jobs.each_page(order_id: "99") do |records|
  records.each { |j| puts j.name }
end
all_jobs = client.jobs.all_pages(order_id: "99")

Mutations

Customers

# Create
customer = client.customers.create(
  primary_contact: { firstName: "Jane", lastName: "Smith", email: "jane@example.com" },
  company_name: "Acme Shirts"
)
puts customer.full_name   # => "Jane Smith"
puts customer.company     # => "Acme Shirts"

# Update
customer = client.customers.update("42", company_name: "New Name Inc")

Orders

# Create (Printavo creates orders as quotes first)
order = client.orders.create(
  contact:          { id: "456" },
  due_at:           "2026-06-01T09:00:00Z",
  customer_due_at:  "2026-06-01",
  nickname:         "Summer Rush"
)

# Update
order = client.orders.update("99", nickname: "Rush Job", production_note: "Ships Friday")

# Move to a new status
registry = client.statuses.registry
order = client.orders.update_status("99", status_id: registry[:in_production].id)
puts order.status   # => "In Production"

Inquiries

# Create
inquiry = client.inquiries.create(
  name:    "Bob Johnson",
  email:   "bob@example.com",
  request: "100 hoodies, front + back print"
)

# Update
inquiry = client.inquiries.update("55", nickname: "Hoodies Rush")

Statuses

# List all statuses
statuses = client.statuses.all
statuses.each { |s| puts "#{s.name} (#{s.color})" }

# Build a registry for O(1) lookup by symbol key
registry = client.statuses.registry
registry[:in_production]          # => <Printavo::Status>
registry[:in_production].color    # => "#ff6600"

# Pair with an order's status_key
order = client.orders.find("99")
status = registry[order.status_key]
puts "#{order.status}#{status.color}"

Inquiries

# List inquiries (quotes / leads)
inquiries = client.inquiries.all
inquiries.each { |i| puts "#{i.nickname}: #{i.status}" }

# Find a specific inquiry
inquiry = client.inquiries.find("55")
puts inquiry.status?(:new_inquiry)    # => true
puts inquiry.customer.full_name       # => "Jane Smith"

Raw GraphQL

For queries not yet wrapped by a resource, use the raw GraphQL client directly:

result = client.graphql.query(<<~GQL)
  {
    customers(first: 5) {
      nodes {
        id
        firstName
        lastName
      }
    }
  }
GQL

result["customers"]["nodes"].each { |c| puts c["firstName"] }

With variables:

result = client.graphql.query(
  "query Customer($id: ID!) { customer(id: $id) { id email } }",
  variables: { id: "42" }
)

Mutations

Use mutate for GraphQL write operations:

result = client.graphql.mutate(
  <<~GQL,
    mutation UpdateOrder($id: ID!, $nickname: String!) {
      updateOrder(id: $id, input: { nickname: $nickname }) {
        order { id nickname }
        errors
      }
    }
  GQL
  variables: { id: "99", nickname: "Rush Job" }
)
result["updateOrder"]["order"]["nickname"]   # => "Rush Job"

Raw GraphQL Pagination

Paginate any custom query without a resource wrapper using paginate. The path: option is dot-separated and maps to the connection in the response:

client.graphql.paginate(MY_QUERY, path: "orders", first: 50) do |nodes|
  nodes.each { |n| puts n["nickname"] }
end

# Nested connection (e.g. order.lineItems)
client.graphql.paginate(JOBS_QUERY, path: "order.lineItems",
                                    variables: { orderId: "99" }) do |nodes|
  nodes.each { |j| puts j["name"] }
end

Webhooks

Printavo::Webhooks.verify provides Rack-compatible HMAC-SHA256 signature verification. No extra dependencies required.

# Pure Ruby / Rack
valid = Printavo::Webhooks.verify(
  signature,  # X-Printavo-Signature header value
  payload,    # raw request body string
  secret      # your webhook secret
)

Rails Controller Example

class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def printavo
    if Printavo::Webhooks.verify(
         request.headers["X-Printavo-Signature"],
         request.raw_post,
         ENV["PRINTAVO_WEBHOOK_SECRET"]
       )
      event = JSON.parse(request.raw_post)
      # process event["type"] ...
      head :ok
    else
      head :unauthorized
    end
  end
end

Error Handling

begin
  client.orders.find("not_a_real_id")
rescue Printavo::AuthenticationError => e
  # Bad email/token
rescue Printavo::RateLimitError => e
  # Exceeded 10 req/5 sec — back off and retry
rescue Printavo::NotFoundError => e
  # Resource doesn't exist
rescue Printavo::ApiError => e
  # GraphQL error — e.message contains details, e.response has raw data
rescue Printavo::Error => e
  # Catch-all for any Printavo error
end

Versioning Roadmap

  • CHANGELOG — what shipped in each release
  • TODO — planned versions, API coverage gaps, and stretch goals

Rules: PATCH = bug fix · MINOR = new backward-compatible feature · MAJOR = breaking change

API Documentation

Development

git clone https://github.com/scarver2/printavo-ruby.git
cd printavo-ruby
bundle install

# Run specs
bundle exec rspec

# Lint
bundle exec rubocop

# Guard DX (watches files, re-runs tests + lint on save)
bundle exec guard

# Interactive console
PRINTAVO_EMAIL=you@example.com PRINTAVO_TOKEN=your_token bin/console

See CONTRIBUTING.md for full contribution guidelines.

Colophon

MIT License

©2026 Stan Carver II

Made in Texas