Best Practices and FAQs

Best Practices

This information provides guidance to avoid common mistakes during new integrations with the PAR POS API, based on actual support cases from vendor and customer integration teams.

Frequency

The best practice for frequency of calls depends on the type of server hosting your group:

  • Multi-tenant servers: 5 concurrent calls with a sleep of 2-3 minutes between calls

  • Single-tenant servers: 10 concurrent calls with a sleep of at least 1 minute between calls

It is not a matter of how many calls are made, but the frequency at which calls are made so as not to take down the API.

Publishing Changes to Locations

  • Changes should be published on one location first, and once tested on a single location, they may be cascaded ahead to other locations.

  • If changes are being done in parallel across multiple locations, single-record changes must happen and batching/bulk operations are not recommended.

  • Changes done in one location should not be cascaded ahead to more than a set of 500 locations.

Batching (performing multiple operations in a single request)

Whenever several save or delete operations are to be performed, the operations can be clubbed as a batched request (more than one operation in a single request). However, batching should adhere to the following guidelines:

  • Batch requests should be sequential. If the system allows for a multi-threaded load, then the recommended Requests per Minute (RPM) for batching should be followed.

  • Batching or bulk operations are not recommended for SavePriceChange and DeletePriceChange.

  • The following applies for both End of Day and Immediate publish:

    • Batch size for save endpoints (except SavePriceChanges) should not be greater than 100. RPM should not be more than 5 requests per minute.

    • Batch size for delete endpoints (except DeletePriceChanges) should not be greater than 20. RPM should not be more than 5 requests per minute.

WSDL Usage

Always try to cache the WSDLs. The WSDLs are on a sandbox server. Never attempt to call it per API call; instead call a cached version of it to avoid an extra hop.

HTTPS vs HTTP

Always use HTTPS instead of HTTP. Using HTTP causes the API to redirect, thus doubling up on the call and causing an excessive load on the server.

TLS

PAR POS only supports TLS version 1.2 or 1.3.

Result Codes

Result codes will always be returned for calls made to the following services:

  • HouseAccounts.svc

  • Kitchen.svc

  • Labor2.svc

  • Ordering.svc

  • Sales2.svc

  • Settings2.svc

A ResultCode = 0 indicates that the call was successful. Always check Result Codes to ensure that your call went through. The Message field in the response will be populated and provide further detail if the call is unsuccessful.

XML Calls

If you are using XML to submit API calls, the XML must be structured according to the order published in the WSDL. Do not change the order of the fields in the request, or it will fail.

Settings Calls

It is unnecessary to continuously pull settings in Settings.svc and Settings2.svc if no changes have been made to the settings. Always call GetLastModifiedTime in Settings2.svc before pulling settings again from either service. Using this call won''t indicate which settings have changed, but the DateTime value returned will indicate when settings have last been updated and can prompt a re-pull of settings.

Frequently Asked Questions

General

I am getting an "Access Denied" response - what does this mean?

If you are receiving an Access Denied from PAR POS in an API response or a ResultCode = 4, then you should check the following:

  • AccessToken - Is your AccessToken correct? Is it correctly placed in the HTTP Web request headers or the request body (depending on the service)?

  • LocationToken - Is your LocationToken correct? Is it correctly placed in the HTTP Web request headers or the request body (depending on the service)?

  • Endpoint - Is your endpoint pointing to the correct server? Is your endpoint HTTPS, not HTTP?

  • Provisioning - Are you provisioned to access this location with your API account''s AccessToken? If not, you can direct any token requests to Main Support at api.support@partech.com or (800) 403-9027.

I am getting successful response codes of "0" for my SubmitOrder calls, but I don''t see my orders - what does this mean?

There is a setting in Settings Editor > Options > Online Ordering > General: Accept Orders When Master Offline that, when checked, will seemingly allow orders to be submitted via the API successfully, but the Register may not receive these orders. You can see if this option is checked, and uncheck it if you prefer for orders to be rejected when the Register is offline.

I am getting successful response codes of "0" for my GetShifts calls, but no shifts are returned - what does this mean?

Check that the DateTime for the BusinessDate parameter you are passing in your request:

  • is in UTC time. You can check the Kind property of the DateTime and specify it to be UTC if not.

  • does not have any offset or time span specified. It should be the BusinessDate with no time span or T00:00:00 in UTC.

The GetShifts call specifically has built-in logic that will not return shifts data if the BusinessDate parameter is not in UTC. We recommend always submitting DateTime objects in UTC.

Can I make an API call for a range of dates?

There are only a select number of calls that allow you to retrieve data for a range of dates:

  • GetHouseAccountCharges in HouseAccounts.svc

  • GetLaborSchedule in Labor2.svc

  • GetFutureDateOrders in Sales2.svc

Otherwise, if you need to retrieve data for multiple dates, you will have to make a loop that calls the API with those business dates individually.

I am interested in live dashboarding - how can I accomplish this?

PAR POS highly recommends making 5-10 concurrent calls with a sleep of 2-3 minutes as best practice. To further avoid latency issues or redundant calls, we propose making calls that only obtain data that has changed since the last call.

Live dashboarding or live data calls include:

  • GetOrders in Sales2.svc - using the ModifiedTime field

  • GetShifts in Labor2.svc - using the ModifiedTime field

  • Any calls in Settings.svc and Settings2.svc - using the GetLastModifiedTime call

This method allows you to retrieve only data that has been added or modified since (and including) the ModifiedTime you specify, in the case of the GetOrders and GetShifts calls. For Settings.svc and Settings2.svc, it is unnecessary to continuously pull settings if no changes have been made. Using GetLastModifiedTime won''t indicate which settings have changed, but the DateTime returned will indicate when settings were last updated and can prompt a re-pull.

If you are interested in live dashboarding or live data, please contact API Support at api.support@partech.com to assist you with that integration.

Services

How do I make a SaveEmployees call to Settings2.svc?

You can save new employees, retrieve existing employees, and update employee information with the SaveEmployees call to Settings2.svc.

Create: Add one or more new Employee(s)

You can add one or more Employees to PAR POS by populating the Employees array in the SaveEmployees call with Employee objects. The creation of any object within SaveEmployees is denoted with a negative integer Id of at least -1 for the first object created, and decrementing for each new object in the request (e.g., -2, -3, etc.).

Employees = [
  Employee:
    Id = -1,
    DisplayName = "John Doe",
    FirstName = "John",
    Jobs:
      EmployeeJob:
        Id = -2,
        JobId = 101,
        PayRate = 10,
        SecurityLevelId = 1,
    LastName = "Doe"

  Employee:
    Id = -3,
    DisplayName = "Jane Doe",
    FirstName = "Jane",
    Jobs:
      EmployeeJob:
        Id = -4,
        JobId = 202,
        PayRate = 20,
        SecurityLevelId = 1,
    LastName = "Doe"
]

John Doe and Jane Doe were created in PAR POS because their Employee objects were denoted with IDs of -1 and -3, respectively. Because EmployeeJob objects were also created under each Employee, those were denoted with IDs of -2 and -4, keeping the increasingly negative sequential order within the request.

When you retrieve Employees through the GetEmployees call in Settings2.svc, the ID fields will be populated with unique PAR POS-assigned Ids for the Employee or EmployeeJob object that was created.

The minimally required fields to save a new employee are Id, DisplayName, FirstName, and LastName.

Note: Employee IDs are unique on a per-location basis. If Employee John Doe works at multiple locations, he will have a different EmployeeId at each location. Pushing the same Employee IDs across multiple locations is not possible; you would have to create the employee at each separate location.

Read: Retrieve all existing employees

You can retrieve all existing employees from PAR POS for a location with the GetEmployees call. It is important to make this call before updating an employee, since the response will return the IDs that you need to target in order to update an Employee, as well as the existing data you''ll have to repopulate in your request.

Collection = [
  Employee:
    Id = 10001,
    DisplayName = "John Doe",
    FirstName = "John",
    Jobs:
      EmployeeJob:
        Id = 10002,
        JobId = 101,
        PayRate = 10,
        SecurityLevelId = 1,
    LastName = "Doe"

  Employee:
    Id = 10003,
    DisplayName = "Jane Doe",
    FirstName = "Jane",
    Jobs:
      EmployeeJob:
        Id = 10004,
        JobId = 202,
        PayRate = 20,
        SecurityLevelId = 1,
    LastName = "Doe"
]

Update: Update the information of an existing Employee

You can update one or more existing employees in PAR POS by populating the Employees array in the SaveEmployees call with Employee objects distinguished by the EmployeeId. In your request, include all Employee fields that you intend to update with new values, along with fields populated with already-existing data.

If you include fields with no value or exclude fields altogether in your request, you will overwrite any existing values for those Employee fields. Likewise, if you do not include all existing EmployeeJobs in a Jobs array, they will be overwritten as well.

Employees = [
  Employee:
    Id = 10001,
    DisplayName = "John D.",
    FirstName = "John",
    Jobs:
      EmployeeJob:
        Id = 10002,
        JobId = 101,
        PayRate = 15,
        SecurityLevelId = 1,
    LastName = "Doe"

  Employee:
    Id = 10003,
    DisplayName = "Jane Doe",
    FirstName = "Jane",
    Jobs:
      EmployeeJob:
        Id = 10004,
        JobId = 202,
        PayRate = 20,
        SecurityLevelId = 1,
      EmployeeJob:
        Id = -1,
        JobId = 303,
        PayRate = 30,
        SecurityLevelId = 1,
    LastName = "Doe"
]

The first part targets Employee 10001 (John Doe), updating his DisplayName to "John D." and PayRate to 15. Other fields should also be populated with their existing values. The second part targets Employee 10003 (Jane Doe), adding a new EmployeeJob denoted with EmployeeJobId = -1 while retaining her original job. Both EmployeeJob records must be included in her Jobs array, otherwise her original EmployeeJob will be overwritten.

Delete: Removing an employee

Once an Employee is created in PAR POS, the Employee object cannot be deleted and will always be returned via the GetEmployees call. There are two options to "remove" an Employee:

  1. Target an Employee and replace their fields with null or other string values, effectively overwriting their information.

  2. Filter out Employees whose Terminated field has a value of true.

Employees = [
  Employee:
    Id = 10001,
    DisplayName = "REMOVED",
    FirstName = "REMOVED",
    LastName = "REMOVED"

  Employee:
    Id = 10003,
    DisplayName = "Jane Doe",
    FirstName = "Jane",
    Jobs:
      EmployeeJob:
        Id = 10004,
        JobId = 202,
        PayRate = 20,
        SecurityLevelId = 1,
      EmployeeJob:
        Id = 10005,
        JobId = 303,
        PayRate = 30,
        SecurityLevelId = 1,
    LastName = "Doe"
    Terminated = true,
    TerminationDate = "2019-01-01T00:00:00"
]

All of John Doe''s personal information has been removed and his Jobs array has been emptied; however, the Employee object with EmployeeId: 10001 will remain since it cannot be deleted. Jane Doe''s personal information persists in PAR POS, but she is recognized as terminated through the Terminated field being set to true. Populating TerminationDate for a terminated Employee is optional but recommended.

How do I make a SubmitOrder call to Ordering.svc?

You can submit a future order to a location through the API using the SubmitOrder call to Ordering.svc.

Submitting a Basic Order

There are three parts to a SubmitOrder request: Options, Order, and SuppressConfirmationEmail.

  1. Options - contains the CalculateItemPrice field. Set to true or false to indicate whether the Order should be calculated with default pricing or with custom pricing outlined in the request.

  2. Order - the structure of a NewOrder object.

  3. SuppressConfirmationEmail - set to true or false to indicate whether a confirmation email should be suppressed upon completion of the request.

Options:
  CalculateItemPrice = true

Order:
  DestinationId = 1,
  FutureOrder:
    PickupTime = "2019-01-01T20:00:00",
    OffsetMinutes = 0,
  Items:
    NewOrderItem:
      Id = 1,
      ItemId = 12345,
      DestinationId = 1,
  Name = "Online Order"

SuppressConfirmationEmail = true

At a minimum, an Order requires 4 fields populated: DestinationId, FutureOrder, Items, and Name.

  1. DestinationId - the Id of the Destination to which this Order will be sent.

  2. FutureOrder - because all orders submitted via the API are considered future orders, this field requires a PickupTime. The Register uses this DateTime to determine whether this is a FutureDateOrder (placed at least one day or more in advance), and when to open and/or send the Order to the kitchen depending on the amount of time required for prep.

  3. Items - at least one NewOrderItem is required in this array.

    • The creation of objects within SubmitOrder is denoted with a positive integer Id of at least 1 for the first object, incrementing for each new object (e.g., 1, 2) - unlike SaveEmployees, which uses decrementing negative integers.

    • The ItemId needs to map to the Id of the Item being ordered.

    • Each NewOrderItem also needs a DestinationId to which it will be sent. This typically should be the same DestinationId at Order-level.

  4. Name - a name for the Order is required to distinguish the Order on the register.

Submitting a Combo Order

When submitting a combo order, the Ids of the NewOrderItem within the Order, Item, and Component must all be validated against the Items Settings (retrievable by GetItems).

Suppose you want to submit an order for an "API Combo" with ItemId = 111 (the Parent Item). It has three components:

  1. Sandwich - ComponentId = 200 has two options:

    • Burger - ItemId = 201

    • Chicken Sandwich - ItemId = 202

  2. Side - ComponentId = 300 has two options:

    • Fries - ItemId = 301

    • Tots - ItemId = 302

  3. Drink - ComponentId = 400 has two options:

    • Soft Drink - ItemId = 401

    • Juice - ItemId = 402

To submit a combo order for a Burger, Fries, and Soft Drink, the Items in your Order should be formatted like this:

Items:
  NewOrderItem:
    Description = "API Combo",
    Id = 1,
    ItemId = 111,
    ComboItems:
      NewOrderItem:
        Description = "Burger",
        Id = 2,
        ItemId = 201,
        ComponentId = 200,
        DestinationId = 1,
      NewOrderItem:
        Description = "Fries",
        Id = 3,
        ItemId = 301,
        ComponentId = 300,
        DestinationId = 1,
      NewOrderItem:
        Description = "Soft Drink",
        Id = 4,
        ItemId = 401,
        ComponentId = 400,
        DestinationId = 1,
    DestinationId = 1

The Parent Item ("API Combo") has the 3 nested items within its ComboItems array. Each Child Item has the ComponentId indicating which component of the combo the item fulfills (Sandwich, Side, or Drink) and the ItemId of the Item selected to fulfill that component. Each NewOrderItem (Parent or Child) still requires an incrementing Id and a DestinationId.

Submitting an attached payment with an Order

SubmitOrder supports multiple NewOrderPayment objects in the Payments array. There are additional fields associated with specific payment types, so the NewOrderPayment type must be specified when attaching a payment. Available types: NewCheckPayment, NewCreditCardPayment, NewExternalPayment, NewGiftCardPayment, and NewGiftCertificatePayment.

Example with a NewCreditCardPayment:

Order:
  DestinationId = 1,
  FutureOrder:
    PickupTime = "2019-01-01T20:00:00",
    OffsetMinutes = 0,
  Items:
    NewOrderItem:
      Id = 1,
      ItemId = 12345,
      DestinationId = 1,
  Name = "Online Order",
  Payments:
    NewOrderPayment (of type "NewCreditCardPayment"):
      Amount = 10.00,
      Id = 2,
      TenderId = 5,
      TipAmount = 2.00,
      AccountNumber = 1234123412341234,
      City = "New York",
      Country = "US",
      CVV = "321",
      ExpirationDate = "0120",
      NameOnCard = "CustomerName",
      PostalCode = "12345",
      State = "CA",
      StreetAddress = "123 PAR St."

All NewOrderPayment objects share the same base fields: Amount, Id (in line with the incrementally increasing IDs within the order), TenderId (corresponds to the payment tender type Id retrievable through GetTenders), and TipAmount if any is attached. The remaining fields are specific to the NewCreditCardPayment type, which can be submitted if there is a Credit Card processor set up for the location to accept online payments.

Example with a NewExternalPayment:

Payments:
  NewOrderPayment (of type "NewExternalPayment"):
    Amount = 10.00,
    Id = 2,
    TenderId = 6,
    TipAmount = 2.00,
    ReferenceDetails:
      PaymentReferenceDetail:
        Name = "CardType",
        Value = "Visa"

External payments are processed externally by an integrator and submitted as a NewExternalPayment type with an external tender. This is for the Register to know that no other payments need to be processed for the order.