0.0
No release in over 3 years
A lightweight, fully-tested Ruby client for the Epos Now REST API. Supports Basic Auth, pagination, and all V4 resources including products, categories, transactions, customers, staff, and more.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 2.0
~> 1.6
~> 13.0
~> 3.13
~> 1.75
~> 0.22
~> 3.24

Runtime

~> 0.2
~> 0.22
 Project Readme

EposNowClient

Gem Version CI License: MIT

A lightweight, fully-tested Ruby client for the Epos Now REST API (V4).

131 specs | 100% line coverage | 100% branch coverage | 0 RuboCop offenses

Features

  • Basic Auth with API Key + Secret (Base64-encoded)
  • 10 resource classes: Products, Categories, Transactions, TenderTypes, TaxGroups, Customers, Staff, Devices, Brands, Locations
  • Automatic pagination (200 items/page, fetches all pages)
  • Retry with exponential backoff on transient network errors
  • Structured error hierarchy for clean exception handling
  • Search & filtering using Epos Now query syntax
  • Zero runtime dependencies beyond HTTParty

Installation

Add to your Gemfile:

gem 'epos_now_client'

Then run:

bundle install

Or install directly:

gem install epos_now_client

Quick Start

require 'epos_now_client'

client = EposNowClient::Client.new(
  api_key: 'your_api_key',
  api_secret: 'your_api_secret'
)

# Fetch all products
products = client.products.all

# Create a category
client.categories.create_category({ 'Name' => 'Drinks' })

# Search products
client.products.search('(Name|contains|Burger)')

Authentication

Epos Now uses Basic Auth with an API Key and Secret. Get your credentials from the Epos Now Backoffice:

  1. Navigate to Web Integrations > REST API
  2. Click Add Device
  3. Copy the API Key and API Secret

Note: Epos Now uses a single API endpoint (https://api.eposnowhq.com) for all environments. There is no separate sandbox URL. Developer accounts use the same API with test credentials.

Configuration

Global (recommended for single-account apps)

EposNowClient.configure do |config|
  config.api_key = ENV['EPOS_NOW_API_KEY']
  config.api_secret = ENV['EPOS_NOW_API_SECRET']
  config.base_url = 'https://api.eposnowhq.com'  # default
  config.timeout = 30                              # default (seconds)
  config.open_timeout = 10                         # default (seconds)
  config.max_retries = 3                           # default
  config.retry_delay = 1.0                         # default (seconds, doubles each retry)
  config.per_page = 200                            # default
  config.logger = Logger.new($stdout)              # optional
end

client = EposNowClient::Client.new

Per-client (for multi-account apps)

client = EposNowClient::Client.new(
  api_key: 'merchant_api_key',
  api_secret: 'merchant_api_secret',
  timeout: 60
)

Rails initializer

# config/initializers/epos_now.rb
EposNowClient.configure do |config|
  config.api_key = Rails.application.credentials.dig(:epos_now, :api_key)
  config.api_secret = Rails.application.credentials.dig(:epos_now, :api_secret)
  config.logger = Rails.logger
end

Usage

Categories

client.categories.all                                         # List all (paginated)
client.categories.find(42)                                    # Get by ID
client.categories.create_category({ 'Name' => 'Drinks' })    # Create
client.categories.update_category(42, { 'Name' => 'Bev' })   # Update
client.categories.delete_category(42)                         # Delete

Products

client.products.all                                           # List all
client.products.find(10)                                      # Get by ID
client.products.create_product({                              # Create
  'Name' => 'Burger',
  'SalePrice' => 9.99,
  'CategoryId' => 1
})
client.products.update_product(10, { 'SalePrice' => 11.99 }) # Update
client.products.delete_product(10)                            # Delete
client.products.search('(Name|contains|Burg)')                # Search

Transactions

client.transactions.all                                       # List all
client.transactions.find(100)                                 # Get by ID
client.transactions.by_date(                                  # By date range
  start_date: '2026-03-01',
  end_date: '2026-03-13'
)
client.transactions.latest                                    # Latest
client.transactions.create_transaction({                      # Create
  'TransactionItems' => [{ 'ProductId' => 1, 'Quantity' => 2 }],
  'Tenders' => [{ 'TenderTypeId' => 1, 'Amount' => 25.50 }],
  'ServiceType' => 0  # 0=EatIn, 1=Takeaway, 2=Delivery
})
client.transactions.delete_transaction(200)                   # Delete
client.transactions.validate(payload)                         # Validate

Tender Types

client.tender_types.all
client.tender_types.find(1)
client.tender_types.create_tender_type({ 'Name' => 'Gift Card' })

Tax Groups

client.tax_groups.all
client.tax_groups.find(1)

Customers

client.customers.all
client.customers.find(1)
client.customers.create_customer({ 'FirstName' => 'Jane', 'LastName' => 'Doe' })
client.customers.update_customer(1, { 'FirstName' => 'Janet' })
client.customers.delete_customer(1)
client.customers.search('(FirstName|contains|Jane)')

Staff

client.staff.all
client.staff.find(1)
client.staff.create_staff({ 'FirstName' => 'Bob', 'Pin' => '1234' })
client.staff.update_staff(1, { 'FirstName' => 'Robert' })
client.staff.delete_staff(1)

Devices

client.devices.all
client.devices.find(1)

Brands

client.brands.all
client.brands.find(1)
client.brands.create_brand({ 'Name' => 'Coca-Cola' })
client.brands.update_brand(1, { 'Name' => 'PepsiCo' })
client.brands.delete_brand(1)

Locations

client.locations.all
client.locations.find(1)

Pagination

All .all methods automatically paginate through every page (200 items per page):

# Fetches ALL products, regardless of how many pages exist
all_products = client.products.all

Search & Filtering

Epos Now supports search filters using the syntax (PropertyName|Operator|Value):

# Available operators: contains, StartsWith, EndsWith, >, >=, <, <=, like
client.products.search('(Name|contains|Burger)')
client.customers.search('(FirstName|StartsWith|J)')
client.products.search('(SalePrice|>|10.00)')

Error Handling

All errors inherit from EposNowClient::Error and include status and body attributes:

begin
  client.products.find(999)
rescue EposNowClient::AuthenticationError => e
  # 401 - Invalid API key or secret
rescue EposNowClient::NotFoundError => e
  # 404 - Resource not found
rescue EposNowClient::ValidationError => e
  # 422 - Invalid request payload
rescue EposNowClient::RateLimitError => e
  # 429 - API rate limit exceeded
rescue EposNowClient::ServerError => e
  # 500-599 - Epos Now server error
rescue EposNowClient::ConnectionError => e
  # Network errors (after all retries exhausted)
rescue EposNowClient::Error => e
  # Catch-all
  e.status  # HTTP status code (Integer or nil)
  e.body    # Parsed response body (Hash, String, or nil)
  e.message # Human-readable error message
end

Retry Logic

The client automatically retries on transient network errors with exponential backoff:

  • Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ETIMEDOUT
  • Net::OpenTimeout, Net::ReadTimeout
  • SocketError
EposNowClient.configure do |config|
  config.max_retries = 3    # Retry up to 3 times (default)
  config.retry_delay = 1.0  # 1s, 2s, 4s backoff (default)
end

API Reference

Resource Methods
client.categories all, find, create_category, update_category, delete_category
client.products all, find, create_product, update_product, delete_product, search
client.transactions all, find, create_transaction, delete_transaction, by_date, latest, validate
client.tender_types all, find, create_tender_type
client.tax_groups all, find
client.customers all, find, create_customer, update_customer, delete_customer, search
client.staff all, find, create_staff, update_staff, delete_staff
client.devices all, find
client.brands all, find, create_brand, update_brand, delete_brand
client.locations all, find

Development

git clone https://github.com/dan1d/epos_now_client.git
cd epos_now_client
bundle install

# Run the full suite
bundle exec rake            # RuboCop + RSpec

# Or individually
bundle exec rspec           # 131 specs, 100% line + branch coverage
bundle exec rubocop         # 0 offenses

# View coverage report
open coverage/index.html

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-feature)
  3. Write tests first (TDD)
  4. Ensure 100% coverage (bundle exec rspec)
  5. Ensure 0 RuboCop offenses (bundle exec rubocop)
  6. Commit your changes (git commit -m 'Add my feature')
  7. Push to the branch (git push origin feature/my-feature)
  8. Create a Pull Request

License

MIT License. See LICENSE.txt for details.