A comprehensive Ruby wrapper for the Loyverse API, providing easy access to all Loyverse resources including items, inventory, receipts, categories, and webhooks.
- Complete API Coverage: Support for all major Loyverse API resources
- Authentication: Personal Access Token and OAuth 2.0 support
- Error Handling: Comprehensive error handling with custom exception classes
- Rate Limiting: Automatic retry logic with exponential backoff
- Type Safety: Well-documented methods with clear parameter types
- Webhook Support: Signature verification for secure webhook handling
Add this line to your application's Gemfile:
gem 'loyverse_api'And then execute:
$ bundle installOr install it yourself as:
$ gem install loyverse_apiConfigure the gem with your Personal Access Token:
require 'loyverse_api'
LoyverseApi.configure do |config|
config.access_token = 'your_access_token_here'
end
# Create a client instance
client = LoyverseApi.clientIn practice you'll usually want to keep the token out of source control by reading it from an environment variable instead:
LoyverseApi.configure do |config|
config.access_token = ENV['LOYVERSE_ACCESS_TOKEN']
endHow that environment variable gets set (a .env file with a gem like
dotenv, your host's secret manager, etc.) is up to your application — this
gem itself has no opinion on it.
Alternatively, you can create a client directly:
configuration = LoyverseApi::Configuration.new
configuration.access_token = 'your_access_token_here'
client = LoyverseApi::Client.new(configuration)- Login to Loyverse Back Office
- Navigate to 'Access Tokens' section
- Click '+ Add access token'
- Fill in name and optional expiration date
- Save and copy the token (shown only once)
Click to see Categories examples
# List categories
categories = client.list_categories
# Get a specific category
category = client.get_category('category-uuid')
# Create a category
new_category = client.create_category(
name: 'Beverages',
color: 'BLUE'
)
# Delete a category
client.delete_category('category-uuid')Click to see Items examples
# List items
items = client.list_items(limit: 50)
# Get items updated after a specific date
recent_items = client.list_items(
updated_at_min: Time.now - (7 * 24 * 60 * 60)
)
# Get a specific item
item = client.get_item('item-uuid')
# Create an item with variants
new_item = client.create_item(
item_name: 'Coffee',
category_id: 'category-uuid',
track_stock: true,
variants: [
{
sku: 'COFFEE-S',
price: 3.50,
cost: 1.50
}
]
)
# Update an item
client.update_item(
'item-uuid',
item_name: 'Premium Coffee'
)
# Delete an item
client.delete_item('item-uuid')Click to see Inventory examples
# List inventory levels
inventory = client.list_inventory
# Filter by variant
variant_inventory = client.list_inventory(variant_id: 'variant-uuid')
# Filter by store
store_inventory = client.list_inventory(store_id: 'store-uuid')
# Update inventory level
client.update_inventory(
variant_id: 'variant-uuid',
store_id: 'store-uuid',
in_stock: 150
)Click to see Receipts examples
# List receipts (newest first by default — see note below on `order`)
receipts = client.list_receipts
# Filter by store
store_receipts = client.list_receipts(store_id: 'store-uuid')
# Filter by date range
receipts_by_date = client.list_receipts(
created_at_min: '2024-01-01T00:00:00Z',
created_at_max: '2024-01-31T23:59:59Z'
)
# Get a specific receipt
receipt = client.get_receipt('12345')
# Create a receipt
new_receipt = client.create_receipt(
receipt_date: Time.now,
store_id: 'store-uuid',
line_items: [
{
variant_id: 'variant-uuid',
quantity: 2,
price: 10.00
}
],
payments: [
{
payment_type_id: 'payment-type-uuid',
money_amount: 20.00
}
]
)
# Create a refund
refund = client.create_refund(
'12345',
refund_date: Time.now,
line_items: [...],
payments: [...]
)Note on
order: as of this writing, the live Loyverse API silently returns an emptyreceiptsarray whenever theorderparam is present at all, regardless of its value.list_receiptsalready returns results newest-first without it, so leaveorderunset unless you're specifically testing whether Loyverse has fixed this upstream.
Click to see Customers examples
# List customers
customers = client.list_customers
# Filter by email
customer = client.list_customers(email: 'customer@example.com')
# Filter by phone
customer = client.list_customers(phone_number: '+1234567890')
# Get a specific customer
customer = client.get_customer('customer-uuid')
# Create a customer
new_customer = client.create_customer(
name: 'John Doe',
email: 'john@example.com',
phone_number: '+1234567890',
address: '123 Main St',
city: 'New York',
postal_code: '10001'
)
# Update a customer
client.update_customer(
'customer-uuid',
name: 'Jane Doe',
email: 'jane@example.com'
)
# Delete a customer
client.delete_customer('customer-uuid')Click to see Discounts examples
# List discounts
discounts = client.list_discounts
# Get a specific discount
discount = client.get_discount('discount-uuid')
# Create a percentage discount
percentage_discount = client.create_discount(
name: '10% Off',
type: LoyverseApi::Endpoints::Discounts::TYPE_FIXED_PERCENT,
discount_amount: 10.0
)
# Create a fixed amount discount
fixed_discount = client.create_discount(
name: '$5 Off',
type: LoyverseApi::Endpoints::Discounts::TYPE_FIXED_AMOUNT,
discount_amount: 5.0,
applies_to: LoyverseApi::Endpoints::Discounts::APPLIES_TO_RECEIPT
)
# Update a discount
client.update_discount(
'discount-uuid',
name: '15% Off',
discount_amount: 15.0
)
# Delete a discount
client.delete_discount('discount-uuid')Click to see Employees examples
# List employees
employees = client.list_employees
# Get a specific employee
employee = client.get_employee('employee-uuid')Note: Employees can only be read via the API. Create, update, and delete operations must be done through the Loyverse Back Office.
Click to see Modifiers examples
# List modifiers
modifiers = client.list_modifiers
# Get a specific modifier
modifier = client.get_modifier('modifier-uuid')
# Create a modifier with options
new_modifier = client.create_modifier(
name: 'Size',
options: [
{ name: 'Small', price: 0 },
{ name: 'Medium', price: 1.0 },
{ name: 'Large', price: 2.0 }
]
)
# Delete a modifier
client.delete_modifier('modifier-uuid')Click to see Webhooks examples
# List webhooks
webhooks = client.list_webhooks
# Get a specific webhook
webhook = client.get_webhook('webhook-uuid')
# Create a webhook
new_webhook = client.create_webhook(
url: 'https://your-server.com/webhooks/loyverse',
event_types: ['ORDER_CREATED', 'ITEM_UPDATED'],
description: 'Production webhook'
)
# Delete a webhook
client.delete_webhook('webhook-uuid')
# Verify webhook signature
is_valid = client.verify_webhook_signature(
request.raw_post,
request.headers['X-Loyverse-Signature'],
'your_webhook_secret'
)# Get first page
page = client.list_items(limit: 100)
# Get next page using cursor from response
next_page = client.list_items(limit: 100, cursor: page['cursor'])The gem provides specific exception classes for different error types:
begin
item = client.get_item('invalid-uuid')
rescue LoyverseApi::NotFoundError => e
puts "Item not found: #{e.message}"
rescue LoyverseApi::AuthenticationError => e
puts "Authentication failed: #{e.message}"
rescue LoyverseApi::RateLimitError => e
puts "Rate limit exceeded: #{e.message}"
rescue LoyverseApi::BadRequestError => e
puts "Bad request: #{e.message}"
puts "Error code: #{e.code}"
puts "Error details: #{e.details}"
rescue LoyverseApi::Error => e
puts "API error: #{e.message}"
endAll dates in the Loyverse API use ISO 8601 format and are always in UTC — the API itself has no concept of a "local" or store timezone. The gem automatically handles Time objects:
# Using Time objects (recommended)
items = client.list_items(
updated_at_min: Time.now - (7 * 24 * 60 * 60), # 7 days ago
updated_at_max: Time.now
)
# Using ISO 8601 strings
items = client.list_items(
updated_at_min: '2024-01-15T00:00:00Z',
updated_at_max: '2024-01-22T23:59:59Z'
)Because every timestamp in and out of the API is UTC, filtering for "today"
(or a shift, or a business day) requires converting your local day into UTC
yourself — a naive UTC midnight-to-midnight window only matches a local
calendar day if you're in UTC+0. For example, a store on CST (UTC-6) has its
local day running from 06:00Z to 06:00Z the next day, not 00:00Z to
00:00Z:
require 'date' # needed for Date.new in plain Ruby
# Store operates on CST (UTC-6, no DST as of Mexico's 2022 reform)
offset = "-06:00"
local_day = Date.new(2026, 7, 4)
day_start_utc = Time.new(local_day.year, local_day.month, local_day.day, 0, 0, 0, offset).utc
day_end_utc = day_start_utc + (24 * 60 * 60)
receipts = client.list_receipts(
created_at_min: day_start_utc,
created_at_max: day_end_utc
)This gem doesn't wrap the Loyverse Stores endpoint and has no built-in notion
of timezone — it's a thin API wrapper by design (see AGENTS.md), so if your
account has stores across multiple timezones, your application needs to keep
its own store_id => timezone mapping and compute the boundaries above per
store. See examples/receipts_for_local_day.rb for a runnable version of
this, including pagination and totals with cancelled receipts excluded.
LoyverseApi.configure do |config|
# Required: Your Personal Access Token
config.access_token = 'your_token'
# Optional: API base URL (default: 'https://api.loyverse.com')
config.api_base_url = 'https://api.loyverse.com'
# Optional: API version (default: 'v1.0')
config.api_version = 'v1.0'
# Optional: Request timeout in seconds (default: 30)
config.timeout = 30
# Optional: Connection open timeout in seconds (default: 10)
config.open_timeout = 10
endThe Loyverse API has the following rate limits:
- Standard: 60 requests per minute
- Alternative: 300 requests per 300 seconds
The gem automatically handles rate limiting with:
- Automatic retry with exponential backoff
- Maximum 3 retry attempts
- Retries for 429, 500, 502, 503, 504 status codes
ORDER_CREATED- New receipts/salesITEM_UPDATED- Product changesINVENTORY_UPDATED- Stock level changes
After checking out the repo, run bundle install to install dependencies. Then, run rake spec to run the tests.
Bug reports and pull requests are welcome on GitHub.
The gem is available as open source under the terms of the MIT License.
For issues related to the gem, please open an issue on GitHub.
For Loyverse API support, please contact Loyverse Support.