Introduction

Heartland Retail provides a full-featured REST API to users. The API can be used to fetch data, integrate with other products, or build custom applications on top of the POS.

All URL paths are relative to an account-specific subdomain: https://{{subdomain}}.retail.heartland.us/api/

Have a suggestion for a correction or improvement to our API docs? Please open an issue on GitHub.

Client Libraries

Here are some client libraries to make interacting with our API easier:

Don't see your language? You can still interact with our API using a general purpose HTTP library.

Want your client library listed here? Open an issue on GitHub.

Support

Heartland Retail provides limited API support for software developers.

Before submitting a support request, please ensure you have inspected the response body from your request to see if any details were returned that may point you towards the underlying issue. We also recommend checking your request body against the API documentation to ensure all values are filled out as required by that endpoint.

If you are unable to self-service your issue, you can submit a support request

OAuth

OAuth2 is a protocol that lets external applications request authorization to private details in a user's Heartland Retail account without getting their password. OAuth tokens can be limited to specific data (see Scopes), and can be revoked by users at any time. You'll need to register your application before getting started.

A registered app is assigned a unique Client ID and Client Secret which will be used in the OAuth flow. The Client Secret should not be shared.

To register your application please submit a request.

OAuth Flow

1.) Requesting authorization

Redirect the user to to this URL to request access to their Heartland Retail account on your behalf:

https://retail.heartland.us/oauth/authorize

The following values should be passed as query string parameters:

Name Type Description
client_id String Required. The client ID you received from Heartland Retail when you registered.
scope String Required. A space delimited list of requested permissions. See Scopes.
redirect_uri String Required. The URL in your app where users will be sent after authorization.
state String Random string to be passed back upon completion. It is used to protect against cross-site request forgery attacks.

2.) Issuing an access token

If the user authorizes your app, Heartland Retail will redirect back to your specified redirect_uri with a temporary code in a code GET parameter, as well as a state parameter if you provided one in the previous step. If the states don't match, the request may have been created by a third party and you should abort the process.

Exchange this for an access token: GET https://retail.heartland.us/api/oauth/token

Name Type Description
client_id String Required. The client ID you received from Heartland Retail when you registered.
client_secret String Required. The client secret you received from Heartland Retail when you registered.
code String Required. The code you received as a response to Step 1
grant_type String Required. The grant type of this flow is authorization_code
redirect_uri String Required. The URL you passed in Step 1

You'll receive a JSON response containing an access_token:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ",
  "token_type": "bearer"
}

3.) Looking up the account's hostname

Interaction with the Heartland Retail API is always done through a hostname particular to each company's account. After receiving the access_token for the account you need to perform a one time lookup of the account's hostname.

To receive the account's host send a request with the received access_token to the following URL:

GET https://retail.heartland.us/api/system/host
Authorize: Bearer {{ access_token }}

Response:

{"host": "example.retail.heartland.us"}

This host value should be used to construct the base URL for future API calls for that Heartland Retail account.

4.) Using access token and host

The access token allows you to make requests to the API on a behalf of a user. All requests must be performed using the host retrieved from the previous step.

GET https://{{ host }}/api/system/whoami
Authorize: Bearer {{ access_token }}

Scopes

Name Description
cash_drawer.close Close cash drawer
cash_drawer.manage Manage cash drawers
cash_drawer.open Open cash drawer
cash_paid.create Create cash paid out/in transactions
cash_paid.read View cash paid out/in history
custom_field.manage Manage custom fields
customer.export Export customers
customer.manage Create and manage customers
customer.merge Merge customers
customer.read View customers
financial.event.read View financial events
financial.manage Manage financial configuration
gift_card.manage Manage gift cards and adjust balances
grid_templates.manage Manage grid templates
integrations.manage Manage integrations
inventory.adjustment.manage Manage adjustments
inventory.adjustment.read View adjustments
inventory.bin.manage Update bin locations
inventory.counts.create Create physical counts
inventory.counts.finalize Review discrepancies and accept physical counts
inventory.counts.acknowledge_discrepancies Acknowledge discrepant counts
inventory.threshold.manage Edit item reorder points and target quantity
inventory.transaction.read View inventory history
inventory.transfer.read View transfers
inventory.transfer.recall_shipment Recall transfer shipments
inventory.transfer.receive Receive transfers
inventory.transfer.resolve_discrepancies Resolve transfer discrepancies
inventory.transfer.select_from_shipment Select receipt items from transfer shipment
inventory.transfer.ship Ship transfers
inventory.values.read View current inventory values
inventory.vendor.read View inventory vendor information
item.create_on_fly Create items on the fly
item.export Export items
item.manage Manage items and grids
item.merge Merge items
item.view_cost View item cost and margin information
location.manage Manage locations
location.read View locations
payment.manage Manage payments
payment.read View payments
payment_type.manage Manage payment types
pos.manage Manage POS settings
purchasing.order.manage Manage orders
purchasing.order.read.canceled View canceled orders
purchasing.order.read.closed View closed orders
purchasing.order.read.open View open orders
purchasing.order.read.pending View pending orders
purchasing.order.read View all orders
purchasing.receipt.manage Manage receipts
purchasing.receipt.read View receipts
purchasing.receipt.select_from_order Select receipt items from purchase order
purchasing.receipt.acknowledge_unauthorized_quantities Acknowledge unauthorized receipt quantities
purchasing.return.manage Manage returns
purchasing.return.read View returns
purchasing.vendor.manage Manage vendors
purchasing.vendor.read View vendors
reason_codes.manage Manage reason codes
reporting.modify Reporting Analysis
reporting.read.group.custom_payment Group reports by custom payment type
reporting.read.group.customer Group reports by customer
reporting.read.group.date Group reports by date
reporting.read.group.gift_card Group reports by gift card payments
reporting.read.group.inventory_adjustment Group reports by inventory adjustment reason
reporting.read.group.inventory_transfer Group reports by inventory transfer
reporting.read.group.item Group reports by item
reporting.read.group.location Group reports by location
reporting.read.group.payment Group reports by payment type
reporting.read.group.promotion_name Group reports by promotion name
reporting.read.group.purchasing Group reports by purchase order
reporting.read.group.sales_transaction Group reports by sales transaction
reporting.read.group.tax_rule Group reports by tax rule
reporting.read.group.time Group reports by time
reporting.read.group.vendor Group reports by vendor
reporting.read.group Group reports
reporting.read.metric.beginning_gift_card View beginning gift card balances
reporting.read.metric.beginning_inventory View beginning inventory values
reporting.read.metric.current_inventory View current inventory values
reporting.read.metric.ending_gift_card View ending gift card balances
reporting.read.metric.ending_inventory View ending inventory values
reporting.read.metric.gift_cards_expired View gift card expirations
reporting.read.metric.inventory_adjustment View inventory adjustment metrics
reporting.read.metric.inventory_transfer View transfer metrics
reporting.read.metric.location_sales_gift_cards View location sales gift cards issued metrics
reporting.read.metric.location_sales View location sales metrics
reporting.read.metric.payment View payment type metrics
reporting.read.metric.purchasing View purchasing metrics
reporting.read.metric.sales_tax View sales tax metrics
reporting.read.metric.shipping View shipping metrics
reporting.read.metric.source_sales_gift_cards View source sales gift cards issued metrics
reporting.read.metric.source_sales View source sales metrics
reporting.read.metric Group by metric
reporting.read View reporting
reporting.sales.read Read sales dashboard reports
reporting.saved_reports.manage Manage Saved Reports
reporting.saved_reports.read View Saved Reports
role.manage Manage roles
sales.coupons.manage Manage coupons
sales.coupons.read View and redeem coupons
sales.daily_summary.read View Daily Summary
sales.invoice.manage Create and fulfill invoices from sales orders
sales.order.adjust_item_price Adjust item prices on a sales order
sales.order.distribute Distribute sales orders
sales.order.edit Create and edit sales orders
sales.order.manage Manage sales orders
sales.order.view View sales orders
sales.pos.adjust_tax_amount Adjust tax amount in the POS
sales.promotions.manage Manage promotions
sales.tax.manage Manage sales tax
sales.ticket.manage Manage POS tickets
sales.transaction.adjust_item_price Adjust item prices in the POS
sales_plans.manage Manage sales plans
setting.manage Manage general settings
shipping_method.manage Manage shipping methods
station.manage Manage stations
user.manage Manage users
ui_extensions.manage Manage UI extensions
webhooks.manage Manage Webhooks
pos_pre_complete_webhooks.manage Add or remove POS Pre Complete Webhooks

Authentication

Accessing the Heartland Retail API requires a secret API token. You can manage your API keys in the API Tokens page. To access the API Tokens page in Heartland Retail click the name in the top right corner of the Heartland Retail dashboard. A drop down menu will open, you can now click "my account" to bring you to your account page. Then click "API" to bring you to API Tokens page.

Generating an API Token:

  1. Click the "Generate new token" button. This will bring you to the "New API Token" page.
  2. Type in a description for the API Token.
  3. Click the "Generate Token" button.
  4. API Token will appear. Be sure to copy your new API token now. You won’t be able to see it again.
  5. Click the "Done" button.

Each HTTP request to the API should include an Authorization header with the API token. Example:

curl -H "Authorization: Bearer {{token}}"

Permissions

Each user in Heartland Retail is assigned a role. These roles have configurable permissions. It is important to note that once you are authenticated your API calls will be limited to the set of actions allowed by your permissions.

If you are making API requests and getting 401 Unauthorized responses you may want to check your permissions via GET /api/system/whoami

Request:
require 'heartland-retail'
client = HeartlandRetail::Client.new(
  'https://{{subdomain}}.retail.heartland.us/api',
  token: '{{token}}'
)
curl -H "Authorization: Bearer {{token}}" "https://{{subdomain}}.retail.heartland.us/api/system/whoami"
Response:
true
{"id": 100001, "login": "login", "first_name": "", "last_name": ""}

Search Results

Search results objects are simple wrappers around result sets that indicate the total number of records that match the request, the number of pages available, and an array containing the results for the current page.

There are two query parameters that alter how what is returned in via search results:

  • page - which page in the results set to retrieve.
  • per_page - how many records to return in each page.

Search Results

total:
Integer

Total number of records returned by the search

pages:
Integer

The number of pages of results

results:
Array

A list of result objects

Filtering

Filtering

Collection resources can be filtered in several ways:

Full-Text Queries

Full text queries perform fuzzy searching across all indexed text fields (which fields exactly varies by collection). Partial word matches are support from the beginning of a term.

/api/items?query=big+blue+thing
/api/items?query=startswi

Filters

Advanced filters use a JSON-encoded query language. This can be specified in one of two ways:

  1. Encoded as query string parameter hash (for convenience)
  2. As a JSON encoded string (for more complex queries)

In each case the value is passed via the _filter parameter or the shorthand ~ parameter. Both parameter names are equivalent.

Using query string parameters

Queries up to a certain level of complexity can be expressed directly as query string parameters. This is useful for constructing ad-hoc queries in a browser:

/api/items?~[price][$gt]=100&~[price][$lt]=200

Resulting pseudo-SQL:

(price > 100 AND price < 100)

Using JSON

For arbitrarily complex queries it is recommended that the filters be encoded as a JSON string.

Here is an example of a complex query using nested logical operators and multiple comparison operators:

JSON:

{
  "$or": [
    {"price": {"$gte": 10, "$lte": 20}},
    {"price": 0},
    {"$and": [
      {"cost": 0},
      {"updated_at": {"$gt": "2012-01-01"}}
    ]}
  ],
  "active": true,
  "tax_category": {"$in": ["c1", "c2", "c3"]}
}

URL-encoded:

/api/items?_filter[]=%7B%22$or%22:[%7B%22price%22:%7B%22$gte%22:10,%22$lte%22:20%7D%7D,%7B%22price%22:0%7D,%7B%22$and%22:[%7B%22cost%22:0%7D,%7B%22updated_at%22:%7B%22$gt%22:%222012-01-01%22%7D%7D]%7D],%22tax_category%22:%7B%22$in%22:[%22c1%22,%22c2%22,%22c3%22]%7D,%22active%22:true%7D

Resulting pseudo-SQL:

(
  ((price >= 10) AND (price <= 20))
  OR (price = 0)
  OR ((cost = 0) AND (updated_at > '2012-01-01'))
)
AND (tax_category IN ('c1', 'c2', 'c3'))
AND (active IS TRUE)

Comparison Operators

  • $eq - Equals
  • $neq - Not equals
  • $in - In array
  • $nin - Not in array
  • $gt - Greater than
  • $gte - Greater than or equals
  • $lt - Less than
  • $lte - Less than or equals
  • $like - Like (see Like Queries)

Like Queries

Like queries use * as a wildcard. The wildcard character must be specified somewhere to get partial match behavior. If no wildcard is specified, $like behaves as $eq. Like queries are case insensitive.

  • Starts with: /api/items?~[custom@color][$like]=blue*
  • Ends with: /api/items?~[custom@color][$like]=*blue
  • Contains: /api/items?~[custom@color][$like]=*blue*

Logical Operators

Logical operators can be arbitrarily nested.

  • $and - Group array of nested expressions with logical AND
  • $or - Group array of nested expressions with logical OR

Custom Fields

Custom fields can be used in any filter expression with this syntax:

/api/items?~[custom@color]=blue
/api/items?~[custom@score][$gt]=100

Filtering Null Values

Checking for null or non-null values can be done using the $eq/$neq operators and the JSON syntax:

{
  "custom@category": {"$eq": null},
  "description": {"$neq": null}
}

Resulting pseudo-SQL:

(custom@category IS NULL) AND (description IS NOT NULL)

Filtering with references to other fields

To compare fields to other fields rather than literal values use a string with the field name wrapped in double curly-braces (e.g. {{field_name}}).

{
  "qty_shipped": {"$gt": "{{qty_received}}"}
}

Resulting pseudo-SQL:

(qty_shipped > qty_received)

Embedding

It is possible to embed the contents of associated objects in an API response using one or more _include[] query string parameters.

For example, to retrieve an object that has a my_object_id property (or any property ending in _id), you can embed the related my_object data like this:

GET /api/some-resource/1234?_include[]=my_object

The response will look something like this:

{
  "id": 1234,
  "my_object_id": 5678,
  "my_object": {
    "id": 5678,
    "description": "Object Description"
  }
}

Reporting

Reporting API

GET /api/reporting/analyzer

Params

metrics[] (required)

An array of metrics to include in the output (see Metrics section). Must specify at least one.

groups[] (optional)

An array of dimension fields to group by (see Groups section).

start_date (optional), end_date (optional)

An ISO 8601 formatted date string (date only, no time). This is used as the start/end date for any metric that has a date dimension.

sales.filters, item.filters, location.filters (optional)

A JSON filter expression (see Filtering doc for syntax) to filter the item, location, or sale records to include when computing metrics.

subtotal (optional, default: false)

If set to true, the response will include rows for subtotals at each grouping level (see groups[]). Setting to true without specifying any groups[] will result in an error.

Each result object will include an additional subtotal_level property. Its value will be null for each non-subtotal result. For the subtotals, its value will be an integer corresponding to the index in the groups[] param array.

For example, given the following groups:

location.name, item.custom@group, item.custom@category

These would be the corresponding subtotal_level property values:

Subtotal Groups Subtotal Level
location.name, item.custom@group 2
location.name 1
Grand Total 0

Metrics

Source Sales

Supported groups: item.*, location.*, customer.*, date.*, time.*

  • source_sales.net_sales
  • source_sales.net_qty_sold
  • source_sales.net_markdowns
  • source_sales.net_margin
  • source_sales.net_margin_ratio
  • source_sales.gross_sales
  • source_sales.gross_qty_sold
  • source_sales.gross_returns
  • source_sales.gross_qty_returned
  • source_sales.transaction_count
  • source_sales.customer_count
  • source_sales.item_count

Location Sales

Supported groups: item.*, location.*, customer.*, date.*, time.*

  • location_sales.sellthrough
  • location_sales.inventory_turn
  • location_sales.net_sales
  • location_sales.net_qty_sold
  • location_sales.net_markdowns
  • location_sales.net_margin
  • location_sales.net_margin_ratio
  • location_sales.gross_sales
  • location_sales.gross_qty_sold
  • location_sales.gross_returns
  • location_sales.gross_qty_returned
  • location_sales.transaction_count
  • location_sales.customer_count
  • location_sales.item_count

Beginning Inventory

Supported groups: item.*, location.*

Required params: start_date

  • beginning_inventory.qty_owned
  • beginning_inventory.cost_owned
  • beginning_inventory.price_owned
  • beginning_inventory.qty_on_po
  • beginning_inventory.price_on_po
  • beginning_inventory.qty_committed
  • beginning_inventory.cost_committed
  • beginning_inventory.price_committed
  • beginning_inventory.qty_in_transit
  • beginning_inventory.cost_in_transit
  • beginning_inventory.price_in_transit
  • beginning_inventory.qty_on_hand
  • beginning_inventory.cost_on_hand
  • beginning_inventory.price_on_hand
  • beginning_inventory.qty_available
  • beginning_inventory.cost_available
  • beginning_inventory.price_available

Ending Inventory

Supported groups: item.*, location.*

Required params: end_date

  • ending_inventory.qty_owned
  • ending_inventory.cost_owned
  • ending_inventory.price_owned
  • ending_inventory.qty_on_po
  • ending_inventory.price_on_po
  • ending_inventory.qty_committed
  • ending_inventory.cost_committed
  • ending_inventory.price_committed
  • ending_inventory.qty_in_transit
  • ending_inventory.cost_in_transit
  • ending_inventory.price_in_transit
  • ending_inventory.qty_on_hand
  • ending_inventory.cost_on_hand
  • ending_inventory.price_on_hand
  • ending_inventory.qty_available
  • ending_inventory.cost_available
  • ending_inventory.price_available

Shipping

Supported groups: locations.*

  • shipping.net_shipping_income

Payments

Supported groups: locations.*, payments.*, credit_card_payments.*

  • payment.payment_type_count
  • payment.credit_card_type_count
  • payment.payments_received
  • payment.refunds
  • payment.net_payments

Groups

NOTE: Some metric/group combinations are invalid. See the metric sections for a list of supported grouping levels. An invalid combination of metrics and groups will result in an error. For example, it's not possible to compute inventory values grouped by customer since inventory levels inherently have no customer dimension.

Item

  • item.public_id
  • item.description
  • item.custom@*key*

Customer

  • customer.public_id
  • customer.first_name
  • customer.last_name
  • customer.email
  • customer.custom@*key*

Location

  • location.public_id
  • location.name

Date

  • date.date
  • date.year
  • date.month_of_year
  • date.month_of_year_name
  • date.week_of_year
  • date.week_of_year_name
  • date.day_of_week
  • date.day_of_week_name

Time

  • time.time
  • time.hour

Payment

  • payment.type

Credit Card Payment

  • credit_card_payment.type

Custom Fields

In Heartland Retail you have the ability to create custom fields for most record types that allow you to store information in a way that makes sense for your business.

Custom fields are referenced on their corresponding group_id record via the key value. You can interact with custom fields on the individual record two different ways.

  1. To update custom field values individually, such as to only update a single custom field value. The sytnax for that is {"custom@field_key":"Value"}. That will only touch the one custom fields you specify.

  2. The other option is to update all custom field values at once. You can do this with {"custom":{"field_key":"Value"}}. It is important to note that this is an absolute value, so any existing custom field values that are not included in this request body will be removed.

For example if you have an item custom field with the name "Style Name" and the key style_name, you would update that fields value on the item record with PUT /api/item/<item_id> with the request body {"custom@style_name":"Cool New Style"}. This will then show "Cool New Style" in the "Style Name" field for that item record.

Custom Fields

Most record types are able to have custom fields that can be used to store any data you would like on that record. The request body for custom fields varies slightly depending on the record type the custom field exists on.

All custom field records have this common base definition:

Custom Field

id:
integer (auto-generated)
group_id:
text (required)

The record type that this custom field should be displayed on

Accepted values: item, customer, financial.account, inventory.transfer, item.vendor, location, payment.check, payment_type., purchasing.order, purchasing.receipt, purchasing.return, purchasing.vendor, sales.order, sales.transaction, sales.transaction.line_item
key:
text (auto-generated)

This will be the internally referenced name for the custom field. The key of the custom field must match with the key in the custom hash on the record type for the data that you want displayed. You can specify a key, or allow it to be auto-generated as a normalized unique value based on the custom field name.

name:
text (required)

The display name of the custom field that will show up in the UI. If no key is specified then this will also be what the custom field key is based on.

required:
boolean

Whether or not this record is currently active and being used

unique:
boolean

If true, this custom field must have a unique value on each record. If this is an item group custom field, having the value set to unique allows the value to be used as a lookup such as from a printed barcode/UPC.

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

validation_type:
text

Specifies if this is a text field or a picklist

Accepted values: list, text
validation_options:
array<string>

This field is required if the validation_type is list, otherwise it is not used. It can accept an array of strings or a JSON string representing an array of values.

valid_list_values:
array<string> (auto-generated)

Auto-generated validation list based on validation_options

metadata:
Object

Custom key/value information stored about this custom field. This is where additional parameters specific to the custom field group are stored.

uuid:
text (auto-generated)
Custom Field Example:
{
  "id": 109391,
  "group_id": "item",
  "key": "style_name",
  "name": "Style Name",
  "validation_type": "text",
  "validation_options": "["Value 1","Value 2","Value 3"]",
  "valid_list_values": [
    "Value 1",
    "Value 2",
    "Value 3"
  ]
}

Create a custom field

POST /api/custom_fields

Custom fields are created by POSTing a custom field record containing the required attributes to the /api/custom_fields resource. When successful, the response headers will contain a Location header that indicates the url at which the newly created custom field can be found. The custom field's unique id will be the last portion of that URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"group_id":"item","name":"Style Name"}' "https://{{subdomain}}.retail.heartland.us/api/custom_fields"
client["custom_fields"]
  .post({"group_id"=>"item", "name"=>"Style Name"})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/custom_fields/146697

Retrieve a custom field

GET /api/custom_fields/{{custom_field_id}}

Custom fields are retrieved from the Heartland Retail API via their unique id. The id can be found via searches or from the Location header in a Create Custom Field response.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/custom_fields/{{custom_field_id}}"
client["custom_fields/{{custom_field_id}}"]
  .get.body
Response:
{
  "id": 102350,
  "group_id": "item",
  "key": "style_name",
  "name": "Style Name",
  "validation_type": "text",
  "validation_options": "["Value 1","Value 2","Value 3"]",
  "valid_list_values": [
    "Value 1",
    "Value 2",
    "Value 3"
  ]
}
{"id"=>104467,
 "group_id"=>"item",
 "key"=>"style_name",
 "name"=>"Style Name",
 "validation_type"=>"text",
 "validation_options"=>"["Value 1","Value 2","Value 3"]",
 "valid_list_values"=>["Value 1", "Value 2", "Value 3"]}

Update a custom field

PUT /api/custom_fields/{{custom_field_id}}

Custom Fields can be edited by PUTting a custom field record containing a valid subset of custom field attributes to the custom field's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"group_id":"item","name":"Style Name","validation_type":"text","validation_options":"["Value 1","Value 2","Value 3"]"}' "https://{{subdomain}}.retail.heartland.us/api/custom_fields/{{custom_field_id}}"
client["custom_fields/{{custom_field_id}}"]
  .put(my_custom_field_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Search custom fields

GET /api/custom_fields

Custom fields can be searched by GETting the root of the custom field resource /api/custom_fields. This API call will result in a search result record containing zero or more custom field records in the values attribute. See Search Results for more information on search results.

Custom fields can be filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/custom_fields/?per_page=1"
client["custom_fields"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 100653,
      "group_id": "item",
      "key": "style_name",
      "name": "Style Name",
      "validation_type": "text",
      "validation_options": "["Value 1","Value 2","Value 3"]",
      "valid_list_values": [
        "Value 1",
        "Value 2",
        "Value 3"
      ]
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>108353,
    "group_id"=>"item",
    "key"=>"style_name",
    "name"=>"Style Name",
    "validation_type"=>"text",
    "validation_options"=>"["Value 1","Value 2","Value 3"]",
    "valid_list_values"=>["Value 1", "Value 2", "Value 3"]},
   "..."]}

Inventory

Inventory APIs control everything related to items in the system, their inventory values, adjusting inventory, and transfering inventory between locations

Adjustment Set

A adjustment set is a record of items that have been or will be adjusted.

Inventory Adjustment Set

id:
integer (auto-generated)
adjustment_reason_id:
integer (required)
location_id:
integer (required)
note:
text
status:
text (required)
Accepted values: pending, complete, canceled
created_by_user_id:
integer (auto-generated)

The user who created this record

updated_by_user_id:
integer (auto-generated)

The most recent user who updated this record

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

Inventory Adjustment Set Line

id:
integer (auto-generated)
adjustment_set_id:
integer (required)
item_id:
integer (required)
qty:
integer (required)
unit_cost:
numeric(10,2) (required)
created_by_user_id:
integer (auto-generated)

The user who created this record

updated_by_user_id:
integer (auto-generated)

The most recent user who updated this record

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

Inventory Adjustment Set Example:
{
  "id": 104773,
  "adjustment_reason_id": 105174,
  "location_id": 100250,
  "created_by_user_id": 103327,
  "updated_by_user_id": 103563
}
Inventory Adjustment Set Line Example:
{
  "id": 100436,
  "adjustment_set_id": 109384,
  "item_id": 105781,
  "qty": 101545,
  "unit_cost": 68.18,
  "created_by_user_id": 109372,
  "updated_by_user_id": 100170
}

Create an adjustment set

POST /api/inventory/adjustment_sets

Creating an adjustment set requires an adjustment reason id and a location id. The location id is used to determine where the adjustment set will be applied.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"adjustment_reason_id":103563,"location_id":104753}' "https://{{subdomain}}.retail.heartland.us/api/inventory/adjustment_sets"
client["inventory/adjustment_sets"]
  .post({"adjustment_reason_id"=>105315, "location_id"=>105995})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/inventory/adjustment_sets/158234

Retrieve an adjustment set

GET /api/inventory/adjustment_sets/{{adjustment_set_id}}

An adjustment set is retrieved using its unique identifier. This identifier can be found by searching for adjustment sets, from references in other records, or from the Location header returned in response to the creation of a new adjustment set.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/inventory/adjustment_sets/{{adjustment_set_id}}"
client["inventory/adjustment_sets/{{adjustment_set_id}}"]
  .get.body
Response:
{
  "id": 104377,
  "adjustment_reason_id": 105724,
  "location_id": 101112,
  "created_by_user_id": 105085,
  "updated_by_user_id": 101892
}
{"id"=>102987,
 "adjustment_reason_id"=>101575,
 "location_id"=>109428,
 "created_by_user_id"=>102472,
 "updated_by_user_id"=>106325}

Update an adjustment set

PUT /api/inventory/adjustment_sets/{{adjustment_set_id}}

Adjustment sets can be edited by PUTting a record containing a valid subset of adjustment set attributes to the adjustment sets resource URL. To complete an adjustment set, you will just need to set the status to "complete"

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"adjustment_reason_id":101972,"location_id":108911}' "https://{{subdomain}}.retail.heartland.us/api/inventory/adjustment_sets/{{adjustment_set_id}}"
client["inventory/adjustment_sets/{{adjustment_set_id}}"]
  .put(my_inventory_adjustment_set_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Retrieve an adjustment set's lines

GET /api/inventory/adjustment_sets/{{adjustment_set_id}}/lines

An adjustment set has zero or more associated lines.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/adjustment_sets/{{adjustment_set_id}}/lines/?per_page=1"
client["inventory/adjustment_sets/{{adjustment_set_id}}/lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 100505,
      "adjustment_set_id": 106843,
      "item_id": 103581,
      "qty": 102581,
      "unit_cost": 65.28,
      "created_by_user_id": 103126,
      "updated_by_user_id": 107416
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>105320,
    "adjustment_set_id"=>104528,
    "item_id"=>101063,
    "qty"=>101004,
    "unit_cost"=>89.43,
    "created_by_user_id"=>103098,
    "updated_by_user_id"=>109626},
   "..."]}

Add an item to an adjustment set

POST /api/inventory/adjustment_sets/{{adjustment_set_id}}/lines

Adds an item with the given qty to the adjustment set. This method can also be used to increment the quantity of an existing item line.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"adjustment_set_id":103785,"item_id":105990,"qty":107872,"unit_cost":77.76}' "https://{{subdomain}}.retail.heartland.us/api/inventory/adjustment_sets/{{adjustment_set_id}}/lines"
client["inventory/adjustment_sets/{{adjustment_set_id}}/lines"]
  .post({"adjustment_set_id"=>101275, "item_id"=>106185, "qty"=>105069, "unit_cost"=>91.63})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/inventory/adjustment_sets/197048/lines/175826

Search adjustment sets

GET /api/inventory/adjustment_sets

Adjustment sets can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/adjustment_sets/?per_page=1"
client["inventory/adjustment_sets"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 109199,
      "adjustment_reason_id": 102767,
      "location_id": 107181,
      "created_by_user_id": 103938,
      "updated_by_user_id": 104509
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>103036,
    "adjustment_reason_id"=>100506,
    "location_id"=>107513,
    "created_by_user_id"=>101137,
    "updated_by_user_id"=>101935},
   "..."]}

Items

The items resource exposes the ability to create and modify items in the Heartland Retail system. The vast majority of item APIs make use of the item record type:

Item

id:
integer (auto-generated)
public_id:
text

The unique public identifier for this item. This can be used to provide your own naming scheme to your inventory. Items can be searched for using this value.

metadata:
text

a placeholder for custom data (deprecated)

cost:
numeric(10,2) (required)

The default cost of purchasing this item

price:
numeric(10,2) (required)

The default sales price

description:
text (required)

A short description of the item.

long_description:
text

A long description of the item

custom:
hash

An object that acts as a container for custom values defined in Heartland Retail as well as any arbitrary values stored by integrations or custom applications.

active:
boolean

Whether or not this record is currently active and being used

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

financial_class_id:
integer

The id of the financial class this item belongs to.

weight:
numeric(10, 2)

The weight of the tem

width:
numeric(10, 2)

The width of the item

height:
numeric(10, 2)

The height of the item

depth:
numeric(10, 2)

The depth of the item

import_batch_id:
integer (computed)

If this item was imported or modified by an import this attribute references the import batch that brought this item into the system or updated it most recently.

primary_vendor_id:
integer

The id for the primary vendor record that supplies this item.

Item Example:
{
  "id": 106762,
  "cost": 92.34,
  "price": 83.65,
  "financial_class_id": 109859,
  "import_batch_id": 101752,
  "primary_vendor_id": 107425
}

Create an item

POST /api/items

Items are created by POSTing an item record containing the required attributes to the /api/items resource. When successful, the response headers will contain a Location header that indicates the url at which the newly created item can be found. The item's unique id will be the last portion of that URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"cost":61.77,"price":28.04}' "https://{{subdomain}}.retail.heartland.us/api/items"
client["items"]
  .post({"cost"=>28.99, "price"=>66.5})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/items/163735

Retrieve an item

GET /api/items/{{item_id}}

Items are retrieved from the Heartland Retail API via their unique id. The id can be found via searches, references from other records, or from the Location header in a Create Item response.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/items/{{item_id}}"
client["items/{{item_id}}"].get.body
Response:
{
  "id": 102390,
  "cost": 95.85,
  "price": 27.71,
  "financial_class_id": 103803,
  "import_batch_id": 100259,
  "primary_vendor_id": 104520
}
{"id"=>106656,
 "cost"=>96.67,
 "price"=>24.5,
 "financial_class_id"=>106534,
 "import_batch_id"=>103670,
 "primary_vendor_id"=>108335}

Update an item

PUT /api/items/{{item_id}}

Items can be edited by PUTting an item record containing a valid subset of item attributes to the item's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"cost":56.99,"price":55.55,"financial_class_id":100739,"import_batch_id":102831,"primary_vendor_id":105868}' "https://{{subdomain}}.retail.heartland.us/api/items/{{item_id}}"
client["items/{{item_id}}"]
  .put(my_item_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Search items

GET /api/items

Items can be searched by GETting the root of the items resource /api/items. This API call will result in a search result record containing zero or more item records in the values attribute. See Search Results for more information on search results.

Items can be filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/items/?per_page=1"
client["items"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 103096,
      "cost": 86.78,
      "price": 15.3,
      "financial_class_id": 105749,
      "import_batch_id": 109261,
      "primary_vendor_id": 107067
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>100671,
    "cost"=>39.39,
    "price"=>91.49,
    "financial_class_id"=>109594,
    "import_batch_id"=>104722,
    "primary_vendor_id"=>104684},
   "..."]}

Merge items

POST /api/items/{{item_id}}/merges

If you have duplicate items and would like to combine them into one item you can do so using the merges API. This will combine their open inventory quantities, inventory histories, and sales histories.

The base item specified in the URL is considered the "master" item. Include an array of slave_ids in the request body. These "slave" items will be deleted after having their data combined into the master.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"slave_ids":[100002,100003]}' "https://{{subdomain}}.retail.heartland.us/api/items/{{item_id}}/merges"
client["items/{{item_id}}/merges"]
  .post({"slave_ids"=>[100002, 100003]})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/items/182227/merges/155936

Item Grids

The items resource exposes the ability to create and modify items in the Heartland Retail system. The vast majority of item grids APIs make use of the item record type:

Item Grid

id:
integer (auto-generated)
metadata:
text

a placeholder for custom data

description:
text (required)

A short description of the item.

long_description:
text

A long description of the item

item_cost:
numeric(10,2)

The default cost of purchasing this item

item_price:
numeric(10,2)

The default sales price

item_original_price:
numeric(10,2)

The original sales price

item_custom:
hash

An object that acts as a container for custom values defined in Heartland Retail as well as any arbitrary values stored by integrations or custom applications.

active:
boolean

Whether or not this record is currently active and being used

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

item_financial_class_id:
integer

The id of the financial class this item belongs to.

item_primary_vendor_id:
integer

The id for the primary vendor record that supplies this item.

Item Grid Example:
{
  "id": 102757,
  "item_cost": 86.11,
  "item_price": 89.79,
  "item_original_price": 80.5,
  "item_financial_class_id": 108297,
  "item_primary_vendor_id": 108225
}

Create an item grid

POST /api/item_grids

Item grids are created by POSTing an item grid record containing the required attributes to the /api/item_grids resource. When successful, the response headers will contain a Location header that indicates the url at which the newly created item grid can be found. The item grid's unique id will be the last portion of that URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/item_grids"
client["item_grids"].post({}).headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/item_grids/111213

Retrieve an item grid

GET /api/item_grids/{{item_grid_id}}

Item grids are retrieved from the Heartland Retail API via their unique id. The id can be found via searches, references from other records, or from the Location header in a Create Item grid response.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/item_grids/{{item_grid_id}}"
client["item_grids/{{item_grid_id}}"].get.body
Response:
{
  "id": 100883,
  "item_cost": 39.51,
  "item_price": 40.06,
  "item_original_price": 13.38,
  "item_financial_class_id": 101476,
  "item_primary_vendor_id": 105496
}
{"id"=>109016,
 "item_cost"=>41.36,
 "item_price"=>42.0,
 "item_original_price"=>91.03,
 "item_financial_class_id"=>101214,
 "item_primary_vendor_id"=>107091}

Update an item grid

PUT /api/item_grids/{{item_grid_id}}

Item grids can be edited by PUTting an item grid record containing a valid subset of item grid attributes to the item grid's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"item_cost":73.77,"item_price":28.62,"item_original_price":15.25,"item_financial_class_id":103636,"item_primary_vendor_id":103732}' "https://{{subdomain}}.retail.heartland.us/api/item_grids/{{item_grid_id}}"
client["item_grids/{{item_grid_id}}"]
  .put(my_item_grid_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Search item grids

GET /api/item_grids

Item grids can be searched by GETting the root of the item grids resource /api/item_grids. This API call will result in a search result record containing zero or more item grid records in the values attribute. See Search Results for more information on search results.

Item grids can be filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/item_grids/?per_page=1"
client["item_grids"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 102484,
      "item_cost": 78.92,
      "item_price": 30.59,
      "item_original_price": 31.23,
      "item_financial_class_id": 107095,
      "item_primary_vendor_id": 105626
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>103048,
    "item_cost"=>40.4,
    "item_price"=>98.73,
    "item_original_price"=>63.69,
    "item_financial_class_id"=>109073,
    "item_primary_vendor_id"=>100803},
   "..."]}

Inventory Values

The inventory values API allows you to retrieve real-time inventory values from across your business. This API uses our advanced filtering capabilities to let you filter and group the data however you like.

Retrieve inventory value totals

GET /api/inventory/values

This will return the total combined inventory values for all items and locations.

You can optionally pass the query parameter group[]=location_id to get the totals by location.

Request:
client[:inventory][:values].get.body
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/values"
Response:
{
  "total"=>1,
  "pages"=>1,
  "results"=>[
    {
      "qty"=>4205.0,
      "qty_on_hand"=>4205.0,
      "qty_committed"=>61.0,
      "qty_on_po"=>2781.0,
      "qty_in_transit"=>55.0,
      "qty_available"=>4089.0,
      "unit_cost"=>9.95,
      "qty_contributed_to_unit_cost"=>4205.0
    }
  ]
}
{
  "results": [
    {
      "qty": 4205.0,
      "qty_on_hand": 4205.0,
      "qty_committed": 61.0,
      "qty_on_po": 2781.0,
      "qty_in_transit": 55.0,
      "qty_available": 4089.0,
      "unit_cost": 9.95,
      "qty_contributed_to_unit_cost": 4205.0
    }
  ],
  "total": 1,
  "pages": 1
}

Retrieve inventory values by item

GET /api/inventory/values

Inventory values are retrieved via a GET request to /api/inventory/values with the query parameter group[]=item_id. A result is returned for each item that has an inventory history. Items that have never been received, adjusted, counted, sold, or returned will not be present as all values are implicitly 0.

You can also optionally pass the query parameter group[]=location_id to return the item totals broken down by location.

Another optional query parameter is exclude_empty_locations=true and this will only return a result for locations with at least one inventory value that is not 0.

Request:
client[:inventory][:values].query(group: [:item_id]).get.body
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/values?group[]=item_id&per_page=2"
Response:
{
  "total"=>19611,
  "pages"=>9806,
  "results"=>[
    {
      "qty"=>1880.0,
      "qty_on_hand"=>1880.0,
      "qty_committed"=>7.0,
      "qty_on_po"=>19.0,
      "qty_in_transit"=>9.0,
      "qty_available"=>1873.0,
      "unit_cost"=>10.25,
      "item_id"=>68033
    },
    {
      "qty"=>112.0,
      "qty_on_hand"=>112.0,
      "qty_committed"=>0.0,
      "qty_on_po"=>8.0,
      "qty_in_transit"=>0.0,
      "qty_available"=>112.0,
      "unit_cost"=>24.93017857142857,
      "item_id"=>68034
    }
  ]
}
{
   "total":19611,
   "pages":9806,
   "results":[
      {
         "qty":1880.0,
         "qty_on_hand":1880.0,
         "qty_committed":7.0,
         "qty_on_po":19.0,
         "qty_in_transit":9.0,
         "qty_available":1873.0,
         "unit_cost":10.2494840425531915,
         "item_id":68033
      },
      {
         "qty":112.0,
         "qty_on_hand":112.0,
         "qty_committed":0.0,
         "qty_on_po":8.0,
         "qty_in_transit":0.0,
         "qty_available":112.0,
         "unit_cost":24.9301785714285714,
         "item_id":68034
      }
   ]
}

Search for an items inventory values

GET /api/inventory/values

You can retrieve the current inventory values for a single item via a GET request to /api/inventory/values with the query parameters group[]=item_id, group[]=location_id , and item_id=100001. This request will only work if all 3 parameters are passed.

You can use the optional query parameter exclude_empty_locations=true to only return a result for locations with at least one inventory value that is not 0.

Request:
client[:inventory][:values].query(group: [:item_id, :location_id], item_id: 100001).get.body
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/values?group[]=item_id&group[]=location_id&item_id=100001&per_page=2"
Response:
{
  "total"=>19611,
  "pages"=>9806,
  "results"=>[
    {
      "qty"=>25.0,
      "qty_on_hand"=>25.0,
      "qty_committed"=>7.0,
      "qty_on_po"=>19.0,
      "qty_in_transit"=>9.0,
      "qty_available"=>18.0,
      "unit_cost"=>10.25,
      "item_id"=>100001,
      "location_id"=>100001
    },
    {
      "qty"=>112.0,
      "qty_on_hand"=>112.0,
      "qty_committed"=>0.0,
      "qty_on_po"=>8.0,
      "qty_in_transit"=>0.0,
      "qty_available"=>112.0,
      "unit_cost"=>10.25,
      "item_id"=>100001,
      "location_id"=>100002
    }
  ]
}
{
   "total":19611,
   "pages":9806,
   "results":[
    {
      "qty": 25.0,
      "qty_on_hand": 25.0,
      "qty_committed": 7.0,
      "qty_on_po": 19.0,
      "qty_in_transit": 9.0,
      "qty_available": 18.0,
      "unit_cost": 10.25,
      "item_id": 100001,
      "location_id": 100001
    },
    {
      "qty": 112.0,
      "qty_on_hand": 112.0,
      "qty_committed": 0.0,
      "qty_on_po": 8.0,
      "qty_in_transit": 0.0,
      "qty_available": 112.0,
      "unit_cost": 10.25,
      "item_id": 100001,
      "location_id": 100002
    }
  ]
}

Inventory Transactions

Heartland Retail creates a record whenever any type of inventory transaction takes place. The Inventory Transactions API allows you to access these records.

Inventory transaction APIs work with the inventory transaction record type:

Inventory Transaction

id:
integer (auto-generated)

The unique identifier for a transaction

item_id:
integer (required)

the item id whose inventory was altered.

unit_cost:
numeric(10,2) (required)

the per-item cost for the items altered in this transaction

location_id:
integer (required)

the id of the location whose inventory is being altered

delta_qty:
numeric(10,2)

the change in quantity

delta_qty_on_po:
numeric(10,2)

the change in quantity pending on a purchase order

delta_qty_committed:
numeric(10,2)

the change in quantity committed to a sale

delta_qty_in_transit:
numeric

the change in quantity being transferred between stores

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

Inventory Transaction Example:
{
  "id": 109306,
  "item_id": 103912,
  "unit_cost": 63.49,
  "location_id": 107279,
  "delta_qty": 40.56,
  "delta_qty_on_po": 33.25,
  "delta_qty_committed": 42.81
}

Search inventory transactions

GET /api/inventory/transactions

Searching and filtering inventory transactions is done via a GET request to /api/inventory/transactions.

Request:
client[:inventory][:transactions].query(per_page: 2).get.body
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transactions?per_page=2"
Response:
{
  "total"=>119,
  "pages"=>60,
  "results"=>
  [
    {
      "id"=>100001,
      "item_id"=>100001,
      "unit_cost"=>45.0,
      "created_at"=>"2013-08-05T09:43:55-04:00",
      "delta_qty"=>-1.0,
      "delta_qty_on_po"=>nil,
      "delta_qty_committed"=>nil,
      "delta_qty_in_transit"=>nil,
      "location_id"=>100001
    },
    {
      "id"=>100002,
      "item_id"=>100003,
      "unit_cost"=>7.0,
      "created_at"=>"2013-08-06T08:20:13-04:00",
      "delta_qty"=>-1.0,
      "delta_qty_on_po"=>nil,
      "delta_qty_committed"=>nil,
      "delta_qty_in_transit"=>nil,
      "location_id"=>100001
    }
  ]
}
{
  "total": 119,
  "pages": 60,
  "results": [
    {
      "id": 100001,
      "item_id": 100001,
      "unit_cost": 45.0,
      "created_at": "2013-08-05T09:43:55-04:00",
      "delta_qty": -1.0,
      "delta_qty_on_po": null,
      "delta_qty_committed": null,
      "delta_qty_in_transit": null,
      "location_id": 100001
    },
    {
      "id": 100002,
      "item_id": 100003,
      "unit_cost": 7.0,
      "created_at": "2013-08-06T08:20:13-04:00",
      "delta_qty": -1.0,
      "delta_qty_on_po": null,
      "delta_qty_committed": null,
      "delta_qty_in_transit": null,
      "location_id": 100001
    }
  ]
}

Inventory Transfers

The inventory transfers resource exposes the ability to create and modify inventory transfers in the Heartland Retail system.

There are two common flows for Inventory Transfers:

Create Transfer => Add Transfer Lines => Mark as shipped => Select received Lines => Mark as received => Complete

Alternatively you can create an Inventory Transfer Request first:

Create Transfer Request => Add Requested Lines => Mark as requested => Select shipped Lines => Mark as shipped => Select received Lines => Mark as received => Complete

The vast majority of inventory transfer APIs make use of the inventory transfer record type:

Inventory Transfer

id:
integer (auto-generated)

The id of the transfer record

status:
text (required)

Status of the transfer

from_location_id:
integer

The id of the location from which items are shipped

to_location_id:
integer

The id of the location to which items are shipped

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

created_by_user_id:
integer
updated_by_user_id:
integer

The id of the last user who updated the transfer

shipped_at:
timestamp without time zone

The timestamp when the transfer was shipped

shipped_by_user_id:
integer

The id of the user who shipped the transfer

received_at:
timestamp without time zone

The timestamp when the transfer was received

received_by_user_id:
integer

The id of the user who received the transfer

total_qty_shipped:
numeric(10,2)

Total quantity of items shipped

total_qty_received:
numeric(10,2)

Total quantity of items received

total_qty_lost:
numeric(10,2)

Total quantity of items lost

custom:
object

Custom field values

total_qty_requested:
numeric(10,2)

Total quantity of items requested (for Transfer Requests)

due_date:
date

Due Date for Transfer Requests

purchasing_receipt_id:
integer

The id of purchasing receipt that initiated the Transfer Request

total_qty_discrepant:
numeric(10,2)

Total quantity of items discrepant (the difference between qtyreceived and qtyshipped)

receipt_overdue:
boolean

Whether the transfer is overdue to be received or not

pick_overdue:
boolean

Whether the transfer is overdue to be picked or not

Inventory TransferLines Bulk Updating

An expected format to update one or more transfer lines.

transfer_lines:
object (required)

A collection of one or more lines entries.

all_selected:
boolean

If true then any existing transfer lines will be updated

column_name:
text

The column to update (only when all_selected is true)

source_column_name:
text

The column name to update to (only when all_selected is true)

Inventory Transfer Example:
{
  "id": 106022,
  "status": "in_transit",
  "from_location_id": 105736,
  "to_location_id": 107891,
  "created_by_user_id": 109189,
  "updated_by_user_id": 106072,
  "shipped_by_user_id": 109889,
  "received_by_user_id": 108123,
  "total_qty_shipped": 56.6,
  "total_qty_received": 75.67,
  "total_qty_lost": 46.41,
  "custom": {
    "custom1": "Custom value 1",
    "custom2": "Custom value 2"
  },
  "total_qty_requested": 31.47,
  "purchasing_receipt_id": 108705,
  "total_qty_discrepant": 72.75
}
Inventory TransferLines Bulk Updating Example:
{
  "transfer_lines": {
    "123": "{ 'qty_received' => 5 }",
    "321": "{ 'qty_received' => 2 }"
  },
  "column_name": "qty_received",
  "source_column_name": "qty_shipped"
}

Search inventory transfers

GET /api/inventory/transfers

Inventory Transfers can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/?per_page=1"
client["inventory/transfers"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 108152,
      "status": "in_transit",
      "from_location_id": 107288,
      "to_location_id": 108813,
      "created_by_user_id": 107663,
      "updated_by_user_id": 101745,
      "shipped_by_user_id": 108826,
      "received_by_user_id": 100645,
      "total_qty_shipped": 98.76,
      "total_qty_received": 81.32,
      "total_qty_lost": 90.49,
      "custom": {
        "custom1": "Custom value 1",
        "custom2": "Custom value 2"
      },
      "total_qty_requested": 32.67,
      "purchasing_receipt_id": 102566,
      "total_qty_discrepant": 97.85
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>103569,
    "status"=>"in_transit",
    "from_location_id"=>100143,
    "to_location_id"=>109977,
    "created_by_user_id"=>100706,
    "updated_by_user_id"=>107870,
    "shipped_by_user_id"=>105596,
    "received_by_user_id"=>104819,
    "total_qty_shipped"=>76.7,
    "total_qty_received"=>62.6,
    "total_qty_lost"=>74.4,
    "custom"=>{"custom1"=>"Custom value 1", "custom2"=>"Custom value 2"},
    "total_qty_requested"=>14.37,
    "purchasing_receipt_id"=>100216,
    "total_qty_discrepant"=>20.99},
   "..."]}

Get an inventory transfer

GET /api/inventory/transfers/{{transfer_id}}

Inventory Transfers can be fetched by GETting the inventory transfer's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}"
client["inventory/transfers/{{transfer_id}}"]
  .get.body
Response:
{
  "id": 103999,
  "status": "in_transit",
  "from_location_id": 106319,
  "to_location_id": 104224,
  "created_by_user_id": 105141,
  "updated_by_user_id": 102477,
  "shipped_by_user_id": 107091,
  "received_by_user_id": 105995,
  "total_qty_shipped": 32.76,
  "total_qty_received": 54.51,
  "total_qty_lost": 10.17,
  "custom": {
    "custom1": "Custom value 1",
    "custom2": "Custom value 2"
  },
  "total_qty_requested": 16.06,
  "purchasing_receipt_id": 104549,
  "total_qty_discrepant": 73.35
}
{"id"=>104403,
 "status"=>"in_transit",
 "from_location_id"=>109871,
 "to_location_id"=>102203,
 "created_by_user_id"=>105239,
 "updated_by_user_id"=>101452,
 "shipped_by_user_id"=>103125,
 "received_by_user_id"=>101063,
 "total_qty_shipped"=>61.88,
 "total_qty_received"=>19.05,
 "total_qty_lost"=>82.64,
 "custom"=>{"custom1"=>"Custom value 1", "custom2"=>"Custom value 2"},
 "total_qty_requested"=>67.25,
 "purchasing_receipt_id"=>109488,
 "total_qty_discrepant"=>51.87}

Create an inventory transfer

POST /api/inventory/transfers

Inventory Transfers are created by POSTing a inventory transfer record containing the required attributes to the /api/inventory/transfers resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"status":"in_transit"}' "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers"
client["inventory/transfers"]
  .post({ "from_location_id"=>"123", "to_location_id"=>"321" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/inventory/transfers/197764

Update an inventory transfer

PUT /api/inventory/transfers/{{transfer_id}}

Inventory Transfers can be edited by PUTting an inventory transfer record containing a valid subset of inventory transfer attributes to the inventory transfer's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"status":"in_transit","from_location_id":103100,"to_location_id":109899,"created_by_user_id":105256,"updated_by_user_id":103199,"shipped_by_user_id":103640,"received_by_user_id":102920,"total_qty_shipped":64.16,"total_qty_received":64.22,"total_qty_lost":28.02,"custom":{"custom1":"Custom value 1","custom2":"Custom value 2"},"total_qty_requested":70.86,"purchasing_receipt_id":107130,"total_qty_discrepant":85.77}' "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}"
client["inventory/transfers/{{transfer_id}}"]
  .put(my_inventory_transfer_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Search inventory transfer lines

GET /api/inventory/transfers/{{transfer_id}}/lines

Inventory Transfer Lines can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/lines/?per_page=1"
client["inventory/transfers/{{transfer_id}}/lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 100357,
      "transfer_id": 107408,
      "item_id": 103783,
      "qty_shipped": 86.0,
      "qty_received": 42.92,
      "qty_lost": 68.33,
      "unit_cost": 50.1,
      "qty_discrepant": 90.25,
      "qty_over": 94.13,
      "qty_short": 82.08,
      "created_by_user_id": 109314,
      "updated_by_user_id": 101250,
      "qty_requested": 75.12
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>106195,
    "transfer_id"=>103279,
    "item_id"=>101312,
    "qty_shipped"=>88.64,
    "qty_received"=>86.23,
    "qty_lost"=>94.5,
    "unit_cost"=>68.58,
    "qty_discrepant"=>22.39,
    "qty_over"=>39.04,
    "qty_short"=>15.14,
    "created_by_user_id"=>105986,
    "updated_by_user_id"=>104010,
    "qty_requested"=>53.33},
   "..."]}

Get an inventory transfer line

GET /api/inventory/transfers/{{transfer_id}}/lines/{{line_id}}

Inventory Transfer Lines can be fetched by GETting the inventory transfer line's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/lines/{{line_id}}"
client["inventory/transfers/{{transfer_id}}/lines/{{line_id}}"]
  .get.body
Response:
{
  "id": 104092,
  "transfer_id": 100664,
  "item_id": 103213,
  "qty_shipped": 48.27,
  "qty_received": 53.8,
  "qty_lost": 66.64,
  "unit_cost": 49.04,
  "qty_discrepant": 56.13,
  "qty_over": 38.01,
  "qty_short": 68.04,
  "created_by_user_id": 105496,
  "updated_by_user_id": 105355,
  "qty_requested": 24.82
}
{"id"=>109060,
 "transfer_id"=>100224,
 "item_id"=>106610,
 "qty_shipped"=>49.19,
 "qty_received"=>45.9,
 "qty_lost"=>54.92,
 "unit_cost"=>84.63,
 "qty_discrepant"=>71.31,
 "qty_over"=>37.32,
 "qty_short"=>21.57,
 "created_by_user_id"=>101311,
 "updated_by_user_id"=>101197,
 "qty_requested"=>52.41}

Create an inventory transfer line

POST /api/inventory/transfers/{{transfer_id}}/lines

Inventory Transfer Lines are created by POSTing a inventory transfer line record containing the required attributes to the /api/inventory/transfers/{{transfer_id}}/lines resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"transfer_id":107643,"qty_shipped":35.78,"qty_received":77.45,"qty_lost":18.56,"unit_cost":61.2,"qty_discrepant":40.7,"qty_over":15.51,"qty_short":48.49,"qty_requested":53.11}' "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/lines"
client["inventory/transfers/{{transfer_id}}/lines"]
  .post({ "item_id"=>"123", "qty_shipped"=>"5" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/inventory/transfers/175492/lines/132494

Update an inventory transfer line

PUT /api/inventory/transfers/{{transfer_id}}/lines/{{line_id}}

Inventory Transfer Lines can be edited by PUTting an inventory transfer line record containing a valid subset of inventory transfer line attributes to the inventory transfer line's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"transfer_id":109937,"item_id":103010,"qty_shipped":38.23,"qty_received":44.35,"qty_lost":94.72,"unit_cost":34.28,"qty_discrepant":22.4,"qty_over":90.32,"qty_short":64.17,"created_by_user_id":104617,"updated_by_user_id":103567,"qty_requested":69.57}' "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/lines/{{line_id}}"
client["inventory/transfers/{{transfer_id}}/lines/{{line_id}}"]
  .put(my_inventory_transfer_line_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Update inventory transfer lines in bulk

PUT /api/inventory/transfers/{{transfer_id}}/lines

Inventory Transfer Lines can be edited in bulk by PUTting an inventory transfer line record containing a valid subset of inventory transfer line attributes and providing the list of transfer line ids to update to the inventory transfer lines resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"transfer_id":102691,"item_id":107950,"qty_shipped":84.83,"qty_received":23.55,"qty_lost":98.82,"unit_cost":74.22,"qty_discrepant":37.45,"qty_over":61.17,"qty_short":97.76,"created_by_user_id":107088,"updated_by_user_id":107958,"qty_requested":82.05}' "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/lines"
client["inventory/transfers/{{transfer_id}}/lines"]
  .put(my_inventory_transfer_line_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Delete an inventory transfer line

DELETE /api/inventory/transfers/{{transfer_id}}/lines/{{line_id}}

Inventory Transfer Lines can be deleted by sending a DELETE request to the inventory transfer line's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/lines/{{line_id}}"
client["inventory/transfers/{{transfer_id}}/lines/{{line_id}}"]
  .delete.status
Response:
200
200

Delete inventory transfer lines in bulk

DELETE /api/inventory/transfers/{{transfer_id}}/lines/{{line_id}}

Inventory Transfer Lines can be deleted in bulk by sending a DELETE request with transfer line ids to the to the transfer lines resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/lines/{{line_id}}"
client["inventory/transfers/{{transfer_id}}/lines/{{line_id}}"]
  .delete.status
Response:
200
200

Search inventory transfer lines with items

GET /api/inventory/transfers/{{transfer_id}}/transfer_lines

This endpoint exposes Inventory Transfer Lines with embedded items for each line.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/transfer_lines/?per_page=1"
client["inventory/transfers/{{transfer_id}}/transfer_lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 103427,
      "transfer_id": 104077,
      "item_id": 107546,
      "qty_shipped": 61.69,
      "qty_received": 19.95,
      "qty_lost": 26.72,
      "unit_cost": 90.71,
      "qty_discrepant": 81.44,
      "qty_over": 88.95,
      "qty_short": 71.98,
      "created_by_user_id": 108741,
      "updated_by_user_id": 109327,
      "qty_requested": 42.13
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>104911,
    "transfer_id"=>109745,
    "item_id"=>108429,
    "qty_shipped"=>90.81,
    "qty_received"=>42.0,
    "qty_lost"=>66.34,
    "unit_cost"=>77.02,
    "qty_discrepant"=>34.26,
    "qty_over"=>54.42,
    "qty_short"=>48.86,
    "created_by_user_id"=>101890,
    "updated_by_user_id"=>107683,
    "qty_requested"=>25.47},
   "..."]}

Update inventory transfer lines from shipment

PUT /api/inventory/transfers/{{transfer_id}}/transfer_lines

Inventory Transfer Lines can be updated in bulk by PUTting a specifically crafted collection where keys are transfer lines ids, and values are updates.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"transfer_lines":{"123":"{ 'qty_received' => 5 }","321":"{ 'qty_received' => 2 }"},"column_name":"qty_received","source_column_name":"qty_shipped"}' "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/transfer_lines"
client["inventory/transfers/{{transfer_id}}/transfer_lines"]
  .post({ "transfer_lines"=>{ "123"=>{"qty_received"=>5}, "321"=>{"qty_received"=>6}, } })
  .get.body
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Search inventory transfer events

GET /api/inventory/transfers/{{transfer_id}}/events

Inventory Transfer Events can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/events/?per_page=1"
client["inventory/transfers/{{transfer_id}}/events"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 108573,
      "transfer_id": 105081,
      "created_by_user_id": 109645,
      "updated_by_user_id": 106431,
      "local_created_at": "2013-08-05T07:22:24-04:00",
      "local_updated_at": "2013-08-05T07:22:38-04:00",
      "total_delta_qty_shipped": 75.74,
      "total_delta_qty_received": 24.2,
      "total_delta_qty_lost": 82.46,
      "total_qty_created": 97.76,
      "total_qty_added": 30.88
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>103877,
    "transfer_id"=>100327,
    "created_by_user_id"=>106651,
    "updated_by_user_id"=>104105,
    "local_created_at"=>"2013-08-05T07:22:24-04:00",
    "local_updated_at"=>"2013-08-05T07:22:38-04:00",
    "total_delta_qty_shipped"=>13.04,
    "total_delta_qty_received"=>93.5,
    "total_delta_qty_lost"=>88.11,
    "total_qty_created"=>96.63,
    "total_qty_added"=>69.48},
   "..."]}

Get an inventory transfer event

GET /api/inventory/transfers/{{transfer_id}}/events/{{event_id}}

Inventory Transfer Events can be fetched by GETting the inventory transfer event's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/events/{{event_id}}"
client["inventory/transfers/{{transfer_id}}/events/{{event_id}}"]
  .get.body
Response:
{
  "id": 102111,
  "transfer_id": 109356,
  "created_by_user_id": 108259,
  "updated_by_user_id": 102885,
  "local_created_at": "2013-08-05T07:22:24-04:00",
  "local_updated_at": "2013-08-05T07:22:38-04:00",
  "total_delta_qty_shipped": 23.28,
  "total_delta_qty_received": 79.01,
  "total_delta_qty_lost": 52.52,
  "total_qty_created": 16.21,
  "total_qty_added": 18.38
}
{"id"=>104456,
 "transfer_id"=>108525,
 "created_by_user_id"=>102258,
 "updated_by_user_id"=>108357,
 "local_created_at"=>"2013-08-05T07:22:24-04:00",
 "local_updated_at"=>"2013-08-05T07:22:38-04:00",
 "total_delta_qty_shipped"=>94.98,
 "total_delta_qty_received"=>92.41,
 "total_delta_qty_lost"=>73.33,
 "total_qty_created"=>18.95,
 "total_qty_added"=>57.99}

Search inventory transfer event lines

GET /api/inventory/transfers/{{transfer_id}}/event_lines

Inventory Transfer Event Lines can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/event_lines/?per_page=1"
client["inventory/transfers/{{transfer_id}}/event_lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 101322,
      "event_id": 102129,
      "transfer_line_id": 104985,
      "delta_qty_shipped": 81.16,
      "delta_qty_received": 97.58,
      "delta_qty_lost": 19.5,
      "adjustment_reason_id": 107907,
      "adjustment_id": 102821,
      "local_created_at": "2013-08-05T07:22:24-04:00",
      "local_updated_at": "2013-08-05T07:22:38-04:00",
      "qty_created": 70.1,
      "qty_added": 25.54
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>109680,
    "event_id"=>106765,
    "transfer_line_id"=>108609,
    "delta_qty_shipped"=>86.44,
    "delta_qty_received"=>50.13,
    "delta_qty_lost"=>99.12,
    "adjustment_reason_id"=>107709,
    "adjustment_id"=>107526,
    "local_created_at"=>"2013-08-05T07:22:24-04:00",
    "local_updated_at"=>"2013-08-05T07:22:38-04:00",
    "qty_created"=>88.84,
    "qty_added"=>16.87},
   "..."]}

Get an inventory transfer event line

GET /api/inventory/transfers/{{transfer_id}}/event_lines/{{line_id}}

Inventory Transfer Event Lines can be fetched by GETting the inventory transfer event line's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/event_lines/{{line_id}}"
client["inventory/transfers/{{transfer_id}}/event_lines/{{line_id}}"]
  .get.body
Response:
{
  "id": 103872,
  "event_id": 105604,
  "transfer_line_id": 103550,
  "delta_qty_shipped": 82.03,
  "delta_qty_received": 46.57,
  "delta_qty_lost": 94.41,
  "adjustment_reason_id": 102486,
  "adjustment_id": 101596,
  "local_created_at": "2013-08-05T07:22:24-04:00",
  "local_updated_at": "2013-08-05T07:22:38-04:00",
  "qty_created": 76.51,
  "qty_added": 25.31
}
{"id"=>108671,
 "event_id"=>102894,
 "transfer_line_id"=>100026,
 "delta_qty_shipped"=>82.92,
 "delta_qty_received"=>56.73,
 "delta_qty_lost"=>41.8,
 "adjustment_reason_id"=>105974,
 "adjustment_id"=>104382,
 "local_created_at"=>"2013-08-05T07:22:24-04:00",
 "local_updated_at"=>"2013-08-05T07:22:38-04:00",
 "qty_created"=>18.89,
 "qty_added"=>45.78}

Search inventory transfer event lines of an event

GET /api/inventory/transfers/{{transfer_id}}/events/{{event_id}}/lines

Inventory Transfer Event Lines can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/events/{{event_id}}/lines/?per_page=1"
client["inventory/transfers/{{transfer_id}}/events/{{event_id}}/lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 106297,
      "event_id": 103153,
      "transfer_line_id": 107191,
      "delta_qty_shipped": 24.36,
      "delta_qty_received": 31.85,
      "delta_qty_lost": 95.2,
      "adjustment_reason_id": 104470,
      "adjustment_id": 106915,
      "local_created_at": "2013-08-05T07:22:24-04:00",
      "local_updated_at": "2013-08-05T07:22:38-04:00",
      "qty_created": 93.63,
      "qty_added": 11.17
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>108608,
    "event_id"=>101837,
    "transfer_line_id"=>103306,
    "delta_qty_shipped"=>65.4,
    "delta_qty_received"=>90.78,
    "delta_qty_lost"=>31.15,
    "adjustment_reason_id"=>107262,
    "adjustment_id"=>104197,
    "local_created_at"=>"2013-08-05T07:22:24-04:00",
    "local_updated_at"=>"2013-08-05T07:22:38-04:00",
    "qty_created"=>49.36,
    "qty_added"=>86.19},
   "..."]}

Get an inventory transfer event line of an event

GET /api/inventory/transfers/{{transfer_id}}/events/{{event_id}}/lines/{{line_id}}

Inventory Transfer Event Lines can be fetched by GETting the inventory transfer event line's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/events/{{event_id}}/lines/{{line_id}}"
client["inventory/transfers/{{transfer_id}}/events/{{event_id}}/lines/{{line_id}}"]
  .get.body
Response:
{
  "id": 101489,
  "event_id": 103475,
  "transfer_line_id": 101429,
  "delta_qty_shipped": 27.97,
  "delta_qty_received": 36.23,
  "delta_qty_lost": 10.01,
  "adjustment_reason_id": 105951,
  "adjustment_id": 104236,
  "local_created_at": "2013-08-05T07:22:24-04:00",
  "local_updated_at": "2013-08-05T07:22:38-04:00",
  "qty_created": 45.89,
  "qty_added": 79.8
}
{"id"=>109703,
 "event_id"=>105890,
 "transfer_line_id"=>105875,
 "delta_qty_shipped"=>89.47,
 "delta_qty_received"=>31.08,
 "delta_qty_lost"=>21.39,
 "adjustment_reason_id"=>104351,
 "adjustment_id"=>103654,
 "local_created_at"=>"2013-08-05T07:22:24-04:00",
 "local_updated_at"=>"2013-08-05T07:22:38-04:00",
 "qty_created"=>69.02,
 "qty_added"=>52.09}

Create an inventory transfer shipment

POST /api/inventory/transfers/{{transfer_id}}/shipments

Inventory Transfers Shipments are created by POSTing a inventory transfer shipment record containing the required attributes to the shipments resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d 'TBD' "https://{{subdomain}}.retail.heartland.us/api/inventory/transfers/{{transfer_id}}/shipments"
client["inventory/transfers/{{transfer_id}}/shipments"]
  .post(TBD)
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/inventory/transfers/188013/shipments/164724

Sales

Sales APIs expose the ability to manage customers, sales orders, and promotions as well as exposing APIs to retrieve information about sales tickets, cash paid transactions, and sales history data.

Tickets

A sales ticket is a record that a POS sale, return, or exchange transaction.

Sales ticket APIs expose the ability to create, retrieve, update, and search for sales tickets. The sales ticket APIs make use of the sales ticket record.

In order to complete a ticket it must have payments sufficient to cover its balance.

Sales Ticket

id:
integer (auto-generated)
metadata:
text
type:
text (required)
Accepted values: Ticket, Return
customer_id:
integer
source_location_id:
integer
sales_rep:
text
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

public_id:
text
station_id:
integer
parent_transaction_id:
integer
recalculate:
boolean

When set to false, we do not calculate and apply any promotion, coupon, or tax rules

status:
text

Current status of the transaction

Accepted values: incomplete, complete, void
total:
numeric(10,2) (computed)
created_by_user_id:
integer (auto-generated)

The user who created this record

custom:
hash

An object that acts as a container for custom values defined in Heartland Retail as well as any arbitrary values stored by integrations or custom applications.

local_created_at:
DateTime

The date and time that this ticket was created with the location timezone applied

local_updated_at:
DateTime

The date and time that this ticket was last updated with the location timezone applied

metadata_private:
text (computed)

Internal store for custom data.

updated_by_user_id:
integer (auto-generated)

The most recent user who updated this record

completed_at:
DateTime

The date and time that this ticket was completed

affect_inventory:
boolean

Whether or not the ticket should affect inventory on completion. Must be set during ticket creation

coupon_id:
integer

ID of coupon to apply to the ticket

sales_rep_ids:
array<id>

An array of user IDs to be used as sales reps for this ticket

Sales ItemLine

id:
integer (auto-generated)
type:
text (required)
sales_transaction_id:
integer
description:
text
value:
numeric(10,2)
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

qty:
numeric(10,2)
adjusted_unit_price:
numeric(10,2)
unit_cost:
numeric(10,2)
original_unit_price:
numeric(10,2)
item_id:
integer (required)
action_id:
text
shipping_line_id:
integer
item_line_id:
integer
sales_transaction_line_id:
integer
address_id:
integer
shipping_method_id:
integer
manually_added:
boolean
reason_codes:
text
lookup_id:
integer
customer_id:
integer
loyalty_points:
integer
order_line_id:
integer
address_revision_id:
integer
order_discount_id:
integer
gift_card_id:
integer
tax_rule_id:
integer

Sales Transaction Payment

A payment for use in a sales transaction

id:
integer (auto-generated)
sales_transaction_id:
integer (auto-generated)
payment_id:
integer (auto-generated)
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

sales_order_payment_id:
integer (auto-generated)
status:
text

The payment's current status indicating whether or not it has been captured.

Accepted values: pending, complete, voided
amount:
numeric(10,2) (required)

The amount of money to pay.

description:
text

The user friendly payment description

gateway_info:
object (computed)

Object containing the details received from the payment gateway

gateway_id:
integer (auto-generated)
details:
object (auto-generated)
type:
text (required)

The payment type

Accepted values: Payments::CashPayment, Payments::CreditCardPayment, Payments::CheckPayment, Payments::CustomPayment, Payments::ExternalPayment, Payments::GiftCardPayment
credit_card_id:
integer (auto-generated)
deposit_payment_id:
integer (auto-generated)
signature_required:
boolean (computed)
cardholder_name:
text (computed)

The display name for the credit card holder on credit card payments

active:
boolean
deposit:
boolean

Set to true to capture payment immediately, when set to false it captures payment on transaction complete

amount_tendered:
numeric(10,2) (required) (computed)

The amount of money that has been paid

gateway_connection_id:
integer (auto-generated)
reference_id:
text (auto-generated)

Payment reference ID

gateway_type:
text (computed)
manually_reconciled:
boolean (computed)
Sales Ticket Example:
{
  "id": 107451,
  "type": "Ticket",
  "customer_id": 101941,
  "source_location_id": 105855,
  "station_id": 108578,
  "parent_transaction_id": 105696,
  "status": "incomplete",
  "total": 38.9,
  "created_by_user_id": 108051,
  "local_created_at": "2013-08-05T09:22:24-04:00",
  "local_updated_at": "2013-08-05T09:22:24-04:00",
  "updated_by_user_id": 108730,
  "completed_at": "2013-08-05T09:22:24-04:00",
  "coupon_id": 108194,
  "sales_rep_ids": [
    100002,
    100003
  ]
}
Sales ItemLine Example:
{
  "id": 109908,
  "type": "ItemLine",
  "sales_transaction_id": 103163,
  "value": 31.87,
  "qty": 4.0,
  "adjusted_unit_price": 42.04,
  "unit_cost": 74.64,
  "original_unit_price": 43.22,
  "item_id": 100001,
  "shipping_line_id": 101606,
  "item_line_id": 107509,
  "sales_transaction_line_id": 104011,
  "address_id": 101235,
  "shipping_method_id": 102120,
  "lookup_id": 108560,
  "customer_id": 109354,
  "loyalty_points": 109872,
  "order_line_id": 109523,
  "address_revision_id": 108975,
  "order_discount_id": 108911,
  "gift_card_id": 106099,
  "tax_rule_id": 104421
}
Sales Transaction Payment Example:
{
  "id": 109661,
  "sales_transaction_id": 100445,
  "payment_id": 107032,
  "sales_order_payment_id": 105706,
  "amount": 14.26,
  "description": "Cash",
  "gateway_id": 105671,
  "type": "Payments::CashPayment",
  "credit_card_id": 105046,
  "deposit_payment_id": 103439,
  "cardholder_name": "John Doe",
  "active": true,
  "amount_tendered": 35.49,
  "gateway_connection_id": 103483
}

Create a ticket

POST /api/sales/tickets

Creating a sales ticket requires a station_id. The station_id is used to determine which location's inventory to alter as a result of the sales ticket.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"type":"Ticket"}' "https://{{subdomain}}.retail.heartland.us/api/sales/tickets"
client["sales/tickets"]
  .post({"type"=>"Ticket"})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/sales/tickets/159871

Retrieve a ticket

GET /api/sales/tickets/{{ticket_id}}

A sales ticket is retrieved using its unique identifiers. This identifier can be found by searching for tickets, from references in other records, or from the Location header returned in response to the creation of a new sales tickets.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/sales/tickets/{{ticket_id}}"
client["sales/tickets/{{ticket_id}}"].get.body
Response:
{
  "id": 101737,
  "type": "Ticket",
  "customer_id": 101255,
  "source_location_id": 102068,
  "station_id": 100406,
  "parent_transaction_id": 103564,
  "status": "incomplete",
  "total": 93.65,
  "created_by_user_id": 100906,
  "local_created_at": "2013-08-05T09:22:24-04:00",
  "local_updated_at": "2013-08-05T09:22:24-04:00",
  "updated_by_user_id": 101753,
  "completed_at": "2013-08-05T09:22:24-04:00",
  "coupon_id": 106576,
  "sales_rep_ids": [
    100002,
    100003
  ]
}
{"id"=>103379,
 "type"=>"Ticket",
 "customer_id"=>109040,
 "source_location_id"=>109460,
 "station_id"=>101613,
 "parent_transaction_id"=>108142,
 "status"=>"incomplete",
 "total"=>22.89,
 "created_by_user_id"=>106877,
 "local_created_at"=>"2013-08-05T09:22:24-04:00",
 "local_updated_at"=>"2013-08-05T09:22:24-04:00",
 "updated_by_user_id"=>105773,
 "completed_at"=>"2013-08-05T09:22:24-04:00",
 "coupon_id"=>109683,
 "sales_rep_ids"=>[100002, 100003]}

Retrieve a ticket's lines

GET /api/sales/tickets/{{ticket_id}}/lines

A sales ticket has zero or more associated lines. Lines can be any of the following types:

  • ItemLine - The sale or return of an item
  • TaxLine - Sales tax charge or refund
  • DiscountLine - Discount to another type of line
  • ShippingLine - Shipping charge, discount or refund

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/sales/tickets/{{ticket_id}}/lines/?per_page=1"
client["sales/tickets/{{ticket_id}}/lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 102640,
      "type": "ItemLine",
      "sales_transaction_id": 102622,
      "value": 77.05,
      "qty": 4.0,
      "adjusted_unit_price": 62.08,
      "unit_cost": 86.4,
      "original_unit_price": 74.26,
      "item_id": 100001,
      "shipping_line_id": 106563,
      "item_line_id": 106808,
      "sales_transaction_line_id": 108596,
      "address_id": 104273,
      "shipping_method_id": 104950,
      "lookup_id": 101607,
      "customer_id": 107882,
      "loyalty_points": 107174,
      "order_line_id": 105477,
      "address_revision_id": 100151,
      "order_discount_id": 102100,
      "gift_card_id": 100563,
      "tax_rule_id": 107957
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>101072,
    "type"=>"ItemLine",
    "sales_transaction_id"=>109608,
    "value"=>54.79,
    "qty"=>4.0,
    "adjusted_unit_price"=>48.82,
    "unit_cost"=>57.7,
    "original_unit_price"=>34.45,
    "item_id"=>100001,
    "shipping_line_id"=>106511,
    "item_line_id"=>106299,
    "sales_transaction_line_id"=>109680,
    "address_id"=>103680,
    "shipping_method_id"=>104479,
    "lookup_id"=>100239,
    "customer_id"=>109644,
    "loyalty_points"=>109547,
    "order_line_id"=>102172,
    "address_revision_id"=>107650,
    "order_discount_id"=>109811,
    "gift_card_id"=>105778,
    "tax_rule_id"=>108903},
   "..."]}

Add an item line to a ticket

POST /api/sales/tickets/{{ticket_id}}/item_lines

An item line indicates the sale or return of an item. This method can also be used to modify the quantity of an existing item line. Tickets are limited to 500 item lines per ticket. Attempting to add more than that will return an error.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"type":"ItemLine","item_id":100001}' "https://{{subdomain}}.retail.heartland.us/api/sales/tickets/{{ticket_id}}/item_lines"
client["sales/tickets/{{ticket_id}}/item_lines"]
  .post({"type"=>"ItemLine", "item_id"=>100001})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/sales/tickets/134253/item_lines/119327

Add a payment to a ticket

POST /api/sales/tickets/{{ticket_id}}/payments

Adds a payment with the given amount and method to the ticket. See the payment data type for more details.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"amount":89.52,"type":"Payments::CashPayment","amount_tendered":64.48}' "https://{{subdomain}}.retail.heartland.us/api/sales/tickets/{{ticket_id}}/payments"
client["sales/tickets/{{ticket_id}}/payments"]
  .post({"amount"=>15.39, "type"=>"Payments::CashPayment", "amount_tendered"=>63.89})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/sales/tickets/195530/payments/172877

Add a coupon to a ticket

PUT /api/sales/tickets/{{ticket_id}}

Adds a coupon to the ticket. See the coupon data type for more details.

Request:
client[:sales][:tickets][403992].put(coupon_id: 1).status
curl -v -H "Authorization: Bearer {{Token}}" -X PUT -H "Content-Type: application/json" -d '{"coupon_id":"1"}' "https://{{subdomain}}.retail.heartland.us/api/sales/tickets/403993"
Response:
200 # success!
# ...
# Status: 200 OK
# ...

# No Content.

Update a ticket

PUT /api/sales/tickets/{{ticket_id}}

A sales ticket can be modified by sending a PUT request to the record's unique URL. The contents of the request must contain a valid subset of sales ticket record attribute. You can pass completed_at to set the specific completion date and time for the ticket (for example in the past). A sales ticket also accepts the affect_inventory flag that if set to false can be used to not perform inventory adjustment on ticket completion.

Request:
client[:sales][:tickets][403992].put(status: 'void').status
curl -v -H "Authorization: Bearer {{Token}}" -X PUT -H "Content-Type: application/json" -d '{"status":"void"}' "https://{{subdomain}}.retail.heartland.us/api/sales/tickets/403993"
Response:
200 # success!
# ...
# Status: 200 OK
# ...

# No Content.

Search tickets

GET /api/sales/tickets

Sales tickets can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/sales/tickets/?per_page=1"
client["sales/tickets"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 106332,
      "type": "Ticket",
      "customer_id": 107260,
      "source_location_id": 106327,
      "station_id": 108326,
      "parent_transaction_id": 104381,
      "status": "incomplete",
      "total": 66.03,
      "created_by_user_id": 100282,
      "local_created_at": "2013-08-05T09:22:24-04:00",
      "local_updated_at": "2013-08-05T09:22:24-04:00",
      "updated_by_user_id": 105996,
      "completed_at": "2013-08-05T09:22:24-04:00",
      "coupon_id": 105170,
      "sales_rep_ids": [
        100002,
        100003
      ]
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>106809,
    "type"=>"Ticket",
    "customer_id"=>105855,
    "source_location_id"=>104204,
    "station_id"=>102603,
    "parent_transaction_id"=>101268,
    "status"=>"incomplete",
    "total"=>74.63,
    "created_by_user_id"=>101019,
    "local_created_at"=>"2013-08-05T09:22:24-04:00",
    "local_updated_at"=>"2013-08-05T09:22:24-04:00",
    "updated_by_user_id"=>102627,
    "completed_at"=>"2013-08-05T09:22:24-04:00",
    "coupon_id"=>108486,
    "sales_rep_ids"=>[100002, 100003]},
   "..."]}

Orders

A sales order is a record that documents an order placed by a customer. Sales orders are typically used for future pickup of items, special orders, or ecommerce orders. This record holds the items, sales prices, shipping method/fee, and the payment information for the order. (For POS sales transactions, see Tickets)

A sales order has four statuses; Pending, Open, Canceled, Closed. While an order is pending, no inventory for items on the order has been committed. Before an order can be moved to the Open status, you must have sufficent payments added to the order as well as a valid shipping/billing address. You can cancel an order as long as it does not have any completed invoices against it. Once all items on the order have been successfully invoiced, the order is automatically transitioned to Closed.

Sales order APIs expose the ability to create, retrieve, update, and search for sales orders. The sales order APIs make use of the sales order record

Sales Order

id:
integer (auto-generated)
customer_id:
integer (required)
station_id:
integer (required)
source_location_id:
integer (required)
status:
text

The status of the order. Valid options: "pending", "open", "canceled", "closed"

sales_rep:
text
recalculate:
boolean
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

shipping_method_id:
integer
shipping_charge:
numeric(10,2)
billing_address_id:
integer
shipping_address_id:
integer
gift_cards:
array

List of gift cards to add to the order

Sales OrderLine

id:
integer (auto-generated)
item_id:
integer (required)
description:
text
qty:
numeric(10,2) (required)
adjusted_unit_price:
numeric(10,2)
original_unit_price:
numeric(10,2)
unit_cost:
numeric(10,2)
total_tax:
numeric(10,2)

Extended tax amount to collect for the line

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

qty_open:
numeric(10,2)
qty_invoiced:
numeric(10,2)
order_id:
integer
ship_from_location_id:
integer
discounts:
array

List of Sales OrderLineDiscount to be applied

Sales OrderPayment

A payment for use in a sales order

id:
integer (auto-generated)
type:
text (required)

The payment type. Valid options: "CashPayment", "CreditCardPayment", "CheckPayment", "CustomPayment", "ExternalPayment"

status:
text

The payment's current status indicating whether or not it has been captured.

deposit:
boolean (required)

If true, the funds are captured immediately. Otherwise funds are captured when fulfilling the order.

amount:
numeric(10,2) (required)

The amount of money to pay.

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

Sales Order Example:
{
  "id": 102919,
  "customer_id": 106930,
  "station_id": 106221,
  "source_location_id": 100502,
  "status": "pending",
  "shipping_method_id": 102218,
  "shipping_charge": 53.75,
  "billing_address_id": 101830,
  "shipping_address_id": 109513,
  "gift_cards": [
    {
      "number": "GIFT-CARD-NUMBER",
      "value": 12.34
    }
  ]
}
Sales OrderLine Example:
{
  "id": 106418,
  "item_id": 104158,
  "qty": 2.0,
  "adjusted_unit_price": 35.85,
  "original_unit_price": 33.08,
  "unit_cost": 48.59,
  "total_tax": 12.5,
  "qty_open": 84.72,
  "qty_invoiced": 52.1,
  "order_id": 104292,
  "ship_from_location_id": 106887,
  "discounts": [
    {
      "description": "Discount",
      "amount": 12.34
    }
  ]
}
Sales OrderPayment Example:
{
  "id": 104973,
  "type": "CashPayment",
  "deposit": true,
  "amount": 56.63
}

Create a sales order

POST /api/sales/orders

Creating sales orders requires you to have a customer_id, a station_id and a source_location_id. The customer_id is used to determine which customers is placing the order. And the source location_id is which location will be credited for the sale once the order has been invoiced.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"customer_id":109174,"station_id":105110,"source_location_id":103446}' "https://{{subdomain}}.retail.heartland.us/api/sales/orders"
client["sales/orders"]
  .post({"customer_id"=>106323, "station_id"=>107876, "source_location_id"=>103715})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/sales/orders/188773

Retrieve a sales order

GET /api/sales/orders/{{order_id}}

A sales order is retrieved using its unique identifiers. This identifier can be found by searching for orders, from references in other records, or from the Location header returned in response to the creation of a new sales order.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/sales/orders/{{order_id}}"
client["sales/orders/{{order_id}}"].get.body
Response:
{
  "id": 103890,
  "customer_id": 100950,
  "station_id": 102039,
  "source_location_id": 102023,
  "status": "pending",
  "shipping_method_id": 106394,
  "shipping_charge": 18.57,
  "billing_address_id": 105875,
  "shipping_address_id": 103805,
  "gift_cards": [
    {
      "number": "GIFT-CARD-NUMBER",
      "value": 12.34
    }
  ]
}
{"id"=>105576,
 "customer_id"=>106494,
 "station_id"=>102418,
 "source_location_id"=>103316,
 "status"=>"pending",
 "shipping_method_id"=>109647,
 "shipping_charge"=>97.02,
 "billing_address_id"=>109642,
 "shipping_address_id"=>101147,
 "gift_cards"=>[{"number"=>"GIFT-CARD-NUMBER", "value"=>12.34}]}

Add an item to a sales order

POST /api/sales/orders/{{order_id}}/lines

An item line indicates the sale or return of an item. This method can also be used to modify the quantity of an existing item line.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"item_id":100438,"qty":2.0}' "https://{{subdomain}}.retail.heartland.us/api/sales/orders/{{order_id}}/lines"
client["sales/orders/{{order_id}}/lines"]
  .post({"item_id"=>100579, "qty"=>2.0})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/sales/orders/138895/lines/198575

Add an item to a sales order with discounts

POST /api/sales/orders/{{order_id}}/lines

It's possible to embed order line discounts when adding an item to an order. This will make the price of the line drop the amount specified by the sum of discounts.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"item_id":102708,"qty":2.0,"discounts":[{"description":"Discount","amount":12.34}]}' "https://{{subdomain}}.retail.heartland.us/api/sales/orders/{{order_id}}/lines"
client["sales/orders/{{order_id}}/lines"]
  .post({"item_id"=>102732, "qty"=>2.0, "discounts"=>[{"description"=>"Discount", "amount"=>12.34}]})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/sales/orders/134172/lines/165013

Retrieve a sales orders's lines

GET /api/sales/orders/{{order_id}}/lines

A sales order has zero or more associated item lines.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/sales/orders/{{order_id}}/lines/?per_page=1"
client["sales/orders/{{order_id}}/lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 105908,
      "item_id": 102499,
      "qty": 2.0,
      "adjusted_unit_price": 29.17,
      "original_unit_price": 48.77,
      "unit_cost": 23.47,
      "total_tax": 12.5,
      "qty_open": 73.41,
      "qty_invoiced": 52.22,
      "order_id": 106179,
      "ship_from_location_id": 107233,
      "discounts": [
        {
          "description": "Discount",
          "amount": 12.34
        }
      ]
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>108726,
    "item_id"=>105081,
    "qty"=>2.0,
    "adjusted_unit_price"=>71.95,
    "original_unit_price"=>97.53,
    "unit_cost"=>82.1,
    "total_tax"=>12.5,
    "qty_open"=>45.71,
    "qty_invoiced"=>27.92,
    "order_id"=>109765,
    "ship_from_location_id"=>100395,
    "discounts"=>[{"description"=>"Discount", "amount"=>12.34}]},
   "..."]}

Distribute an item line on a sales order

PUT /api/sales/orders/{{order_id}}/lines/{{line_id}}

Distributing an item line designates the location that item will be fulfilled from. You must specify the ship_from_location_id

Request:
client[:sales][:orders][403992][:lines][135225].put(ship_from_location_id: '100002')
curl -v -H "Authorization: Bearer {{Token}}" -X PUT -H "Content-Type: application/json" -d '{"ship_from_location_id":"100002"}' "https://{{subdomain}}.retail.heartland.us/api/sales/orders/403993/lines/135225"
Response:
200 # success!
# ...
# Status: 200 OK
# ...

Add shipping to a sales order

PUT /api/sales/orders/{{order_id}}/

A sales order can record a shipping method and amount to charge for the shipment. To add a shipping method you need the shipping_method_id and the shipping_charge_amount.

Request:
client[:sales][:orders][403992].put(shipping_method_id: 100003, shipping_charge: 22.00)
curl -v -H "Authorization: Bearer {{Token}}" -X PUT -H "Content-Type: application/json" -d '{"shipping_charge":100002,"shipping_method_id":22.00}' "https://{{subdomain}}.retail.heartland.us/api/sales/orders/403993/"
Response:
200 # success!
# ...
# Status: 200 OK
# ...

Add a payment to a sales order

POST /api/sales/orders/{{order_id}}/payments

Adds a payment with the given amount and method to the order. See the payment data type for more details. A sales order stores the payment information to be captured either immediately or upon shipment of the order (fulling the invoice). There must be sufficent payments added to the order before it can be moved to the Open status.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"type":"CashPayment","deposit":true,"amount":23.27}' "https://{{subdomain}}.retail.heartland.us/api/sales/orders/{{order_id}}/payments"
client["sales/orders/{{order_id}}/payments"]
  .post({"type"=>"CashPayment", "deposit"=>true, "amount"=>17.18})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/sales/orders/165414/payments/172187

Update a sales order

PUT /api/sales/orders/{{order_id}}

A sales order can be modified by sending a PUT request to the record's unique URL. The contents of the request must contain a valid subset of sales order record attribute

Request:
client[:sales][:orders][403992].put(status: 'open').status
curl -H "Authorization: Bearer {{Token}}" -X PUT -H "Content-Type: application/json" -d '{"status":"open"}' "https://{{subdomain}}.retail.heartland.us/api/sales/orders/403993"
Response:
200 # success!
# ...
# Status: 200 OK
# ...

Search sales orders

GET /api/sales/orders

Sales orders can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/sales/orders/?per_page=1"
client["sales/orders"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 101997,
      "customer_id": 101991,
      "station_id": 101469,
      "source_location_id": 108922,
      "status": "pending",
      "shipping_method_id": 109662,
      "shipping_charge": 81.06,
      "billing_address_id": 100872,
      "shipping_address_id": 106258,
      "gift_cards": [
        {
          "number": "GIFT-CARD-NUMBER",
          "value": 12.34
        }
      ]
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>100262,
    "customer_id"=>100643,
    "station_id"=>106693,
    "source_location_id"=>104366,
    "status"=>"pending",
    "shipping_method_id"=>102033,
    "shipping_charge"=>48.02,
    "billing_address_id"=>100397,
    "shipping_address_id"=>102905,
    "gift_cards"=>[{"number"=>"GIFT-CARD-NUMBER", "value"=>12.34}]},
   "..."]}

Invoices

A sales invoice is a record that documents the full or partial fulfillment of a sales order. (For POS sales transactions, see Tickets)

Sales invoice APIs expose the ability to create, retrieve, update, and search for sales invoices. The sales invoice APIs make use of the sales invoice record

Sales Invoice

id:
integer (auto-generated)
metadata:
text
type:
text (required)
Accepted values: Invoice
customer_id:
integer
source_location_id:
integer
sales_rep:
text
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

public_id:
text
station_id:
integer
recalculate:
boolean

When set to false, we do not calculate and apply any promotion, coupon, or tax rules

status:
text

Current status of the transaction

Accepted values: incomplete, complete, void
order_id:
integer
custom:
hash

An object that acts as a container for custom values defined in Heartland Retail as well as any arbitrary values stored by integrations or custom applications.

completed_at:
DateTime

The date and time that this ticket was completed

affect_inventory:
boolean

Whether or not the ticket should affect inventory on completion. Must be set during ticket creation

billing_address_id:
integer

ID of customer address used for billing

Sales Invoice Example:
{
  "id": 104734,
  "type": "Invoice",
  "customer_id": 103394,
  "source_location_id": 101092,
  "station_id": 105226,
  "status": "incomplete",
  "order_id": 103857,
  "completed_at": "2013-08-05T09:22:24-04:00",
  "billing_address_id": 103096
}

Create an invoice

POST /api/sales/invoices

Creating sales invoices requires you to have a station id and a sales order id. The station id is used to determine which location's inventory to alter as a result of the sales invoice and the order id is used to determine what should be invoiced and which customer should receive it.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"type":"Invoice"}' "https://{{subdomain}}.retail.heartland.us/api/sales/invoices"
client["sales/invoices"]
  .post({"type"=>"Invoice"})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/sales/invoices/11480

Retrieve an invoice

GET /api/sales/invoices/{{invoice_id}}

A sales invoice is retrieved using its unique identifiers. This identifier can be found by searching for invoices, from references in other records, or from the Location header returned in response to the creation of a new sales invoices.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/sales/invoices/{{invoice_id}}"
client["sales/invoices/{{invoice_id}}"].get.body
Response:
{
  "id": 101502,
  "type": "Invoice",
  "customer_id": 101111,
  "source_location_id": 105486,
  "station_id": 101369,
  "status": "incomplete",
  "order_id": 100287,
  "completed_at": "2013-08-05T09:22:24-04:00",
  "billing_address_id": 100406
}
{"id"=>109721,
 "type"=>"Invoice",
 "customer_id"=>105948,
 "source_location_id"=>102544,
 "station_id"=>105476,
 "status"=>"incomplete",
 "order_id"=>106065,
 "completed_at"=>"2013-08-05T09:22:24-04:00",
 "billing_address_id"=>108774}

Retrieve an invoice's lines

GET /api/sales/invoices/{{invoice_id}}/lines

A sales invoice has zero or more associated lines. Lines can be any of the following types:

  • ItemLine - The sale or return of an item
  • TaxLine - Sales tax charge or refund
  • DiscountLine - Discount to another type of line
  • ShippingLine - Shipping charge, discount or refund

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/sales/invoices/{{invoice_id}}/lines/?per_page=1"
client["sales/invoices/{{invoice_id}}/lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 109292,
      "type": "ItemLine",
      "sales_transaction_id": 100396,
      "value": 39.18,
      "qty": 4.0,
      "adjusted_unit_price": 25.37,
      "unit_cost": 71.95,
      "original_unit_price": 65.23,
      "item_id": 100001,
      "shipping_line_id": 106041,
      "item_line_id": 102039,
      "sales_transaction_line_id": 102381,
      "address_id": 102174,
      "shipping_method_id": 104295,
      "lookup_id": 101995,
      "customer_id": 106886,
      "loyalty_points": 100753,
      "order_line_id": 107926,
      "address_revision_id": 109196,
      "order_discount_id": 109208,
      "gift_card_id": 108571,
      "tax_rule_id": 100447
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>104827,
    "type"=>"ItemLine",
    "sales_transaction_id"=>103823,
    "value"=>99.51,
    "qty"=>4.0,
    "adjusted_unit_price"=>37.65,
    "unit_cost"=>88.09,
    "original_unit_price"=>82.0,
    "item_id"=>100001,
    "shipping_line_id"=>107357,
    "item_line_id"=>100748,
    "sales_transaction_line_id"=>103055,
    "address_id"=>103794,
    "shipping_method_id"=>102991,
    "lookup_id"=>109918,
    "customer_id"=>102732,
    "loyalty_points"=>104089,
    "order_line_id"=>105271,
    "address_revision_id"=>100016,
    "order_discount_id"=>105383,
    "gift_card_id"=>107871,
    "tax_rule_id"=>106113},
   "..."]}

Update an invoice

PUT /api/sales/invoices/{{invoice_id}}

A sales invoice can be modified by sending a PUT request to the record's unique URL. The contents of the request must contain a valid subset of sales invoice record attribute

Request:
client[:sales][:invoices][403992].put(status: 'void').status
curl -H "Authorization: Bearer {{Token}}" -X PUT -H "Content-Type: application/json" -d '{"status":"void"}' "https://{{subdomain}}.retail.heartland.us/api/sales/invoices/403993"
Response:
200 # success!
# ...
# Status: 200 OK
# ...

# No Content.

Search invoices

GET /api/sales/invoices

Sales invoices can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/sales/invoices/?per_page=1"
client["sales/invoices"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 103292,
      "type": "Invoice",
      "customer_id": 109265,
      "source_location_id": 101188,
      "station_id": 109810,
      "status": "incomplete",
      "order_id": 109354,
      "completed_at": "2013-08-05T09:22:24-04:00",
      "billing_address_id": 104464
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>100725,
    "type"=>"Invoice",
    "customer_id"=>102418,
    "source_location_id"=>104073,
    "station_id"=>101744,
    "status"=>"incomplete",
    "order_id"=>109956,
    "completed_at"=>"2013-08-05T09:22:24-04:00",
    "billing_address_id"=>100512},
   "..."]}

Customers

Customer APIs expose the ability to create, retrieve, update, and search for customers. The customer APIs make use of the customer record.

Customer

id:
integer (auto-generated)
metadata:
text
public_id:
text

Unique ID string

first_name:
text (required)

First name

last_name:
text

Last name

email:
text

Email address

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

address_id:
integer

ID of default address record

shipping_address_id:
integer

ID of address record used for shipping

billing_address_id:
integer

ID of address record used for billing

active:
boolean

Whether or not this record is currently active and being used

custom:
object

Custom field values

customer_import_batch_id:
integer (auto-generated)

ID of batch if this record was created as part of an import batch

name:
text (computed)

Full name

address:
object

Address record for primary address

loyalty_points_balance:
integer (auto-generated)
loyalty_points_total:
integer (auto-generated)

Address

id:
integer (auto-generated)
address_id:
integer (auto-generated)
current_revision_id:
integer (auto-generated)
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

type:
text
first_name:
text
last_name:
text
line_1:
text
line_2:
text
city:
text
state:
text

If the country is set to 'US' then 2 letter state acronym required

phone:
text
country:
text

Set to 2 letter country acronym. Set value to anything other than 'US' to use a non standard state/postal_code

postal_code:
text

If the country is set to 'US' then a 5 digit ZIP or ZIP+4 code is required

custom:
object

Custom field values

Customer Example:
{
  "id": 109030,
  "first_name": "Walter",
  "last_name": "White",
  "email": "walter@example.com",
  "address_id": 107505,
  "shipping_address_id": 109690,
  "billing_address_id": 102454,
  "custom": {
    "custom1": "Custom value 1",
    "custom2": "Custom value 2"
  },
  "customer_import_batch_id": 105084,
  "name": "Walter White",
  "loyalty_points_balance": 108966,
  "loyalty_points_total": 103754
}
Address Example:
{
  "id": 100497,
  "address_id": 108800,
  "current_revision_id": 107538,
  "first_name": "Bruce",
  "last_name": "Wayne",
  "line_1": "1007 Mountain Drive",
  "city": "Gotham",
  "state": "NJ",
  "phone": "212-555-5555",
  "country": "US",
  "postal_code": "10025",
  "custom": {
    "custom1": "Custom value 1",
    "custom2": "Custom value 2"
  }
}

Create a customer

POST /api/customers

To create a customer you must specify at least a first name or a last name. Only one of those two values is actually required.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"first_name":"Walter"}' "https://{{subdomain}}.retail.heartland.us/api/customers"
client["customers"]
  .post({"first_name"=>"Walter"})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/customers/199537

Retrieve a customer

GET /api/customers/{{customer_id}}

A Customer is retrieved using its unique identifiers. This identifier can be found by searching for customers, from references in other records, or from the Location header returned in response to the creation of a new customer.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/customers/{{customer_id}}"
client["customers/{{customer_id}}"].get.body
Response:
{
  "id": 103293,
  "first_name": "Walter",
  "last_name": "White",
  "email": "walter@example.com",
  "address_id": 102597,
  "shipping_address_id": 104241,
  "billing_address_id": 104051,
  "custom": {
    "custom1": "Custom value 1",
    "custom2": "Custom value 2"
  },
  "customer_import_batch_id": 109037,
  "name": "Walter White",
  "loyalty_points_balance": 100696,
  "loyalty_points_total": 103456
}
{"id"=>105547,
 "first_name"=>"Walter",
 "last_name"=>"White",
 "email"=>"walter@example.com",
 "address_id"=>102359,
 "shipping_address_id"=>100265,
 "billing_address_id"=>107601,
 "custom"=>{"custom1"=>"Custom value 1", "custom2"=>"Custom value 2"},
 "customer_import_batch_id"=>103162,
 "name"=>"Walter White",
 "loyalty_points_balance"=>100424,
 "loyalty_points_total"=>105546}

Update a customer

PUT /api/customers/{{customer_id}}

A customer can be modified by sending a PUT request to the record's unique URL. The contents of the request must contain a valid subset of customer record attribute

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"first_name":"Walter","last_name":"White","email":"walter@example.com","address_id":101946,"shipping_address_id":103887,"billing_address_id":100790,"custom":{"custom1":"Custom value 1","custom2":"Custom value 2"},"name":"Walter White"}' "https://{{subdomain}}.retail.heartland.us/api/customers/{{customer_id}}"
client["customers/{{customer_id}}"]
  .put(my_customer_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Search customers

GET /api/customers

Customers can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/customers/?per_page=1"
client["customers"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 108657,
      "first_name": "Walter",
      "last_name": "White",
      "email": "walter@example.com",
      "address_id": 103117,
      "shipping_address_id": 102726,
      "billing_address_id": 109410,
      "custom": {
        "custom1": "Custom value 1",
        "custom2": "Custom value 2"
      },
      "customer_import_batch_id": 101377,
      "name": "Walter White",
      "loyalty_points_balance": 109782,
      "loyalty_points_total": 101860
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>103821,
    "first_name"=>"Walter",
    "last_name"=>"White",
    "email"=>"walter@example.com",
    "address_id"=>102754,
    "shipping_address_id"=>105795,
    "billing_address_id"=>105808,
    "custom"=>{"custom1"=>"Custom value 1", "custom2"=>"Custom value 2"},
    "customer_import_batch_id"=>104726,
    "name"=>"Walter White",
    "loyalty_points_balance"=>109347,
    "loyalty_points_total"=>106917},
   "..."]}

Merge customers

POST /api/customers/{{customer_id}}/merges

If you have duplicate customers and would like to combine them into one customer you can do so using the merges API. This will combine their sales history and customer data.

The base customer specified in the URL is considered the "master" customer. Include an array of slave_ids in the request body. These "slave" customers will be deleted after having their data combined into the master.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"slave_ids":[100002,100003]}' "https://{{subdomain}}.retail.heartland.us/api/customers/{{customer_id}}/merges"
client["customers/{{customer_id}}/merges"]
  .post({"slave_ids"=>[100002, 100003]})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/customers/161110/merges/18282

Create an address

POST /api/customers/{{customer_id}}/addresses

You are able to create multiple addresses per customer each with their own address_id that can be used to specify the customer's address preferences.

To assign an address to be the default, billing, or shipping address you must create or retrieve an address to get the address_id which can then be used to update the customer record. By specifying the address_id when updating the customer, you are able to set the default address. You are also able to specify a shipping_address_id or a billing_address_id to set the default specific address type for use in a sales order.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/customers/{{customer_id}}/addresses"
client["customers/{{customer_id}}/addresses"]
  .post({})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/customers/174146/addresses/195660

List customer addresses

GET /api/customers/{{customer_id}}/addresses

You can retrieve all addresses currently attached to a customer by using the customer's unique identifier. This identifier can be found by searching for customers, from references in other records, or from the Location header returned in response to the creation of a new customer.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/customers/{{customer_id}}/addresses"
client["customers/{{customer_id}}/addresses"]
  .get.body
Response:
{
  "id": 102418,
  "address_id": 108726,
  "current_revision_id": 100039,
  "first_name": "Bruce",
  "last_name": "Wayne",
  "line_1": "1007 Mountain Drive",
  "city": "Gotham",
  "state": "NJ",
  "phone": "212-555-5555",
  "country": "US",
  "postal_code": "10025",
  "custom": {
    "custom1": "Custom value 1",
    "custom2": "Custom value 2"
  }
}
{"id"=>100053,
 "address_id"=>100925,
 "current_revision_id"=>106908,
 "first_name"=>"Bruce",
 "last_name"=>"Wayne",
 "line_1"=>"1007 Mountain Drive",
 "city"=>"Gotham",
 "state"=>"NJ",
 "phone"=>"212-555-5555",
 "country"=>"US",
 "postal_code"=>"10025",
 "custom"=>{"custom1"=>"Custom value 1", "custom2"=>"Custom value 2"}}

Retrieve an address

GET /api/customers/{{customer_id}}/addresses/{{address_id}}

An address is retrieved using its unique identifier. This identifier can be found by searching for all addresses attached to a customer, from references in other records, or from the Location header returned in response to the creation of a new address.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/customers/{{customer_id}}/addresses/{{address_id}}"
client["customers/{{customer_id}}/addresses/{{address_id}}"]
  .get.body
Response:
{
  "id": 102641,
  "address_id": 101051,
  "current_revision_id": 106558,
  "first_name": "Bruce",
  "last_name": "Wayne",
  "line_1": "1007 Mountain Drive",
  "city": "Gotham",
  "state": "NJ",
  "phone": "212-555-5555",
  "country": "US",
  "postal_code": "10025",
  "custom": {
    "custom1": "Custom value 1",
    "custom2": "Custom value 2"
  }
}
{"id"=>104256,
 "address_id"=>101226,
 "current_revision_id"=>103664,
 "first_name"=>"Bruce",
 "last_name"=>"Wayne",
 "line_1"=>"1007 Mountain Drive",
 "city"=>"Gotham",
 "state"=>"NJ",
 "phone"=>"212-555-5555",
 "country"=>"US",
 "postal_code"=>"10025",
 "custom"=>{"custom1"=>"Custom value 1", "custom2"=>"Custom value 2"}}

Update an address

PUT /api/customers/{{customer_id}}/addresses/{{address_id}}

An address can be modified by sending a PUT request to the record's unique URL. The contents of the request must contain a valid subset of address record attributes

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"first_name":"Bruce","last_name":"Wayne","line_1":"1007 Mountain Drive","city":"Gotham","state":"NJ","phone":"212-555-5555","country":"US","postal_code":"10025","custom":{"custom1":"Custom value 1","custom2":"Custom value 2"}}' "https://{{subdomain}}.retail.heartland.us/api/customers/{{customer_id}}/addresses/{{address_id}}"
client["customers/{{customer_id}}/addresses/{{address_id}}"]
  .put(my_address_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Tax Jurisdictions

The tax jurisdictions resource exposes the ability to create and modify tax jurisdictions in the Heartland Retail system.

Tax Jurisdiction

id:
integer (auto-generated)

The id of the tax jurisdiction

name:
text

The name of the tax jurisdiction

type:
text

The type of the tax jurisdiction (either StateJurisdiction or PostalCodeJurisdiction)

country:
text
state:
text
postal_codes:
text

The postal codes of the tax jurisdiction (for PostalCodeJurisdiction)

tax_holiday_start:
date
tax_holiday_end:
date
Tax Jurisdiction Example:
{
  "id": 100757,
  "name": "MA",
  "type": "StateJurisdiction",
  "country": "US",
  "state": "MA"
}

Search tax jurisdictions

GET /api/sales/tax_jurisdictions

Tax Jurisdictions can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/sales/tax_jurisdictions/?per_page=1"
client["sales/tax_jurisdictions"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 103764,
      "name": "MA",
      "type": "StateJurisdiction",
      "country": "US",
      "state": "MA"
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>100741,
    "name"=>"MA",
    "type"=>"StateJurisdiction",
    "country"=>"US",
    "state"=>"MA"},
   "..."]}

Get a tax jurisdiction

GET /api/sales/tax_jurisdictions/{{jurisdiction_id}}

Tax Jurisdictions can be fetched by GETting the tax jurisdiction's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/sales/tax_jurisdictions/{{jurisdiction_id}}"
client["sales/tax_jurisdictions/{{jurisdiction_id}}"]
  .get.body
Response:
{
  "id": 103039,
  "name": "MA",
  "type": "StateJurisdiction",
  "country": "US",
  "state": "MA"
}
{"id"=>103809,
 "name"=>"MA",
 "type"=>"StateJurisdiction",
 "country"=>"US",
 "state"=>"MA"}

Create a tax jurisdiction

POST /api/sales/tax_jurisdictions

Tax Jurisdictions are created by POSTing a tax jurisdiction record containing the required attributes to the /api/sales/tax_jurisdictions resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/sales/tax_jurisdictions"
client["sales/tax_jurisdictions"]
  .post({ "name"=>"MA", "type"=>"StateJurisdiction", "country"=>"US", "state"=>"MA" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/sales/tax_jurisdictions/149344

Update a tax jurisdiction

PUT /api/sales/tax_jurisdictions/{{jurisdiction_id}}

Tax Jurisdictions can be edited in bulk by PUTting a tax jurisdiction record containing a valid subset of tax jurisdiction attributes to the tax jurisdiction's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"name":"MA","type":"StateJurisdiction","country":"US","state":"MA"}' "https://{{subdomain}}.retail.heartland.us/api/sales/tax_jurisdictions/{{jurisdiction_id}}"
client["sales/tax_jurisdictions/{{jurisdiction_id}}"]
  .put(my_tax_jurisdiction_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Delete a tax jurisdiction

DELETE /api/sales/tax_jurisdictions/{{jurisdiction_id}}

Tax Jurisdictions can be deleted by sending a DELETE request to the tax jurisdiction's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/sales/tax_jurisdictions/{{jurisdiction_id}}"
client["sales/tax_jurisdictions/{{jurisdiction_id}}"]
  .delete.status
Response:
200
200

Tax Rules

The tax rules resource exposes the ability to create and modify tax rules in the Heartland Retail system.

Tax Rule

id:
integer (auto-generated)

The id of the tax rule record

name:
text

The name of the tax rule

amount:
double precision

Tax percentage for "Percentage"-based tax or flat amount for "Flat Per Item"

minimum_price:
numeric(10,2)

Whether or not a certain price must be reached before tax is applied

liability_account_id:
integer

Financial account that you map to will be the account that is credited/debited when the journal entries are synced to a connected financial system.

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

jurisdiction_id:
integer

Which tax jurisdiction the rule is attributed to (globally if not set)

type:
text (required)

The type of the tax rule (either PercentageRule or FlatPerItemRule)

maximum_price:
numeric(10,2)

Whether items should not be taxed if they are above a certain price

tax_amount_below_minimum:
boolean (required)

Whether or not to tax the amount below the minimum_price

archived:
boolean (required)

Whether or not tax rule is archived

customer_filter:
string

Apply the rule only to particular customers (JSON-serialized)

item_filter:
string

Apply the rule only to particular items (JSON-serialized)

financial_error:
boolean (computed)

Whether the tax rule is configured in financial system correctly or not

Tax Rule Example:
{
  "id": 101158,
  "minimum_price": 18.18,
  "liability_account_id": 104358,
  "jurisdiction_id": 107137,
  "maximum_price": 41.12,
  "customer_filter": "{"$and":[{"custom@taxed":{"$eq":"true"}}]}",
  "item_filter": "{"$and":[{"custom@tax_category":{"$eq":"CLOTHING"}}]}"
}

Search tax rules

GET /api/sales/tax_rules

Tax Rules can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/sales/tax_rules/?per_page=1"
client["sales/tax_rules"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 105820,
      "minimum_price": 28.36,
      "liability_account_id": 101661,
      "jurisdiction_id": 100980,
      "maximum_price": 14.85,
      "customer_filter": "{"$and":[{"custom@taxed":{"$eq":"true"}}]}",
      "item_filter": "{"$and":[{"custom@tax_category":{"$eq":"CLOTHING"}}]}"
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>104078,
    "minimum_price"=>25.46,
    "liability_account_id"=>105442,
    "jurisdiction_id"=>100474,
    "maximum_price"=>12.54,
    "customer_filter"=>"{"$and":[{"custom@taxed":{"$eq":"true"}}]}",
    "item_filter"=>
     "{"$and":[{"custom@tax_category":{"$eq":"CLOTHING"}}]}"},
   "..."]}

Get a tax rule

GET /api/sales/tax_rules/{{rule_id}}

Tax Rules can be fetched by GETting the tax rule's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/sales/tax_rules/{{rule_id}}"
client["sales/tax_rules/{{rule_id}}"].get.body
Response:
{
  "id": 102375,
  "minimum_price": 57.82,
  "liability_account_id": 107330,
  "jurisdiction_id": 104881,
  "maximum_price": 29.08,
  "customer_filter": "{"$and":[{"custom@taxed":{"$eq":"true"}}]}",
  "item_filter": "{"$and":[{"custom@tax_category":{"$eq":"CLOTHING"}}]}"
}
{"id"=>105117,
 "minimum_price"=>40.56,
 "liability_account_id"=>100208,
 "jurisdiction_id"=>105315,
 "maximum_price"=>15.95,
 "customer_filter"=>"{"$and":[{"custom@taxed":{"$eq":"true"}}]}",
 "item_filter"=>
  "{"$and":[{"custom@tax_category":{"$eq":"CLOTHING"}}]}"}

Create a tax rule

POST /api/sales/tax_rules

Tax Rules are created by POSTing a tax rule record containing the required attributes to the /api/sales/tax_rules resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/sales/tax_rules"
client["sales/tax_rules"]
  .post({ "name"=>"CA Sales Tax", "amount"=>"50", "item_filter"=>"{"$and":[{"custom@tax_category":{"$eq":"CLOTHING"}}]}" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/sales/tax_rules/117162

Update a tax rule

PUT /api/sales/tax_rules/{{rule_id}}

Tax Rules can be edited by PUTting a tax rule record containing a valid subset of tax rule attributes and providing the list of tax rules ids to update to the tax rules resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"minimum_price":18.22,"liability_account_id":106510,"jurisdiction_id":104852,"maximum_price":92.24,"customer_filter":"{"$and":[{"custom@taxed":{"$eq":"true"}}]}","item_filter":"{"$and":[{"custom@tax_category":{"$eq":"CLOTHING"}}]}"}' "https://{{subdomain}}.retail.heartland.us/api/sales/tax_rules/{{rule_id}}"
client["sales/tax_rules/{{rule_id}}"]
  .put(my_tax_rule_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Update tax rules in bulk

PUT /api/sales/tax_rules

Tax Rules can be edited in bulk by PUTting a tax rule record containing a valid subset of tax rule attributes to the tax rule's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"transfer_id":102886,"item_id":103791,"qty_shipped":50.2,"qty_received":89.4,"qty_lost":87.42,"unit_cost":88.81,"qty_discrepant":65.88,"qty_over":51.34,"qty_short":93.61,"created_by_user_id":103883,"updated_by_user_id":101885,"qty_requested":62.13}' "https://{{subdomain}}.retail.heartland.us/api/sales/tax_rules"
client["sales/tax_rules"]
  .put(my_inventory_transfer_line_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Delete a tax rule

DELETE /api/sales/tax_rules/{{rule_id}}

Tax Rules can be deleted by sending a DELETE request to the tax rule's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/sales/tax_rules/{{rule_id}}"
client["sales/tax_rules/{{rule_id}}"]
  .delete.status
Response:
200
200

Delete tax rules in bulk

DELETE /api/sales/tax_rules

Tax Rules can be deleted in bulk by sending a DELETE request with tax rules ids to the tax rules resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/sales/tax_rules"
client["sales/tax_rules"].delete.status
Response:
200
200

Promotions

Heartland Retail includes a powerful rules-based promotion system. There are two ways to define promotions that can be used to apply discounts and special offers to a sale transaction: Promotion Rules and Coupons.

Promotion Rules and Coupons are each similarly structured and based on the concept of conditions and actions. This part of the documentation will focus on the similarities between these two promotions. For information about the specifics of each, see their respective sections.

Conditions

Conditions determine whether the given promotion's actions should be applied to the current sales transaction. These are transaction-level conditions specified in the condition_definition field.

Be careful not to confuse these with item-level conditions which some actions support. If your promotion needs to target only certain items but should apply to any transaction then you do not need to specify a condition_definition.

Actions

Actions define the effects the promotion will have on the sales transaction. A promotion must have one or more actions. Each action has a type which determines the effect on the transaction. Actions may have additional parameters depending on the type.

See Action Types for a list of available types and their parameters.

Example

Here's an example of a promotion rule. This rule would apply an employee discount of 30% off the original price of any item except those whose primary vendor's ID is 100003. This rule only applies if sales transaction has customer associated and that customer's "Is Employee" custom field is set to "Yes".

{
  "id": 1,
  "name": "Employee Discount",
  "start_at": "2015-01-21 00:00:00",
  "end_at": null,
  "rank": 1,
  "condition_definition": {
    "$and": [
      {
        "customer.custom.is_employee": {
          "$eq": "Yes"
        }
      }
    ]
  },
  "action_definition": [
    {
      "type": "DiscountItem",
      "params": {
        "discount": 0.3,
        "discount_type": "percent",
        "item_conditions": {
          "$and": [
            {
              "item_line.item_primary_vendor_id": {
                "$neq": "100003"
              }
            }
          ]
        },
        "item_price_attribute": "original_price"
      },
      "id": "8e5fd952-a186-11e4-8eee-bc764e2061ef"
    }
  ],
  "archived?": true,
  "disabled?": false,
  "created_at": "2015-01-21T15:59:07+00:00",
  "updated_at": "2015-01-21T16:02:08+00:00"
}

Promotion Rules

The promotion rules resource exposes the ability to create, retrieve, search, update and delete promotion rules in the Heartland Retail system.

A promotion rule is considered active if it is not archived, not disabled and the current date and time is within start_at and end_at.

See Filtering for details on the filter expression syntax of condition_definition.

The promotion rule APIs make use of the promotion rule record type:

Promotion Rule

A promotion rule is a record that represents a rule used in processing promotions and pricing.

id:
integer (auto-generated)
name:
text (required)

The name of this promotion.

start_at:
DateTime

The date and time this promotion starts.

end_at:
DateTime

The date and time this promotion ends.

rank:
integer

It determines the order in which promotions are applied. Promotions are applied from lowest to highest rank.

archived:
Boolean

Whether or not the promotion is archived.

disabled:
Boolean

Whether or not the promotion is disabled.

Promotion Action Definition:
action_definition

A filter expression.

Promotion Condition Definition:
condition_definition

A filter expression.

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

Promotion Rule Example:
{
  "id": 102389,
  "rank": 101112,
  "Promotion Action Definition": {
    "type": "DiscountItem",
    "params": {
      "discount": 5,
      "discount_type": "amount",
      "item_price_attribute": "price",
      "item_conditions": {
        "$and": [
          {
            "item_line.item_primary_vendor_id": "100001"
          }
        ]
      }
    }
  },
  "Promotion Condition Definition": {
    "$and": [
      {
        "customer.customer.rewards_level": {
          "$eq": "gold"
        }
      }
    ]
  }
}

Create a promotion rule

POST /api/promotion_rules

Promotion rules are created by POSTing a promotion rule record containing at least the required attributes to the /api/promotion_rules resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/promotion_rules"
client["promotion_rules"]
  .post({})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/promotion_rules/117771

Retrieve a promotion rule

GET /api/promotion_rules/{{promotion_rule_id}}

Promotion rules are retrieved via their unique id. The id can be found via a search.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/promotion_rules/{{promotion_rule_id}}"
client["promotion_rules/{{promotion_rule_id}}"]
  .get.body
Response:
{
  "id": 101334,
  "rank": 107634,
  "Promotion Action Definition": {
    "type": "DiscountItem",
    "params": {
      "discount": 5,
      "discount_type": "amount",
      "item_price_attribute": "price",
      "item_conditions": {
        "$and": [
          {
            "item_line.item_primary_vendor_id": "100001"
          }
        ]
      }
    }
  },
  "Promotion Condition Definition": {
    "$and": [
      {
        "customer.customer.rewards_level": {
          "$eq": "gold"
        }
      }
    ]
  }
}
{"id"=>104851,
 "rank"=>101674,
 "Promotion Action Definition"=>
  {"type"=>"DiscountItem",
   "params"=>
    {"discount"=>5,
     "discount_type"=>"amount",
     "item_price_attribute"=>"price",
     "item_conditions"=>
      {"$and"=>[{"item_line.item_primary_vendor_id"=>"100001"}]}}},
 "Promotion Condition Definition"=>
  {"$and"=>[{"customer.customer.rewards_level"=>{"$eq"=>"gold"}}]}}

Search promotion rules

GET /api/promotion_rules

Promotion rules can be searched by GETting the root of the promotion rules resource. This API call will result in a search result record containing zero or more promotion rule records in the results attribute. See Search Results for more information on search results.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/promotion_rules/?per_page=1"
client["promotion_rules"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 104291,
      "rank": 103134,
      "Promotion Action Definition": {
        "type": "DiscountItem",
        "params": {
          "discount": 5,
          "discount_type": "amount",
          "item_price_attribute": "price",
          "item_conditions": {
            "$and": [
              {
                "item_line.item_primary_vendor_id": "100001"
              }
            ]
          }
        }
      },
      "Promotion Condition Definition": {
        "$and": [
          {
            "customer.customer.rewards_level": {
              "$eq": "gold"
            }
          }
        ]
      }
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>100921,
    "rank"=>105122,
    "Promotion Action Definition"=>
     {"type"=>"DiscountItem",
      "params"=>
       {"discount"=>5,
        "discount_type"=>"amount",
        "item_price_attribute"=>"price",
        "item_conditions"=>
         {"$and"=>[{"item_line.item_primary_vendor_id"=>"100001"}]}}},
    "Promotion Condition Definition"=>
     {"$and"=>[{"customer.customer.rewards_level"=>{"$eq"=>"gold"}}]}},
   "..."]}

Update a promotion rule

PUT /api/promotion_rules/{{promotion_rule_id}}

Promotion rules can be edited by PUTting a promotion rule record containing a valid subset of promotion rule attributes to the promotion rule's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"rank":105466,"Promotion Action Definition":{"type":"DiscountItem","params":{"discount":5,"discount_type":"amount","item_price_attribute":"price","item_conditions":{"$and":[{"item_line.item_primary_vendor_id":"100001"}]}}},"Promotion Condition Definition":{"$and":[{"customer.customer.rewards_level":{"$eq":"gold"}}]}}' "https://{{subdomain}}.retail.heartland.us/api/promotion_rules/{{promotion_rule_id}}"
client["promotion_rules/{{promotion_rule_id}}"]
  .put(my_promotion_rule_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Delete a promotion rule

DELETE /api/promotion_rules/{{promotion_rule_id}}

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/promotion_rules/{{promotion_rule_id}}"
client["promotion_rules/{{promotion_rule_id}}"]
  .delete.status
Response:
200
200

Coupons

The Coupons resource exposes the ability to create, retrieve, search, update and delete Coupons in the Heartland Retail system.

A coupon is considered active if it is not archived, not disabled and the current date and time is within start_at and end_at.

See Filtering for details on the filter expression syntax of condition_definition.

The coupon APIs make use of the coupon record type:

Coupon

A coupon is a record that represents a rule used in processing coupons and pricing.

id:
integer (auto-generated)
name:
text (required)

The name of this coupon.

code:
text (required)

The code of this coupon.

start_at:
DateTime

The date and time this coupon starts.

end_at:
DateTime

The date and time this coupon ends.

archived:
Boolean

Whether or not the coupon is archived.

disabled:
Boolean

Whether or not the coupon is disabled.

Promotion Action Definition:
action_definition

A filter expression.

Promotion Condition Definition:
condition_definition

A filter expression.

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

Coupon Example:
{
  "id": 1,
  "name": "name",
  "code": "10new",
  "Promotion Action Definition": {
    "type": "DiscountItem",
    "params": {
      "discount": 5,
      "discount_type": "amount",
      "item_price_attribute": "price",
      "item_conditions": {
        "$and": [
          {
            "item_line.item_primary_vendor_id": "100001"
          }
        ]
      }
    }
  },
  "Promotion Condition Definition": {
    "$and": [
      {
        "customer.customer.rewards_level": {
          "$eq": "gold"
        }
      }
    ]
  }
}

Create a coupon

POST /api/coupons

Coupons are created by POSTing a coupon record containing at least the required attributes to the /api/coupons resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"name":"name","code":"10new"}' "https://{{subdomain}}.retail.heartland.us/api/coupons"
client["coupons"]
  .post({"name"=>"name", "code"=>"10new"}))
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
{"id"=>1, "name"=>"name", "code"=>"10new"}

Retrieve a coupon

GET /api/coupons/{{coupon_id}}

Coupons are retrieved via their unique id. The id can be found via a search.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/coupons/{{coupon_id}}"
client["coupons/{{coupon_id}}"].get.body
Response:
{
  "id": 103608,
  "name": "name",
  "code": "10new",
  "Promotion Action Definition": {
    "type": "DiscountItem",
    "params": {
      "discount": 5,
      "discount_type": "amount",
      "item_price_attribute": "price",
      "item_conditions": {
        "$and": [
          {
            "item_line.item_primary_vendor_id": "100001"
          }
        ]
      }
    }
  },
  "Promotion Condition Definition": {
    "$and": [
      {
        "customer.customer.rewards_level": {
          "$eq": "gold"
        }
      }
    ]
  }
}
{"id"=>1,
 "name"=>"name",
 "code"=>"10new",
 "Promotion Action Definition"=>
  {"type"=>"DiscountItem",
   "params"=>
    {"discount"=>5,
     "discount_type"=>"amount",
     "item_price_attribute"=>"price",
     "item_conditions"=>
      {"$and"=>[{"item_line.item_primary_vendor_id"=>"100001"}]}}},
 "Promotion Condition Definition"=>
  {"$and"=>[{"customer.customer.rewards_level"=>{"$eq"=>"gold"}}]}}

Search Coupons

GET /api/coupons

Coupons can be searched by GETting the root of the Coupons resource. This API call will result in a search result record containing zero or more coupon records in the results attribute. See Search Results for more information on search results.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/coupons/?per_page=1"
client["coupons"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 1,
      "name": "name",
      "code": "10new",
      "Promotion Action Definition": {
        "type": "DiscountItem",
        "params": {
          "discount": 5,
          "discount_type": "amount",
          "item_price_attribute": "price",
          "item_conditions": {
            "$and": [
              {
                "item_line.item_primary_vendor_id": "100001"
              }
            ]
          }
        }
      },
      "Promotion Condition Definition": {
        "$and": [
          {
            "customer.customer.rewards_level": {
              "$eq": "gold"
            }
          }
        ]
      }
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>1,
    "name"=>"name",
    "code"=>"10new",
    "Promotion Action Definition"=>
     {"type"=>"DiscountItem",
      "params"=>
       {"discount"=>5,
        "discount_type"=>"amount",
        "item_price_attribute"=>"price",
        "item_conditions"=>
         {"$and"=>[{"item_line.item_primary_vendor_id"=>"100001"}]}}},
    "Promotion Condition Definition"=>
     {"$and"=>[{"customer.customer.rewards_level"=>{"$eq"=>"gold"}}]}},
   "..."]}

Update a coupon

PUT /api/coupons/{{coupon_id}}

Coupons can be edited by PUTting a coupon record containing a valid subset of coupon attributes to the coupon's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"name":"name","code":"10new","Promotion Action Definition":{"type":"DiscountItem","params":{"discount":5,"discount_type":"amount","item_price_attribute":"price","item_conditions":{"$and":[{"item_line.item_primary_vendor_id":"100001"}]}}},"Promotion Condition Definition":{"$and":[{"customer.customer.rewards_level":{"$eq":"gold"}}]}}' "https://{{subdomain}}.retail.heartland.us/api/coupons/{{coupon_id}}"
client["coupons/{{coupon_id}}"]
  .put({"name"=>"name", "code"=>"10new"})
  .get.body
Response:
# ...
# Status: 200 OK
# ...

# No Content
{"id"=>1, "name"=>"name", "code"=>"10new"}

Delete a coupon

DELETE /api/coupons/{{coupon_id}}

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/coupons/{{coupon_id}}"
client["coupons/{{coupon_id}}"].delete.status
Response:
200
200

Action Types

The following promotion action types may be used to specify promotion behaviors on either Promotion Rules or Coupons. See Promotions for usage details.

DiscountItem

Applies the given discount to item lines on the transaction that match the given item_conditions. If item_conditions are not specified, applies to any item.

type:
text (required)

Must be DiscountItem

discount:
float (required)

Amount of the discount. The value here is dependent upon the discount_type. If discount_type is percent value should be the percent off divided by 100 (e.g. 25% off = 0.25). If discount_type is amount value should be the amount to subtract from the item's unit price. (e.g. $12.34 off = 12.34) If discount_type is fixed_price value should be the amount to use as the item's unit price (e.g. $12.34 per item = 12.34)

discount_type:
string (required)

The type of discount to apply to the matching items. See discount for more details. Valid options are amount, percent, and fixed_price.

item_price_attribute:
string

The date and time this promotion starts.

item_conditions:
Filter Expression

Which items on the transaction should receive the discount. See filtering docs for more details. If omitted, discount will apply to any item.

DiscountSubtotal

Applies the given discount to the transaction.

type:
text (required)

Must be DiscountSubtotal

discount:
float (required)

Amount of the discount. The value here is dependent upon the discount_type. If discount_type is percent value should be the percent off divided by 100 (e.g. 25% off = 0.25). If discount_type is amount value should be the amount to subtract from the item's unit price. (e.g. $12.34 off = 12.34)

discount_type:
string (required)

The type of discount to apply to transaction. See discount for more details. Valid options are amount, percent.

DiscountItem Example:
{
  "type": "DiscountItem",
  "discount": 0.25,
  "discount_type": "percent",
  "item_price_attribute": "price",
  "item_conditions": {
    "$and": [
      {
        "item_line.price": {
          "$gt": 100
        }
      }
    ]
  }
}
DiscountSubtotal Example:
{
  "type": "DiscountSubtotal",
  "discount": 0.25,
  "discount_type": "percent"
}

Locations

Location related resources include physical locations, POS stations, and cash drawers

Locations

A location represents a place where sales and inventory transactions can occur. This can be a physical store or it can represent a virtual store like an e-commerce site.

Location APIs make use of the location record type:

Location

A location represents a physical space where sales transactions can take place.

id:
Integer (auto-generated)

unique identifier for the record

metadata:
Object

custom key/value information stored about this location

name:
String (required)

The name of the location

address_id:
Integer

The unique identifier for the address record associated with this location

public_id:
String

a unique identifier provided by the user for the purpose of coding information

address_revision_id:
Integer

unique identifier for the current revision of the address associated with this location

active:
boolean

Whether or not this record is currently active and being used

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

timezone_identifier_id:
Integer (auto-generated)

unique identifier for the timezone record

created_by_user_id:
integer (auto-generated)

The user who created this record

updated_by_user_id:
integer (auto-generated)

The most recent user who updated this record

uuid:
String (auto-generated)

Unique identifier for the location

custom:
hash

An object that acts as a container for custom values defined in Heartland Retail as well as any arbitrary values stored by integrations or custom applications.

price_list_id:
Integer (auto-generated)

unique identifier for the price list record

settings:
Object

key/value information stored about this locations settings

credit_cards_configured:
boolean

Whether the location has a credit card processor configured or not

Location Example:
{
  "id": 100001,
  "name": "Boston",
  "address_id": 100004,
  "public_id": "BOS",
  "address_revision_id": 100016,
  "timezone_identifier_id": 1,
  "created_by_user_id": 106126,
  "updated_by_user_id": 105157,
  "uuid": "d0a77755-ddfb-4276-a523-afc7bb959b2d",
  "price_list_id": 1,
  "credit_cards_configured": "false"
}

Search locations

GET /api/locations

Search for and list locations

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/locations/?per_page=1"
client["locations"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 100001,
      "name": "Boston",
      "address_id": 100004,
      "public_id": "BOS",
      "address_revision_id": 100016,
      "timezone_identifier_id": 1,
      "created_by_user_id": 105820,
      "updated_by_user_id": 107811,
      "uuid": "d0a77755-ddfb-4276-a523-afc7bb959b2d",
      "price_list_id": 1,
      "credit_cards_configured": "false"
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>100001,
    "name"=>"Boston",
    "address_id"=>100004,
    "public_id"=>"BOS",
    "address_revision_id"=>100016,
    "timezone_identifier_id"=>1,
    "created_by_user_id"=>108675,
    "updated_by_user_id"=>103334,
    "uuid"=>"d0a77755-ddfb-4276-a523-afc7bb959b2d",
    "price_list_id"=>1,
    "credit_cards_configured"=>"false"},
   "..."]}

Purchasing

Purchasing APIs expose the ability to manage purchase orders, receipts, and returns.

Purchase Order

A purchase order is a record of items that have been or will be ordered from a vendor.

In order to complete a purchase order, it must have a receipt against it for all items on order.

Purchasing Order

id:
integer (auto-generated)
public_id:
text
status:
text (required)
start_shipments_at:
Date
end_shipments_at:
Date
vendor_id:
integer (required)
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

created_by_user_id:
integer
description:
text
receive_at_location_id:
integer (required)
custom:
object
total_qty:
numeric(10,2)
total_cost:
numeric(10,2)
total_price:
numeric(10,2)
total_received_qty:
numeric(10,2)
total_received_cost:
numeric(10,2)
total_received_price:
numeric(10,2)
total_open_qty:
numeric(10,2)
total_open_cost:
numeric(10,2)
total_open_price:
numeric(10,2)
initial_margin_ratio:
numeric
po_import_batch_id:
integer

Purchasing OrderLine

id:
integer (auto-generated)
item_id:
integer (required)
order_id:
integer (required)
qty:
numeric(10,2) (required)
unit_cost:
numeric(10,2) (required)
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

qty_received:
integer (required)
received_cost:
numeric
open_cost:
numeric
qty_expected:
numeric
status:
text
Purchasing Order Example:
{
  "id": 106686,
  "vendor_id": 104254,
  "created_by_user_id": 104199,
  "receive_at_location_id": 100631,
  "total_qty": 57.69,
  "total_cost": 11.08,
  "total_price": 30.79,
  "total_received_qty": 58.46,
  "total_received_cost": 58.4,
  "total_received_price": 82.72,
  "total_open_qty": 98.44,
  "total_open_cost": 98.83,
  "total_open_price": 77.35,
  "po_import_batch_id": 104576
}
Purchasing OrderLine Example:
{
  "id": 102246,
  "item_id": 100842,
  "order_id": 105896,
  "qty": 41.39,
  "unit_cost": 88.38,
  "qty_received": 103166
}

Create a purchase order

POST /api/purchasing/orders

Creating a purchase order requires a vendor and the location id. The location id is used to determine where the order will be received.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"vendor_id":107068,"receive_at_location_id":101771}' "https://{{subdomain}}.retail.heartland.us/api/purchasing/orders"
client["purchasing/orders"]
  .post({"vendor_id"=>101978, "receive_at_location_id"=>104575})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/purchasing/orders/161747

Retrieve a purchase order

GET /api/purchasing/orders/{{purchase_order_id}}

A purchase order is retrieved using its unique identifier. This identifier can be found by searching for purchase orders, from references in other records, or from the Location header returned in response to the creation of a new purchase order.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/purchasing/orders/{{purchase_order_id}}"
client["purchasing/orders/{{purchase_order_id}}"]
  .get.body
Response:
{
  "id": 100732,
  "vendor_id": 101968,
  "created_by_user_id": 102676,
  "receive_at_location_id": 102365,
  "total_qty": 89.91,
  "total_cost": 18.32,
  "total_price": 59.1,
  "total_received_qty": 73.1,
  "total_received_cost": 74.52,
  "total_received_price": 55.68,
  "total_open_qty": 62.57,
  "total_open_cost": 36.72,
  "total_open_price": 17.65,
  "po_import_batch_id": 109767
}
{"id"=>106525,
 "vendor_id"=>102581,
 "created_by_user_id"=>100793,
 "receive_at_location_id"=>103680,
 "total_qty"=>97.56,
 "total_cost"=>68.27,
 "total_price"=>73.01,
 "total_received_qty"=>61.1,
 "total_received_cost"=>23.32,
 "total_received_price"=>68.83,
 "total_open_qty"=>84.31,
 "total_open_cost"=>18.97,
 "total_open_price"=>23.62,
 "po_import_batch_id"=>104450}

Retrieve a purchase orders's lines

GET /api/purchasing/orders/{{purchase_order_id}}/lines

A purchase order has zero or more associated lines.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/purchasing/orders/{{purchase_order_id}}/lines/?per_page=1"
client["purchasing/orders/{{purchase_order_id}}/lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 104104,
      "item_id": 108894,
      "order_id": 107935,
      "qty": 28.83,
      "unit_cost": 68.8,
      "qty_received": 103595
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>102785,
    "item_id"=>105680,
    "order_id"=>101460,
    "qty"=>29.4,
    "unit_cost"=>58.8,
    "qty_received"=>105108},
   "..."]}

Add an item to a purchase order

POST /api/purchasing/orders/{{purchase_order_id}}/lines

Adds an item with the given qty to the purchase order. This method can also be used to increment the quantity of an existing item line.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"item_id":101104,"order_id":109311,"qty":99.97,"unit_cost":54.86,"qty_received":100168}' "https://{{subdomain}}.retail.heartland.us/api/purchasing/orders/{{purchase_order_id}}/lines"
client["purchasing/orders/{{purchase_order_id}}/lines"]
  .post({"item_id"=>106774, "order_id"=>100350, "qty"=>67.42, "unit_cost"=>22.78, "qty_received"=>100063})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/purchasing/orders/155054/lines/138184

Purchase Receipt

A purchase receipt is a record that documents the full or partial fulfillment of a purchase order. (For purchase orders, see Purchase Order)

Purchasing receipt APIs expose the ability to create, retrieve, and update receipts.

Purchasing Receipt

id:
integer (auto-generated)
status:
text (required)
order_id:
integer
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

custom:
object
public_id:
text

Purchasing ReceiptLine

id:
integer (auto-generated)
item_id:
integer (required)
qty:
numeric(10,2) (required)
unit_cost:
numeric(10,2)
receipt_id:
integer (required)
order_line_id:
integer
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

qty_expected:
numeric
qty_unauthorized:
numeric
Purchasing Receipt Example:
{
  "id": 101252,
  "order_id": 107809
}
Purchasing ReceiptLine Example:
{
  "id": 105400,
  "item_id": 102281,
  "qty": 88.15,
  "unit_cost": 99.88,
  "receipt_id": 102809,
  "order_line_id": 101756
}

Create a receipt

POST /api/purchasing/receipts

Creating a purchase receipt requires a vendor and the location id. The location id is used to determine where the inventory is being received.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/purchasing/receipts"
client["purchasing/receipts"]
  .post({})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/purchasing/receipts/110626

Retrieve a receipt

GET /api/purchasing/receipts/{{purchase_receipt_id}}

A receipt is retrieved using its unique identifier. This identifier can be found by searching for receipts, from references in other records, or from the Location header returned in response to the creation of a new purchase order.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/purchasing/receipts/{{purchase_receipt_id}}"
client["purchasing/receipts/{{purchase_receipt_id}}"]
  .get.body
Response:
{
  "id": 104017,
  "order_id": 102574
}
{"id"=>101836, "order_id"=>106370}

Retrieve a receipt's lines

GET /api/purchasing/receipts/{{purchase_receipt_id}}/lines

A receipt has zero or more associated lines.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/purchasing/receipts/{{purchase_receipt_id}}/lines/?per_page=1"
client["purchasing/receipts/{{purchase_receipt_id}}/lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 104126,
      "item_id": 107316,
      "qty": 44.52,
      "unit_cost": 34.23,
      "receipt_id": 107281,
      "order_line_id": 102629
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>109762,
    "item_id"=>100539,
    "qty"=>97.69,
    "unit_cost"=>40.64,
    "receipt_id"=>100315,
    "order_line_id"=>104847},
   "..."]}

Add an item to a receipt

POST /api/purchasing/receipts/{{purchase_receipt_id}}/lines

Adds an item with the given qty to the receipt. This method can also be used to modify the quantity of an existing item line.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"item_id":101665,"qty":13.08,"receipt_id":105874}' "https://{{subdomain}}.retail.heartland.us/api/purchasing/receipts/{{purchase_receipt_id}}/lines"
client["purchasing/receipts/{{purchase_receipt_id}}/lines"]
  .post({"item_id"=>107901, "qty"=>83.82, "receipt_id"=>107890})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/purchasing/receipts/16499/lines/114680

Purchase Return

A purchase return is a record of an item that has been or will be returned to a vendor.

Purchase return APIs expose the ability to create, retrieve, and update returns.

Purchasing Receipt

id:
integer (auto-generated)
status:
text (required)
order_id:
integer
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

custom:
object
public_id:
text

Purchasing ReceiptLine

id:
integer (auto-generated)
item_id:
integer (required)
qty:
numeric(10,2) (required)
unit_cost:
numeric(10,2)
receipt_id:
integer (required)
order_line_id:
integer
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

qty_expected:
numeric
qty_unauthorized:
numeric
Purchasing Receipt Example:
{
  "id": 107662,
  "order_id": 103773
}
Purchasing ReceiptLine Example:
{
  "id": 104194,
  "item_id": 102658,
  "qty": 80.06,
  "unit_cost": 56.96,
  "receipt_id": 106031,
  "order_line_id": 109863
}

Create a return

POST /api/purchasing/returns

Creating a purchase return requires a vendor and the location id. The location id is used to determine where the inventory is coming from.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"vendor_id":105062,"return_from_location_id":101872}' "https://{{subdomain}}.retail.heartland.us/api/purchasing/returns"
client["purchasing/returns"]
  .post({"vendor_id"=>106171, "return_from_location_id"=>105041})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/purchasing/returns/199364

Retrieve a return

GET /api/purchasing/returns/{{purchase_return_id}}

A purchase return is retrieved using its unique identifier. This identifier can be found by searching for purchase return, from references in other records, or from the Location header returned in response to the creation of a new purchase return.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/purchasing/returns/{{purchase_return_id}}"
client["purchasing/returns/{{purchase_return_id}}"]
  .get.body
Response:
{
  "id": 103610,
  "vendor_id": 105281,
  "return_from_location_id": 106405
}
{"id"=>107304, "vendor_id"=>109608, "return_from_location_id"=>100034}

Retrieve a return's lines

GET /api/purchasing/returns/{{purchase_return_id}}/lines

A purchase return has zero or more associated lines.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/purchasing/returns/{{purchase_return_id}}/lines/?per_page=1"
client["purchasing/returns/{{purchase_return_id}}/lines"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 106485,
      "item_id": 109916,
      "return_id": 106263,
      "qty": 57.72,
      "unit_cost": 73.37
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>104587,
    "item_id"=>101338,
    "return_id"=>103332,
    "qty"=>20.46,
    "unit_cost"=>48.47},
   "..."]}

Add an item to a return

POST /api/purchasing/returns/{{purchase_return_id}}/lines

Adds an item with the given qty to the purchase return. This method can also be used to modify the quantity of an existing item line.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"item_id":101317,"return_id":109625,"qty":69.65,"unit_cost":85.25}' "https://{{subdomain}}.retail.heartland.us/api/purchasing/returns/{{purchase_return_id}}/lines"
client["purchasing/returns/{{purchase_return_id}}/lines"]
  .post({"item_id"=>101846, "return_id"=>106529, "qty"=>32.66, "unit_cost"=>22.3})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/purchasing/returns/131494/lines/125332

Purchase Vendor

The purchase vendor resource exposes the ability to create and modify purchase vendors in the Heartland Retail system.

Purchasing Vendor

id:
integer (auto-generated)
name:
text (required)
address_id:
integer
created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

public_id:
text
active:
boolean

Whether or not this record is currently active and being used

vendor_import_batch_id:
integer
Purchasing Vendor Example:
{
  "id": 107855,
  "address_id": 104057,
  "vendor_import_batch_id": 108638
}

Create a vendor

POST /api/purchasing/vendors

Purchase vendors are created by POSTing a purchase vendor record containing the required attributes to the /api/purchasing/vendors resource. When successful, the response headers will contain a Location header that indicates the url at which the newly created purchase vendor can be found. The vendor's unique id will be the last portion of that URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/purchasing/vendors"
client["purchasing/vendors"]
  .post({})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/purchasing/vendors/17246

Retrieve a purchase vendor

GET /api/purchasing/vendors/{{vendor_id}}

Purchase vendors are retrieved from the Heartland Retail API via their unique id. The id can be found via searches, references from other records, or from the Location header in a Create Purchase Vendor response.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/purchasing/vendors/{{vendor_id}}"
client["purchasing/vendors/{{vendor_id}}"]
  .get.body
Response:
{
  "id": 101299,
  "address_id": 106071,
  "vendor_import_batch_id": 100922
}
{"id"=>105475, "address_id"=>109094, "vendor_import_batch_id"=>105639}

Update a purchase vendor

PUT /api/purchasing/vendors/{{vendor_id}}

Purchase vendors can be edited by PUTting a purchase vendor record containing a valid subset of purchase vendor attributes to the purchase vendor's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"address_id":104433,"vendor_import_batch_id":100817}' "https://{{subdomain}}.retail.heartland.us/api/purchasing/vendors/{{vendor_id}}"
client["purchasing/vendors/{{vendor_id}}"]
  .put(my_purchasing_vendor_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Search purchase vendors

GET /api/purchasing/vendors

Purchase vendors can be searched by GETting the root of the purchase vendors resource /api/purchasing/vendors. This API call will result in a search result record containing zero or more purchase vendor records in the values attribute. See Search Results for more information on search results.

Purchase vendors can be filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/purchasing/vendors/?per_page=1"
client["purchasing/vendors"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 107326,
      "address_id": 109379,
      "vendor_import_batch_id": 100380
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>105672, "address_id"=>107081, "vendor_import_batch_id"=>101656},
   "..."]}

Webhooks

The Webhooks resource exposes the ability to create, retrieve, search, update and delete Webhook in the Heartland Retail system.

Webhooks

Webhooks are limited to 10 URLs per event. Attempting to register more than that will return an error indicating which event(s) exceed the limit.

Webhooks retry once per hour for up to 72 hours. A webhook is considered successful when the handler returns a 200 status code.

The webhook APIs make use of the webhook record type:

Webhook

id:
integer (auto-generated)
url:
string (required)
events:
array (required)

available events

created_by_user_id:
integer (auto-generated)

The user who created this record

updated_by_user_id:
integer (auto-generated)

The most recent user who updated this record

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

Webhook Example:
{
  "id": 108908,
  "url": "http://example.org",
  "created_by_user_id": 104399,
  "updated_by_user_id": 108007
}

Create a webhook

POST /api/webhooks

Webhooks are created by POSTing a webhook record containing at least the required attributes to the /api/webhooks resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"url":"http://example.org"}' "https://{{subdomain}}.retail.heartland.us/api/webhooks"
client["webhooks"]
  .post({"url"=>"http://example.com", "events"=>["webhook_registered"]}))
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
{"id"=>1, "url"=>"http://example.com", "events"=>["webhook_registered"]}

Retrieve a webhook

GET /api/webhooks/{{webhook_id}}

Webhooks are retrieved via their unique id. The id can be found via a search.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/webhooks/{{webhook_id}}"
client["webhooks/{{webhook_id}}"].get.body
Response:
{
  "id": 103784,
  "url": "http://example.org",
  "created_by_user_id": 107277,
  "updated_by_user_id": 108222
}
{"id"=>102748,
 "url"=>"http://example.org",
 "created_by_user_id"=>106740,
 "updated_by_user_id"=>101533}

Search Webhooks

GET /api/webhooks

Webhooks can be searched by GETting the root of the Webhooks resource. This API call will result in a search result record containing zero or more webhook records in the results attribute. See Search Results for more information on search results.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/webhooks/?per_page=1"
client["webhooks"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 103315,
      "url": "http://example.org",
      "created_by_user_id": 105076,
      "updated_by_user_id": 105121
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>107304,
    "url"=>"http://example.org",
    "created_by_user_id"=>108648,
    "updated_by_user_id"=>109293},
   "..."]}

Update a webhook

PUT /api/webhooks/{{webhook_id}}

Webhooks can be edited by PUTting a webhook record containing a valid subset of webhook attributes to the webhook's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"url":"http://example.org"}' "https://{{subdomain}}.retail.heartland.us/api/webhooks/{{webhook_id}}"
client["webhooks/{{webhook_id}}"]
  .put(my_webhook_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Delete a webhook

DELETE /api/webhooks/{{webhook_id}}

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/webhooks/{{webhook_id}}"
client["webhooks/{{webhook_id}}"].delete.status
Response:
200
200

Events

webhook_registered

Occurs whenever a webhook is created.

Payload:

{
  "webhook": {
    "id": 1234,
    "url": "https://example.com/target",
    "events": [
      "customer_created",
      "customer_merged"
    ]
  }
}

customer_created

Occurs whenever a customer is created. Describes a customer.

Payload:

{
  "id": 1,
  "first_name": "First Name",
  "last_name": "Last Name",
  ...
}

customer_updated

Occurs whenever a customer has changed. Describes a customer.

Payload:

{
  "id": 1,
  "first_name": "First Name",
  "last_name": "Last Name",
  ...
}

customer_merged

Occurs whenever customers have merged.

Payload:

{
  "old_customer_ids": [1, 2, 3, 4],
  "new_customer_id": 5
}

sales_transaction_customer_selected

Occurs whenever a customer has been selected in sale transaction. Describes a customer and a sale transaction.

Payload:

{
  "customer": {
    "id": 1,
    "first_name": "First Name",
    "last_name": "Last Name",
    ...
  },
  "sales_transaction": {
    "id": 101388,
    "customer_id": 102262,
    "source_location_id": 103683,
    "station_id": 108059,
    "parent_transaction_id": 108668,
    "order_id": 103520,
    "total": 22.81,
    "created_by_user_id": 107176
    ...
  }
}

sales_transaction_completed

Occurs whenever sale transaction is completed. Describes a sale transaction.

Payload:

{
  "id": 101388,
  "customer_id": 102262,
  "source_location_id": 103683,
  "station_id": 108059,
  "parent_transaction_id": 108668,
  "order_id": 103520,
  "total": 22.81,
  "created_by_user_id": 107176
  ...
}

item_created

Occurs whenever item is created. Describes an item.

Payload:

{
  "id": 108162,
  "cost": 92.68,
  "price": 47.01,
  "financial_class_id": 103307,
  "import_batch_id": 103994,
  "primary_vendor_id": 108158,
  ...
}

item_updated

Occurs whenever item has changed. Describes an item.

Payload:

{
  "id": 108162,
  "cost": 92.68,
  "price": 47.01,
  "financial_class_id": 103307,
  "import_batch_id": 103994,
  "primary_vendor_id": 108158,
  ...
}

### purchasing_vendor_created
Occurs whenever purchasing vendor is created. Describes a [purchase vendor](#resource-purchase_vendor).

Payload:

```json
{
  "id": 103504,
  "address_id": 104772,
  "vendor_import_batch_id": 103806,
  ...
}

purchasing_vendor_updated

Occurs whenever purchasing vendor has changed. Describes a purchase vendor.

Payload:

{
  "id": 103504,
  "address_id": 104772,
  "vendor_import_batch_id": 103806,
  ...
}

promotion_rule_created

Occurs whenever promotion rule is created. Describes a promotion rule.

Payload:

{
  "id": 105928,
  "rank": 102965,
  "action_definition": [
    {
      "type": "DiscountSubtotal",
      "params": {
        "discount": 20,
        "discount_type": "amount"
      }
    }
  ],
  "condition_definition": {
    "$and": [
      {
        "customer.customer.rewards_level": {
          "$eq": "gold"
        }
      }
    ]
  }
}

promotion_rule_updated

Occurs whenever promotion rule has changed. Describes a promotion rule.

Payload:

{
  "id": 105928,
  "rank": 102965,
  "action_definition": [
    {
      "type": "DiscountSubtotal",
      "params": {
        "discount": 20,
        "discount_type": "amount"
      }
    }
  ],
  "condition_definition": {
    "$and": [
      {
        "customer.customer.rewards_level": {
          "$eq": "gold"
        }
      }
    ]
  }
}

Traffic

Traffic APIs expose the ability to create, retrieve, and update for traffic counters and counts. The traffic APIs make use of the traffic counter and traffic count records.

Traffic Counters

Traffic counters recognize shapes and count them as visitors. These counters can be set up on the entrances to the store or within the store. Traffic counters represent devices that could be assigned to locations.

TrafficCounter

id:
integer (auto-generated)
location_id:
integer

Location of the traffic counter

name:
text (required)

Counter name

original_name:
text (required)

Original device name (coming from integrations)

entrance:
boolean (required)

Is this a store entrance traffic counter?

zone:
text (required)

If not the entrance, the name of the zone

custom:
hash

An object that acts as a container for any arbitrary values stored by integrations or custom applications.

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

TrafficCounter Example:
{
  "id": 101942,
  "location_id": 105638
}

Create a counter

POST /api/traffic_counters

Creating a traffic counter requires a name, original_name (could be the same as name), entrance and zone. If you pass the location_id, the counter will be assigned to the location.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/traffic_counters"
client["traffic_counters"]
  .post({})
  .headers['Location']
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/traffic_counters/162508

Update a counter

PUT /api/traffic_counters/{{counter_id}}

A traffic counter can be modified by sending a PUT request to the record's unique URL. The contents of the request must contain a valid subset of traffic counter record attribute.

Request:
client[:traffic_counters][123].put(name: 'Boston').status
curl -v -H "Authorization: Bearer {{Token}}" -X PUT -H "Content-Type: application/json" -d '{"name":"Boston"}' "https://{{subdomain}}.retail.heartland.us/api/traffic_counters/123"
Response:
200 # success!
# ...
# Status: 200 OK
# ...

# No Content.

Search counters

GET /api/traffic_counters

Traffic counters can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/traffic_counters/?per_page=1"
client["traffic_counters"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 106475,
      "location_id": 103027
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>[{"id"=>107127, "location_id"=>108936}, "..."]}

Traffic Counts

A traffic count is number of visitors for the given counter and datetime.

TrafficCounter

id:
integer (auto-generated)
location_id:
integer

Location of the traffic counter

name:
text (required)

Counter name

original_name:
text (required)

Original device name (coming from integrations)

entrance:
boolean (required)

Is this a store entrance traffic counter?

zone:
text (required)

If not the entrance, the name of the zone

custom:
hash

An object that acts as a container for any arbitrary values stored by integrations or custom applications.

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

TrafficCounter Example:
{
  "id": 104643,
  "location_id": 100797
}

Create a count

PUT /api/traffic_counts

Creating a traffic count requires a traffic_counter_id, datetime and value.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"traffic_counter_id":107497,"value":107215,"year":107761,"month":103197,"week":106035,"hour":103872,"date_id":108150,"time_id":102232}' "https://{{subdomain}}.retail.heartland.us/api/traffic_counts"
client["traffic_counts"]
  .put(my_traffic_count_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Retrieve a count

GET /api/traffic_counts/{{count_id}}

A traffic count is retrieved using its unique identifier. This identifier can be found by searching for counts, from references in other records, or from the response to the creation of a new count.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/traffic_counts/{{count_id}}"
client["traffic_counts/{{count_id}}"].get.body
Response:
{
  "id": 101328,
  "traffic_counter_id": 107367,
  "value": 108371,
  "year": 108632,
  "month": 108398,
  "week": 106206,
  "hour": 100489,
  "date_id": 107796,
  "time_id": 105527
}
{"id"=>107750,
 "traffic_counter_id"=>106963,
 "value"=>101626,
 "year"=>100920,
 "month"=>108327,
 "week"=>109397,
 "hour"=>102410,
 "date_id"=>103395,
 "time_id"=>105905}

Search counts

GET /api/traffic_counts

Traffic counts can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/traffic_counts/?per_page=1"
client["traffic_counts"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 100348,
      "traffic_counter_id": 100741,
      "value": 108141,
      "year": 108777,
      "month": 103939,
      "week": 107073,
      "hour": 107850,
      "date_id": 102730,
      "time_id": 103739
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>103338,
    "traffic_counter_id"=>108037,
    "value"=>104698,
    "year"=>103457,
    "month"=>109393,
    "week"=>105575,
    "hour"=>108691,
    "date_id"=>104658,
    "time_id"=>106465},
   "..."]}

Permissions

Permissions APIs expose the ability to retrieve the list of available permissions.

Permissions

A permission is a named authorization granted to do something.

Permission

id:
text (required)

The name of the permission

description:
text

Description of the permission

Permission Example:
{
  "id": "role.manage",
  "description": "Authorize users to manage roles records"
}

Search permissions

GET /api/permissions

Permissions can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/permissions/?per_page=1"
client["permissions"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": "role.manage",
      "description": "Authorize users to manage roles records"
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>"role.manage",
    "description"=>"Authorize users to manage roles records"},
   "..."]}

Roles

Roles APIs expose the ability to manage permissions and alert typesas as well as exposing an API to add or remove users from the given role.

Roles Records

A role is a collection of permissions with a name assigned to one or more users, and subscribed to one or more alert types.

Role

id:
integer (auto-generated)

The ID of the role record

name:
text (required)

The name of the role

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

users_count:
integer (computed)

Count of users with the role

Role Example:
{
  "id": 107314,
  "users_count": 106274
}

Search roles

GET /api/roles

Roles can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/roles/?per_page=1"
client["roles"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 106007,
      "users_count": 102181
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>[{"id"=>100672, "users_count"=>108155}, "..."]}

Create role

POST /api/roles

Roles are created by POSTing a role record containing the required attributes to the /api/roles resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/roles"
client["roles"]
  .post({ "name"=>"Sales Associate" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/roles/156700

Update a role

PUT /api/roles/{{role_id}}

Roles can be edited by PUTting a role record containing a valid subset of role attributes to the role's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"users_count":105537}' "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}"
client["roles/{{role_id}}"]
  .put(my_role_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Get a role

GET /api/roles/{{role_id}}

Roles can be fetched by GETting the role's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}"
client["roles/{{role_id}}"].get.body
Response:
{
  "id": 104802,
  "users_count": 101826
}
{"id"=>108758, "users_count"=>108833}

Delete a role

DELETE /api/roles/{{role_id}}

Roles can be deleted by sending a DELETE request the role's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}"
client["roles/{{role_id}}"].delete.status
Response:
200
200

Roles Permissions

A role can authorize one ore more permissions and be assigned to one or multiple users. For more information on permissions see Permissions.

Permission

id:
text (required)

The name of the permission

description:
text

Description of the permission

Permission Example:
{
  "id": "role.manage",
  "description": "Authorize users to manage roles records"
}

Search permissions of the role

GET /api/roles/{{role_id}}/permissions

Roles permissions can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/permissions/?per_page=1"
client["roles/{{role_id}}/permissions"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": "role.manage",
      "description": "Authorize users to manage roles records"
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>"role.manage",
    "description"=>"Authorize users to manage roles records"},
   "..."]}

Add a new permission to the role

PUT /api/roles/{{role_id}}/permissions

Permissions can be added to roles by PUTing their IDs to the role's permissions resource.

Request:
client["roles/123/permissions"].post({ "permission_ids"=>["customer.read", "role.manage"], "enabled"=>"true" }).get.body
curl -v -H "Authorization: Bearer {{Token}}" -X PUT -H "Content-Type: application/json" -d '{"permission_ids"=>["customer.read", "role.manage"], "enabled"=>"true"}' "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/permissions"
Response:
204 # No Content
# ...
# Status: 204 No Content
# ...

# No Content.

Get a permission of the role

GET /api/roles/{{role_id}}/permissions/{{permission_id}}

Role permissions can be fetched by GETting the role permission's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/permissions/{{permission_id}}"
client["roles/{{role_id}}/permissions/{{permission_id}}"]
  .get.body
Response:
{
  "id": 101482,
  "description": "Authorize users to manage roles records"
}
{"id"=>"role.manage", "description"=>"Authorize users to manage roles records"}

Remove a permission from the role

DELETE /api/roles/{{role_id}}/permissions/{{permission_id}}

Remove a permission from the role by sending a DELETE request to the role permission's URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/permissions/{{permission_id}}"
client["roles/{{role_id}}/permissions/{{permission_id}}"]
  .delete.status
Response:
200
200

Roles Alert Types

A role can be subscribed to one or more alert types. All users with the role will see alert types their roles are subscribed to.

Alerts AllType

id:
integer (auto-generated)
type:
text

Type of the alert

description:
text

Description of the alert

category:
text

Category of the alert

Alerts AllType Example:
{
  "id": 107264,
  "type": "SalesOrdersDistributableAlertType",
  "description": "Distributable Sales Orders",
  "category": "sales"
}

Search alert types of the role

GET /api/roles/{{role_id}}/alert_types

Roles alert types can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/alert_types/?per_page=1"
client["roles/{{role_id}}/alert_types"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 108942,
      "type": "SalesOrdersDistributableAlertType",
      "description": "Distributable Sales Orders",
      "category": "sales"
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>107324,
    "type"=>"SalesOrdersDistributableAlertType",
    "description"=>"Distributable Sales Orders",
    "category"=>"sales"},
   "..."]}

Subscribe the role to a new alert type

POST /api/roles/{{role_id}}/alert_types

Alert types can be added to roles by POSTing their IDs to the role's alert types resource.

When successful, the response headers will contain a Location header that indicates the url at which the newly created alert subscription can be found. The alert subscription's unique id will be the last portion of that URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/alert_types"
client["roles/123/alert_types"]
  .post({ "alert_type_id"=>"321" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/roles/150501/alert_types/180534

Get an alert type of the role

GET /api/roles/{{role_id}}/alert_types/{{alert_type_id}}

Role alert type can be fetched by GETting the role alert type's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/alert_types/{{alert_type_id}}"
client["roles/{{role_id}}/alert_types/{{alert_type_id}}"]
  .get.body
Response:
{
  "id": 104224,
  "type": "SalesOrdersDistributableAlertType",
  "description": "Distributable Sales Orders",
  "category": "sales"
}
{"id"=>102973,
 "type"=>"SalesOrdersDistributableAlertType",
 "description"=>"Distributable Sales Orders",
 "category"=>"sales"}

Unsubscribe the role from an alert type

DELETE /api/roles/{{role_id}}/alert_types/{{alert_type_id}}

Alert types can be removed from roles by sending a DELETE request to the role alert type's resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/alert_types/{{alert_type_id}}"
client["roles/{{role_id}}/alert_types/{{alert_type_id}}"]
  .delete.status
Response:
200
200

Roles Users

A role can be assigned to one or more users. For more information on managing users see Users.

User

id:
integer (auto-generated)

The ID of the user record

login:
text (required)

The unique username for the user

first_name:
text

First name of the user

last_name:
text

Last name of the user

email:
text (required)

Email address

phone:
text

Phone number

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

public_id:
text

The unique public identifier for the user

all_locations:
boolean

Whether the user has access to all locations or not

active:
boolean

Whether or not this record is currently active and being used

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

is_admin:
boolean

Whether the user is admin or not

is_account_owner:
boolean (required)

Whether the user is account owner or not

is_account_admin:
boolean (required)

(Deprecated) Whether the user is account admin or not

uuid:
uuid (auto-generated)

The UUID of the user record

sales_rep:
boolean

Whether the user is a sales representative or not

name:
string (computed)

First name and Last name of the user (or login if they are not set)

User Example:
{
  "id": 101422
}

Search users of the role

GET /api/roles/{{role_id}}/users

Roles users can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/users/?per_page=1"
client["roles/{{role_id}}/users"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 101461
    },
    "..."
  ]
}
{"total"=>42, "pages"=>5, "results"=>[{"id"=>103947}, "..."]}

Add the role to users

POST /api/roles/{{role_id}}/users

Users can be added to roles by POSTing their IDs to the role's users resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/users"
client["roles/123/users"]
  .post({ "users"=>["222","333"] })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/roles/159775/users/133740

Remove the role from users

DELETE /api/roles/{{role_id}}/users

Users can be removed from roles by sending a DELETE request with their IDs to the role users resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/roles/{{role_id}}/users"
client["roles/{{role_id}}/users"].delete.status
Response:
200
200

Users

Users APIs expose the ability to manage users, roles, and permissions as well as exposing an API to retrieve alerts available for the given user.

Users Records

A user is a record storing information about end users of the system. Users can't be deleted because they are tied to other resources across the application. However, they can be marked as inactive when necessary.

User

id:
integer (auto-generated)

The ID of the user record

login:
text (required)

The unique username for the user

first_name:
text

First name of the user

last_name:
text

Last name of the user

email:
text (required)

Email address

phone:
text

Phone number

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

public_id:
text

The unique public identifier for the user

all_locations:
boolean

Whether the user has access to all locations or not

active:
boolean

Whether or not this record is currently active and being used

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

is_admin:
boolean

Whether the user is admin or not

is_account_owner:
boolean (required)

Whether the user is account owner or not

is_account_admin:
boolean (required)

(Deprecated) Whether the user is account admin or not

uuid:
uuid (auto-generated)

The UUID of the user record

sales_rep:
boolean

Whether the user is a sales representative or not

name:
string (computed)

First name and Last name of the user (or login if they are not set)

User Example:
{
  "id": 105077
}

Search users

GET /api/users

Users can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/users/?per_page=1"
client["users"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 104612
    },
    "..."
  ]
}
{"total"=>42, "pages"=>5, "results"=>[{"id"=>101536}, "..."]}

Create user

POST /api/users

Users are created by POSTing a user record containing the required attributes to the /api/users resource. The password is required and stored encrypted in the database.

When successful, the response headers will contain a Location header that indicates the url at which the newly created user can be found. The user's unique id will be the last portion of that URL.

You can also optionally set the users roles and locations with the create user request. To set roles, you can include role_ids with an array of Role ID's, or include is_admin with true as the value to grant the user full admin access. To set locations, you can pass location_ids with an array of Location ID's, or include all_locations with true as the value to give the user access to all locations.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/users"
client["users"]
  .post({ "first_name"=>"John", "last_name"=>"Doe", "login"=>"johndoe", "email"=>"john@doe.com", "password"=>"password1", "sales_rep"=>"true", "role_ids"=>["111","222"], "location_ids"=>["1111","2222"] })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/users/126817

Update a user

PUT /api/users/{{user_id}}

Users can be edited by PUTting a user record containing a valid subset of user attributes to the user's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}"
client["users/{{user_id}}"]
  .put(my_user_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Update current user

PUT /api/users/profile

Update the current user profile by PUTting a user record containing a valid subset of user attributes to the /profile resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/users/profile"
client["users/profile"].put(my_user_object).status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Get a user

GET /api/users/{{user_id}}

Users can be fetched by GETting the user's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}"
client["users/{{user_id}}"].get.body
Response:
{
  "id": 102394
}
{"id"=>108893}

Roles and Permissions

A user has one or more roles assigned to it. Each role grants at least one permission. For more information on managing roles see Roles.

Role

id:
integer (auto-generated)

The ID of the role record

name:
text (required)

The name of the role

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

users_count:
integer (computed)

Count of users with the role

User Permission

id:
integer (auto-generated)
role_id:
integer (required)
permission_id:
text (required)

Permission Identifier

user_id:
integer (required)
Role Example:
{
  "id": 108662,
  "users_count": 101013
}
User Permission Example:
{
  "id": 100993,
  "role_id": 104426,
  "user_id": 101014
}

Search roles of the user

GET /api/users/{{user_id}}/roles

Users roles can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}/roles/?per_page=1"
client["users/{{user_id}}/roles"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 100410,
      "users_count": 106490
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>[{"id"=>103920, "users_count"=>106542}, "..."]}

Add a new role to the user

POST /api/users/{{user_id}}/roles

Users can be granted roles by POSTing them to the user's roles resource.

When successful, the response headers will contain a Location header that indicates the url at which the newly created role can be found. The role's unique id will be the last portion of that URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{}' "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}/roles"
client["users/123/roles"]
  .post({ "role_id"=>"321" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/users/190948/roles/151626

Get a role of the user

GET /api/users/{{user_id}}/roles/{{role_id}}

User roles can be fetched by GETting the user role's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}/roles/{{role_id}}"
client["users/{{user_id}}/roles/{{role_id}}"]
  .get.body
Response:
{
  "id": 101721,
  "users_count": 102050
}
{"id"=>109481, "users_count"=>106613}

Remove a role from the user

DELETE /api/users/{{user_id}}/roles/{{role_id}}

Remove a role from the user by sending a DELETE request to the user location's URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}/roles/{{role_id}}"
client["users/{{user_id}}/roles/{{role_id}}"]
  .delete.status
Response:
200
200

Search permissions of the user

GET /api/users/{{user_id}}/permissions

Users permissions can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}/permissions/?per_page=1"
client["users/{{user_id}}/permissions"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 100405,
      "role_id": 108896,
      "user_id": 101264
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>[{"id"=>105642, "role_id"=>108865, "user_id"=>107366}, "..."]}

User Locations

A user can have access to one or more locations. Only data attributed to those locations is accessible by the user. For more information on managing locations see Locations.

Location

A location represents a physical space where sales transactions can take place.

id:
Integer (auto-generated)

unique identifier for the record

metadata:
Object

custom key/value information stored about this location

name:
String (required)

The name of the location

address_id:
Integer

The unique identifier for the address record associated with this location

public_id:
String

a unique identifier provided by the user for the purpose of coding information

address_revision_id:
Integer

unique identifier for the current revision of the address associated with this location

active:
boolean

Whether or not this record is currently active and being used

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

timezone_identifier_id:
Integer (auto-generated)

unique identifier for the timezone record

created_by_user_id:
integer (auto-generated)

The user who created this record

updated_by_user_id:
integer (auto-generated)

The most recent user who updated this record

uuid:
String (auto-generated)

Unique identifier for the location

custom:
hash

An object that acts as a container for custom values defined in Heartland Retail as well as any arbitrary values stored by integrations or custom applications.

price_list_id:
Integer (auto-generated)

unique identifier for the price list record

settings:
Object

key/value information stored about this locations settings

credit_cards_configured:
boolean

Whether the location has a credit card processor configured or not

Location Example:
{
  "id": 100001,
  "name": "Boston",
  "address_id": 100004,
  "public_id": "BOS",
  "address_revision_id": 100016,
  "timezone_identifier_id": 1,
  "created_by_user_id": 108278,
  "updated_by_user_id": 100307,
  "uuid": "d0a77755-ddfb-4276-a523-afc7bb959b2d",
  "price_list_id": 1,
  "credit_cards_configured": "false"
}

Search locations of the user

GET /api/users/{{user_id}}/locations

Users locations can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}/locations/?per_page=1"
client["users/{{user_id}}/locations"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 100001,
      "name": "Boston",
      "address_id": 100004,
      "public_id": "BOS",
      "address_revision_id": 100016,
      "timezone_identifier_id": 1,
      "created_by_user_id": 105672,
      "updated_by_user_id": 109808,
      "uuid": "d0a77755-ddfb-4276-a523-afc7bb959b2d",
      "price_list_id": 1,
      "credit_cards_configured": "false"
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>100001,
    "name"=>"Boston",
    "address_id"=>100004,
    "public_id"=>"BOS",
    "address_revision_id"=>100016,
    "timezone_identifier_id"=>1,
    "created_by_user_id"=>103749,
    "updated_by_user_id"=>107335,
    "uuid"=>"d0a77755-ddfb-4276-a523-afc7bb959b2d",
    "price_list_id"=>1,
    "credit_cards_configured"=>"false"},
   "..."]}

Get a location of the user

GET /api/users/{{user_id}}/locations/{{location_id}}

User locations can be fetched by GETting the user location's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}/locations/{{location_id}}"
client["users/{{user_id}}/locations/{{location_id}}"]
  .get.body
Response:
{
  "id": 103713,
  "name": "Boston",
  "address_id": 100004,
  "public_id": "BOS",
  "address_revision_id": 100016,
  "timezone_identifier_id": 1,
  "created_by_user_id": 104562,
  "updated_by_user_id": 105360,
  "uuid": "d0a77755-ddfb-4276-a523-afc7bb959b2d",
  "price_list_id": 1,
  "credit_cards_configured": "false"
}
{"id"=>100001,
 "name"=>"Boston",
 "address_id"=>100004,
 "public_id"=>"BOS",
 "address_revision_id"=>100016,
 "timezone_identifier_id"=>1,
 "created_by_user_id"=>101948,
 "updated_by_user_id"=>107243,
 "uuid"=>"d0a77755-ddfb-4276-a523-afc7bb959b2d",
 "price_list_id"=>1,
 "credit_cards_configured"=>"false"}

Add a new location to the user

POST /api/users/{{user_id}}/locations

Users can be granted access to locations by POSTing them to the user's locations resource.

When successful, the response headers will contain a Location header that indicates the url at which the newly created user location can be found. The user location's unique id will be the last portion of that URL.

If you want to grant access to all locations update the user record with all_locations=true flag instead.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"name":"Boston"}' "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}/locations"
client["users/123/locations"]
  .post({ "location_id"=>"321" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/users/168272/locations/174293

Remove a location from the user

DELETE /api/users/{{user_id}}/locations/{{location_id}}

Remove access to a location from the user by sending a DELETE request to the user location's URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -X DELETE -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}/locations/{{location_id}}"
client["users/{{user_id}}/locations/{{location_id}}"]
  .delete.status
Response:
200
200

Alerts

Alerts represents the events that require the user's attention. Each alert is marked with a color to represent its severity: green, yellow, red.

Alert

type:
text (required)

Type of the alert

color:
text (required)

The severity of the alert. Can be red, yellow or green

message:
text (required)

Human-readable message of the alert

info:
object (required)

Additional information related to the alert

count:
integer (required)

Count of times the alert has been triggered

Alert Example:
{
  "count": 100215
}

Get alerts of the user

GET /api/users/{{user_id}}/alerts

Users alerts can be fetched by querying the user alerts' URL and providing the location_id parameter.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/users/{{user_id}}/alerts/?per_page=1"
client["users/{{user_id}}/alerts"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "count": 103440
    },
    "..."
  ]
}
{"total"=>42, "pages"=>5, "results"=>[{"count"=>107215}, "..."]}

Payment Types

Payment Types APIs expose the ability to manage payment types.

Payment Types

A payment type is a record storing information about payment method.

Payment Type APIs make use of the payment_type record type:

PaymentType

id:
integer (auto-generated)
public_id:
text

The unique public identifier for this payment type. This can be used to provide your own naming scheme to your payment types.

name:
text (required)

Name of the payment type

payment_account_id:
integer

Financial account that you map to will be the account that is debited/credited when the journal entries are synced to a connected financial system.

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

kind:
text

Kind of the payment type (PaymentType by default or GiftCardPaymentType for gift card payments)

Accepted values: PaymentType, GiftCardPaymentType
description:
text

Description of the payment type

custom_field_group:
text (auto-generated)

Name of the custom fields group

key:
text (auto-generated)

Key of the payment type

accept_on_sales_order:
boolean

Whether the payment type is accepted on sales order or not

default_to_amount_due:
boolean

Whether the payment amounts of this type should be defaulted to balance due or not

builtin:
boolean (auto-generated)

Whether the payment type is builtin or not

active:
boolean

Whether or not this record is currently active and being used

payment_process_type:
text

Processing type of the payment type (either manual, webhooks, heartland, or big_commerce). Manual means that the payment is processed with Heartland Retail.

Accepted values: manual, webhooks, heartland, big_commerce
check_balance_url:
text

URL to check the balance for webhooks payment types

capture_url:
text

URL to capture payments for webhooks payment types

refund_url:
text

URL to refund payments for webhooks payment types

void_url:
text

URL to void payments for webhooks payment types

deposit_account_id:
integer

Financial account that you map to will be the account that is credited/debited when the journal entries are synced to a connected financial system.

max_refund_amount:
numeric(10,2)

Maximum amount of allowed refund for the payment type

financial_error:
boolean (computed)

Whether the payment type is configured in financial system correctly or not

expiration_account_id:
integer

(Only for gift card payment types) Financial account that you map to will be the account that is credited/debited when the journal entries of gift card adjustments are synced to a connected financial system.

PaymentType Example:
{
  "id": 105613,
  "public_id": "cash",
  "name": "Cash",
  "payment_account_id": 108735,
  "kind": "PaymentType",
  "description": "Cash",
  "custom_field_group": "payment_type.cash",
  "key": "cash",
  "deposit_account_id": 104389,
  "max_refund_amount": 12.54,
  "expiration_account_id": 103029
}

Search payment types

GET /api/payment_types

Payment Types can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/payment_types/?per_page=1"
client["payment_types"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 109457,
      "public_id": "cash",
      "name": "Cash",
      "payment_account_id": 105744,
      "kind": "PaymentType",
      "description": "Cash",
      "custom_field_group": "payment_type.cash",
      "key": "cash",
      "deposit_account_id": 100219,
      "max_refund_amount": 16.2,
      "expiration_account_id": 104222
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>104498,
    "public_id"=>"cash",
    "name"=>"Cash",
    "payment_account_id"=>108244,
    "kind"=>"PaymentType",
    "description"=>"Cash",
    "custom_field_group"=>"payment_type.cash",
    "key"=>"cash",
    "deposit_account_id"=>103810,
    "max_refund_amount"=>94.78,
    "expiration_account_id"=>107253},
   "..."]}

Create payment type

POST /api/payment_types

Payment types are created by POSTing a payment type record containing the required attributes to the /api/payment_types resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"name":"Cash"}' "https://{{subdomain}}.retail.heartland.us/api/payment_types"
client["payment_types"]
  .post({ "name"=>"Custom Payment Type" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/payment_types/186755

Update a payment type

PUT /api/payment_types/{{payment_type_id}}

Payment types can be edited by PUTting a payment type record containing a valid subset of payment type attributes to the payment type's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"public_id":"cash","name":"Cash","payment_account_id":102349,"kind":"PaymentType","description":"Cash","deposit_account_id":107937,"max_refund_amount":18.38,"expiration_account_id":102942}' "https://{{subdomain}}.retail.heartland.us/api/payment_types/{{payment_type_id}}"
client["payment_types/{{payment_type_id}}"]
  .put(my_payment_type_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Get a payment type

GET /api/payment_types/{{payment_type_id}}

Payment types can be fetched by GETting the payment type's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/payment_types/{{payment_type_id}}"
client["payment_types/{{payment_type_id}}"]
  .get.body
Response:
{
  "id": 104894,
  "public_id": "cash",
  "name": "Cash",
  "payment_account_id": 100133,
  "kind": "PaymentType",
  "description": "Cash",
  "custom_field_group": "payment_type.cash",
  "key": "cash",
  "deposit_account_id": 106648,
  "max_refund_amount": 77.57,
  "expiration_account_id": 107014
}
{"id"=>109583,
 "public_id"=>"cash",
 "name"=>"Cash",
 "payment_account_id"=>109370,
 "kind"=>"PaymentType",
 "description"=>"Cash",
 "custom_field_group"=>"payment_type.cash",
 "key"=>"cash",
 "deposit_account_id"=>101542,
 "max_refund_amount"=>29.83,
 "expiration_account_id"=>103422}

Shipping

Shipping APIs expose the ability to manage shipping methods, carriers and shipments themselves.

Shipping Methods

The shipping methods resource exposes the ability to create and modify shipping methods in the Heartland Retail system.

Shipping Method

id:
integer (auto-generated)

The id of the shipping method record

metadata:
text

Custom key/value information stored about this shipping method

name:
text (required)

The name of the shipping method

active:
boolean

Whether or not this record is currently active and being used

income_account_id:
integer

Financial account that you map to will be the account that is debited/credited when the journal entries are synced to a connected financial system.

amount:
numeric(10,2) (required)

The cost of the shipping method that will be charged to the customer when this shipping method is applied to a Sales Order (if no amount is specified on the order)

uuid:
uuid (auto-generated)
financial_error:
boolean (computed)

Whether the shipping method is configured in financial system correctly or not

Shipping Method Example:
{
  "id": 107998,
  "name": "USPS",
  "income_account_id": 102126,
  "amount": 9.99
}

Search shipping methods

GET /api/shipping/methods

Shipping Methods can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/shipping/methods/?per_page=1"
client["shipping/methods"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 102829,
      "name": "USPS",
      "income_account_id": 104683,
      "amount": 9.99
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>109914, "name"=>"USPS", "income_account_id"=>106255, "amount"=>9.99},
   "..."]}

Get a shipping method

GET /api/shipping/methods/{{method_id}}

Shipping Methods can be fetched by GETting the shipping method's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/shipping/methods/{{method_id}}"
client["shipping/methods/{{method_id}}"].get.body
Response:
{
  "id": 101412,
  "name": "USPS",
  "income_account_id": 101421,
  "amount": 9.99
}
{"id"=>103288, "name"=>"USPS", "income_account_id"=>104881, "amount"=>9.99}

Create a shipping method

POST /api/shipping/methods

Shipping Methods are created by POSTing a shipping method record containing the required attributes to the /api/shipping/methods resource.

The amount specified on a shipping method is the default amount if no amount is specified on the order.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"name":"USPS","amount":9.99}' "https://{{subdomain}}.retail.heartland.us/api/shipping/methods"
client["shipping/methods"]
  .post({ "name"=>"USPS", "amount"=>"50" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/shipping/methods/157898

Update a shipping method

PUT /api/shipping/methods/{{method_id}}

Shipping Methods can be edited by PUTting an shipping method record containing a valid subset of shipping method attributes to the shipping method's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -X PUT -w "%{http_code}" -d '{"name":"USPS","income_account_id":102821,"amount":9.99}' "https://{{subdomain}}.retail.heartland.us/api/shipping/methods/{{method_id}}"
client["shipping/methods/{{method_id}}"]
  .put(my_shipping_method_object)
  .status
Response:
# ...
# Status: 200 OK
# ...

# No Content
=> 200

Reason Codes

Reason Codes APIs expose the ability to manage reason codes.

Gift Card Adjustment Reasons

GiftCardAdjustmentReason

id:
integer (auto-generated)

The id of the adjustment record

name:
text (required)

The name of the reason

description:
text

The description of the reason

account_id:
integer

Financial account that you map to will be the account that is credited/debited when the journal entries are synced to a connected financial system.

active:
boolean

Whether or not this record is currently active and being used

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

builtin:
boolean (auto-generated)

Whether the reason is builtin or not

financial_error:
boolean (computed)

Whether the reason is configured in financial system correctly or not

GiftCardAdjustmentReason Example:
{
  "id": 102679,
  "account_id": 102751
}

Search reason codes

GET /api/reason_codes/gift_card_adjustment_reasons

Gift Card Adjustment Reasons can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/reason_codes/gift_card_adjustment_reasons/?per_page=1"
client["reason_codes/gift_card_adjustment_reasons"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 102703,
      "account_id": 104640
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>[{"id"=>105613, "account_id"=>101925}, "..."]}

Gift Card

Gift Card APIs expose the ability to manage gift cards.

Gift Cards

The gift cards resource exposes the ability to create and modify gift cards in the Heartland Retail system.

GiftCard

id:
integer (auto-generated)

The id of the gift card record

number:
text (required)

The number of the gift card (case insensitive alphanumeric with no special characters)

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

updated_at:
DateTime (auto-generated)

The date and time that this record was last updated (in ISO8601 format)

expires_at:
date

The date after which the gift card may no longer be used

uuid:
uuid (auto-generated)

The uuid of the gift card record

balance:
number (computed)

The current balance of the gift card

GiftCard Example:
{
  "id": 105402,
  "number": "GC12345",
  "expires_at": "2025-08-05"
}

Search gift cards

GET /api/gift_cards

Gift Cards can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/gift_cards/?per_page=1"
client["gift_cards"].query(per_page: 1).get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 103564,
      "number": "GC12345",
      "expires_at": "2025-08-05"
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>107472, "number"=>"GC12345", "expires_at"=>"2025-08-05"}, "..."]}

Get a gift card by its number

GET /api/gift_cards/{{gift_card_number}}

Gift Cards can be fetched by GETting the gift card number's URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/gift_cards/{{gift_card_number}}"
client["gift_cards/{{gift_card_number}}"].get.body
Response:
{
  "id": 100207,
  "number": "GC12345",
  "expires_at": "2025-08-05"
}
{"id"=>108894, "number"=>"GC12345", "expires_at"=>"2025-08-05"}

Create a gift card

POST /api/gift_cards

Gift Cards are created by POSTing a gift card record containing the required attributes to the /api/gift_cards resource.

When successful, the response headers will contain a Location header that indicates the url at which the newly created gift card can be found. The gift card's unique number will be the last portion of that URL.

For more information on available adjustment reasons see Gift Card Adjustment Reasons.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"number":"GC12345"}' "https://{{subdomain}}.retail.heartland.us/api/gift_cards"
client["gift_cards"]
  .post({ "number"=>"GC123456", "reason_id"=>"1", "expires_at"=>"2025-08-05" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/gift_cards/181844

Adjustments

The adjustments resource exposes the ability to modify a gift card balance without a sales transaction.

For more information on available adjustment reasons see Gift Card Adjustment Reasons.

GiftCardAdjustment

id:
integer (auto-generated)

The id of the adjustment record

gift_card_id:
integer (required)

The id of the gift card record

reason_id:
integer (required)

The id of the Reason Code record

created_by_user_id:
integer

The id of the user that created the adjustment

delta_balance:
numeric(10,2) (required)

The amount of the adjustment

created_at:
DateTime (auto-generated)

The date and time that this record was created (in ISO8601 format)

local_created_at:
DateTime (auto-generated)

The date and time that this record was created in the location timezone (in ISO8601 format)

gift_card_number:
text (computed)

Gift Card Number

GiftCardAdjustment Example:
{
  "id": 101985,
  "gift_card_id": 107271,
  "reason_id": 107962,
  "created_by_user_id": 103027,
  "delta_balance": 21.87,
  "local_created_at": "2013-08-05T07:22:24-04:00"
}

Search gift card adjustments

GET /api/gift_card/adjustments

Gift Card Adjustments can be searched for, filtered and sorted by any of their properties. For more information on filtering search results see Filtering.

Request:
curl -H "Authorization: Bearer {{Token}}" "https://{{subdomain}}.retail.heartland.us/api/gift_card/adjustments/?per_page=1"
client["gift_card/adjustments"]
  .query(per_page: 1)
  .get.body
Response:
{
  "total": 42,
  "pages": 5,
  "results": [
    {
      "id": 104537,
      "gift_card_id": 104371,
      "reason_id": 100217,
      "created_by_user_id": 104734,
      "delta_balance": 95.55,
      "local_created_at": "2013-08-05T07:22:24-04:00"
    },
    "..."
  ]
}
{"total"=>42,
 "pages"=>5,
 "results"=>
  [{"id"=>103895,
    "gift_card_id"=>101688,
    "reason_id"=>101351,
    "created_by_user_id"=>103753,
    "delta_balance"=>53.02,
    "local_created_at"=>"2013-08-05T07:22:24-04:00"},
   "..."]}

Get a gift card adjustment

GET /api/gift_card/adjustments/{{adjustment_id}}

Gift Card Adjustments can be fetched by GETting the gift card adjustment's resource URL.

Request:
curl -H "Authorization: Bearer {{Token}}" -w "%{http_code}" "https://{{subdomain}}.retail.heartland.us/api/gift_card/adjustments/{{adjustment_id}}"
client["gift_card/adjustments/{{adjustment_id}}"]
  .get.body
Response:
{
  "id": 104818,
  "gift_card_id": 103959,
  "reason_id": 109905,
  "created_by_user_id": 101258,
  "delta_balance": 74.56,
  "local_created_at": "2013-08-05T07:22:24-04:00"
}
{"id"=>106939,
 "gift_card_id"=>105503,
 "reason_id"=>106499,
 "created_by_user_id"=>102443,
 "delta_balance"=>44.08,
 "local_created_at"=>"2013-08-05T07:22:24-04:00"}

Create a gift card adjustment

POST /api/gift_card/adjustments

Gift Card Adjustments are created by POSTing a gift card adjustment record containing the required attributes to the /api/gift_card/adjustments resource.

Request:
curl -H "Authorization: Bearer {{Token}}" -H "Content-Type: application/json" -w "%{http_code}" -d '{"gift_card_id":105659,"reason_id":105425,"delta_balance":84.55}' "https://{{subdomain}}.retail.heartland.us/api/gift_card/adjustments"
client["gift_card/adjustments"]
  .post({ "gift_card_id"=>"123456", "delta_balance"=>"50", "reason_id"=>"1" })
  .get.body
Response:
# ...
# Status: 201 Created
# ...

# No Content
/api/gift_card/adjustments/139025