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.
- 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
Add to your Gemfile:
gem "printavo-ruby"or
bundle add printavo-rubyor install directly:
gem install printavo-rubyPrintavo 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"]
)# config/initializers/printavo.rb
PRINTAVO = Printavo::Client.new(
email: ENV["PRINTAVO_EMAIL"],
token: ENV["PRINTAVO_TOKEN"]
)# 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"# 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"# 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? # => trueAll 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")# 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")# 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"# 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")# 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}"# 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"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" }
)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"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"] }
endPrintavo::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
)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
endbegin
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- 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
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/consoleSee CONTRIBUTING.md for full contribution guidelines.
©2026 Stan Carver II

