Skip to main content

Display Integration

With Display Integration, customers select tips and confirm the payment on a Tippy device (usually a tablet) provided to businesses by Tippy. We provide the UX and guide the customer through selecting the tips and confirming the payment.

v1/display/devices/activate#

If you decide to sync devices so that Tippy generates a unique identifier for Tippy devices, before a device can be used, Tippy will display the identifier on the device's screen and instruct them to enter it into your POS software.

Once they've done that, it's up to you to store that identifier and call /v1/display/devices/activate to let us know that you've done so. Then the device can start being used.

Returns a 200 response on success.

Request body structure (TS notation)

interface ActivateDeviceRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business whose device is being synced
payload: {
deviceId: string; // The unique device identifier
};
}

Request example

POST /v1/display/devices/activate HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"deviceId": "3WJZ12"
}
}

v1/display/transaction/create#

Use this endpoint to create a transaction. This starts the checkout flow on the Tippy device.

The display field specifies what should be displayed on the checkout summary. Each line can be either an item with its quantity and amount, or a category with one or more sub-items. If the items field for a line is provided, it's considered to be a category. Otherwise, it's a single item.

Tipping without service.amount is also available. What this means is that those kind of tips will not be counted in aggregate reports. You could use this option in cases when a customer wants to tip an assistant, etc. Sending 0 for service.amount will be treated as not sending it at all.

To shorten the user experience and avoid displaying payment prompts on the screen, you can pass in isAsync flag and set it to TRUE. This will send the device into idle state after the tip selection when customers press on Continue to Payment button. The device will be ready for new transactions. Transactions with isAsync set to TRUE still require /confirm or /cancel endpoints to bring them to their final state, but those endpoints will be handled in the background and will no longer have any effect on the device state.

In some use cases (e.g.: when editing transaction or when consumer verbaly notifies a staff member of tip amount), it can be desired for POS to preselect tip amounts. In those cases you can pass preselectedTip field with an amount as a value to the service object and the device will not prompt the consumer to select the tip for the staff member. If there are multiple staff members on a given transaction, you can pass preselectedTip for each of them in which case the device will go straight to the review screen.

Returns a 200 response on success.

Request body structure (TS notation)

interface TxCreateRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business where the tx is taking place
payload: {
deviceId: string; // The unique device identifier
transactionId: string; // A unique identifier for this transaction
service: {
// An array of objects with data for each staff member who was part of the service
employeeId: string; // Unique employeeId for staff member
firstName: string; // First name of staff member
lastName: string; // Last name of staff member
avatar: string; // URL for displaying staff member's picture
amount?: number; // The service amount for this staff member, used for tip proposal calculation
preselectedTip?: number; // The preselected tip for this staff member, used to skip tip selection on device
}[];
display: {
// An array of objects representing lines to show on the checkout summary
name: string; // The name of this item or category
qty?: number; // Optional quantity for this item
total: number; // The total amount for this item or category
items?: {
// An optional array of sub-items to include if displaying this line as a category
name: string; // The name of this item
qty: number; // The quantity for this item
amount: number; // The item's price
}[];
expanded?: boolean; // If this line is a category, set this to true if you want its sub-items to be visible initially.
// Otherwise, the user has to tap it to see its sub-items.
}[];
isAsync?: boolean; // Mark transaction as async
total: number; // The grand total to display
customer: {
// An object representing customer data
id: string; // Unique identifier for this customer
firstName: string;
lastName: string;
};
};
}

Request example

POST /v1/display/transaction/create HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"transactionId": "transactionId_14",
"deviceId": "AAAAA-DFD22-DFDDF-5555",
"service": [
{
"employeeId": "employee_123434",
"firstName": "Barbara",
"lastName": "Stamets",
"avatar": "https://path.to/profile-picture.png",
"amount": 150.00
},
{
"employeeId": "employee_342424",
"firstName": "Shaniqua",
"lastName": "Simmons",
"avatar": "https://path.to/profile-picture.png",
"amount": 50.00
}
],
"display": [
{
"name": "Products",
"total": 60.80,
"items": [
{
"name": "Tpy Shampoo",
"qty": 2,
"amount": 20
},
{
"name": "Coloring",
"qty": 1,
"amount": 20.80
}
]
},
{
"name": "Gift card",
"qty": 1,
"total": 15.20,
}
],
"isAsync": false,
"total": 76,
"customer": {
"id": "cus_idx3943843",
"firstName": "Mary",
"lastName": "Smith"
}
}
}

v1/display/transaction/update#

Once a transaction has been created, it's possible to update it (e.g.: if the front desk made a mistake when inputting staff members who were involved in the service).

Use the transactionId to refer to a created transaction.

Returns a 200 response on success.

Request body structure (TS notation)

interface TxUpdateRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business where the tx is taking place
payload: {
deviceId: string; // The unique device identifier
transactionId: string; // A unique identifier for this transaction
service: {
// An array of objects with data for each staff member who was part of the service
employeeId: string; // Unique employeeId for staff member
firstName: string; // First name of staff member
lastName: string; // Last name of staff member
avatar: string; // URL for displaying staff member's picture
amount: number; // The service amount for this staff member, used for tip proposal calculation
preselectedTip?: number; // The preselected tip for this staff member, used to skip tip selection on device
}[];
display: {
// An array of objects representing items to show on the checkout summary
name: string; // The name of this item or category
qty?: number; // Optional quantity for this item
total: number; // The total amount for this item or category
items?: {
// An optional array of sub-items to include if displaying this line as a category
name: string; // The name of this item
qty: number; // The quantity for this item
amount: number; // The item's price
}[];
expanded?: boolean; // If this line is a category, set this to true if you want its sub-items to be visible initially.
// Otherwise, the user has to tap it to see its sub-items.
}[];
isAsync?: boolean; // Mark transaction as async
total: number; // The grand total to display
customer: {
// An object representing customer data
id: string; // Unique identifier for this customer
firstName: string;
lastName: string;
};
};
}

v1/display/transaction/init#

After a transaction is created, the customer will select tips on the Tippy device. Tippy's API will let you know via webhook what the tip selections were. When you've received the tip selections and when the POS terminal is ready to accept the customer's payment, call v1/display/transaction/init. The Tippy device will display an invitation to the customer to tap/dip/swipe their card.

You can also provide a custom amount that will be displayed to the customer when they are prompted to pay. This is useful if, for example, they are using gift cards or they want to settle part of the payment in cash.

If you don't provide a custom amount, the total from the transaction will be displayed.

Request body structure (TS notation)

interface TxInitRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business where the tx is taking place
payload: {
deviceId: string; // The unique device identifier
transactionId: string; // A unique identifier for this transaction
amount?: number; // An optional amount to display
};
}

Request example

POST /v1/display/transaction/init HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"deviceId": "3WJZ12",
"transactionId": "t_132984982",
"amount": 64.32
}
}

v1/display/transaction/confirm#

When you've accepted payment for a transaction, call v1/display/transaction/confirm to let Tippy know. The Tippy device will go back to its home screen and Tippy will distribute the tips among staff members in the background.

Returns a 200 response on success.

Request body structure (TS notation)

interface TxConfirmRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business where the tx is taking place
payload: {
deviceId: string; // The unique device identifier
transactionId: string; // A unique identifier for this transaction
};
}

Request example

POST /v1/display/transaction/confirm HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"deviceId": "3WJZ12"
"transactionId": "t_132984982"
}
}

v1/display/transaction/reject#

If a customer's card is declined or the payment fails for some other reason, call v1/display/transaction/reject. The Tippy device will display a message that the payment failed, along with a reason.

Once you're ready to retry the payment, call v1/display/transaction/init to let the Tippy device know.

Returns a 200 response on success.

Request body structure (TS notation)

interface TxRejectRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business where the tx is taking place
payload: {
deviceId: string; // The unique device identifier
transactionId: string; // A unique identifier for this transaction
note: string; // The reason for why the payment has failed. Will be displayed on the Tippy device
};
}

Request example

POST /v1/display/transaction/reject HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"deviceId": "3WJZ12"
"transactionId": "t_132984982",
"note": "Your card has expired."
}
}

v1/display/transaction/cancel#

If a transaction cannot be completed, call v1/display/transaction/cancel. Tippy will mark the transaction as canceled and return to the home screen.

Returns a 200 response on success.

Request body structure (TS notation)

interface TxCancelRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business where the tx is taking place
payload: {
deviceId: string; // The unique device identifier
transactionId: string; // A unique identifier for this transaction
};
}

Request example

POST /v1/display/transaction/cancel HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"deviceId": "3WJZ12"
"transactionId": "t_132984982",
}
}

v1/display/transaction/last#

If there is any open (incomplete) transaction, call v1/display/transaction/last will return a transactionId, otherwise transactionId will not be present in response.

Returns a 200 response on success.

Request body structure (TS notation)

interface TxLastRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business where the tx is taking place
payload: {
deviceId: string; // The unique device identifier
};
}

Request example

POST /v1/display/transaction/last HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"deviceId": "3WJZ12"
}
}

Response example

Case that incomplete transaction exists

{
"data": {
"transactionId": "132024028403"
}
}

Case when incomplete transaction does not exist

{
"data": {}
}

v1/display/transaction/details#

This endpoint allows you to retrieve the details of a specific transaction based on its unique ID. You will need to provide the transaction ID that was used when creating the transaction in order to access this endpoint.

A successful call to this endpoint will return a 200 OK response with a JSON structure containing the following properties:

  • transactionId (string): The unique ID of the transaction.
  • deviceId (string): The ID of the device where the transaction was handled.
  • status (string): The status of the transaction.
  • isAsync (boolean): Indicates whether the transaction is running in async mode.
  • createdAt (string): The date and time when the transaction was created.
  • customer (object): Information about the customer associated with the transaction.
  • services (array): A list of employees used to create this transaction.
  • tips (array): A list of tips for each employee involved in the transaction.
  • refunds (array): A list of refunds created for this transaction.
  • gross (number): The gross value of the transaction.
  • net (number): The net value of the transaction.
  • fee (number): The fee associated with the transaction.

The status property can be one of the following values:

  • created: The transaction has been created but the tips ware not selected.
  • pending: The transaction is pending and awaiting further action. This status indicates that the user has already chosen tips on the device, and the transaction is waiting for additional actions.
  • confirmed: The transaction has been successfully confirmed and processed.
  • failed: The transaction has been marked as failed.
  • canceled: The transaction has been canceled before it was completed.
  • refunded: The transaction has been fully refunded.

The tips array in the response contains objects representing tips for each employee involved in the transaction. Each tip object has the following properties:

  • employeeId (string): The ID of the employee who received the tip.
  • status (string): The status of the tip.
  • level (string): The tip level, which can be one of the following values: "low," "medium," "high," or "custom."
  • serviceAmount (number): The amount of the service for which the tip is intended.
  • gross (number): The gross value of the tip.
  • net (number): The net value of the tip.
  • fee (number): The fee associated with the tip.
  • createdAt (string): The date and time when the tip was created.
  • included (boolean): True when tip was created with the included flag.

The status property of the tip can be one of the following values:

  • pending: The tip has been created, but the transaction is awaiting confirmation.
  • confirmed: The tip has been confirmed and sent to the employee.
  • void: The tip has been canceled or refunded.

Request body structure (TS notation)

interface TxDetailsRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business where the tx is taking place
payload: {
transactionId: string; // An id of the transaction being retrieved
};
}

Request example

POST /v1/display/transaction/details HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"transactionId": "t_132984982",
}
}

Response example

{
"data": {
"transactionId": "t_132984982",
"deviceId": "3WJZ12",
"status": "confirmed",
"isAsync": false,
"createdAt": "2022-05-17T13:16:49.123Z",
"customer": {
"id": "cus_idx3943843",
"firstName": "Mary",
"lastName": "Smith"
},
"tips": [
{
"employeeId": "employee_1234",
"status": "confirmed",
"level": "custom",
"serviceAmount": 50,
"gross": 21.45,
"net": 20.4,
"fee": 1.05,
"createdAt": "2022-05-17T13:29:34.403Z"
},
{
"employeeId": "employee_1234",
"status": "void",
"level": "custom",
"serviceAmount": 50,
"gross": 41.45,
"net": 40,
"fee": 1.45,
"createdAt": "2022-05-17T13:28:31.009Z"
},
{
"employeeId": "employee_1235",
"status": "confirmed",
"level": "custom",
"serviceAmount": 50,
"gross": 45,
"net": 45,
"fee": 0,
"createdAt": "2022-05-17T13:28:31.009Z",
"included": true
}
],
"refunds": [
{
"transactionId": "TX-000_16-11999076-1684329493597",
"employeeId": "employee_1234",
"status": "completed",
"isPartial": true,
"gross": 20,
"net": 19.6,
"fee": 0.4,
"reason": "Place refund reason here.",
"createdAt": "2022-05-17T13:29:34.403Z"
}
],
"service": [
{
"employeeId": "employee_1235",
"firstName": "Shaniqua",
"lastName": "Simmons",
"avatar": "https://path.to/profile-picture.png",
"amount": 50,
"includedTip": 10
},
{
"employeeId": "employee_1234",
"firstName": "Barbara",
"lastName": "Stamets",
"avatar": "https://path.to/profile-picture.png",
"amount": 50,
"preselectedTip": 5
}
],
"gross": 86.45,
"net": 85,
"fee": 1.45
}
}

v1/display/transaction/refund#

If a transaction is refunded, notify Tippy by calling v1/display/transaction/refund. That way, we can keep track on our side and subtract the refunded amounts from the staff members' tip balances.

A refund can be full or partial. In the case of a full refund, just set partial to false and leave out the amount field. In the case of a partial refund, set partial to true and specify the amount.

If you want to control distribution of the refunded tips between staff members who were part of a service whose transaction is being refunded, use the talents field. talents is an array of object, each object containing an employeeId field that uniquely identifies the staff member and an amount field that specifies what the amount subtracted from that employee's tip balance should be. If you don't specify talents, we split the refund evenly between staff members.

A partial refund amount will always be deducted from the gross amount of the entire transaction (including fees).

If you want to simulate a refund, you can set the parameter dryRun to true. This will return the result with the calculations as if the refund had actually taken place, but nothing will be executed or saved. Suitable if you want to check the numbers before making the refund.

Returns a 200 response on success.

Request body structure (TS notation)

interface TxRefundRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business where the tx is taking place
payload: {
transactionId: string; // An id of the transaction being refunded
partial?: boolean; // Whether this is a partial refund (default: false)
dryRun?: boolean; // Whether this refund should execute or just simulate execution (default: false)
amount?: number; // Partial refund amount
talents?: {
// List of objects specifying distribution amount staff members
employeeId: string; // The employee id of the staff member
amount: number; // The amount to subtract from their tip balance
}[];
reason?: string; // Optional reason for refund
};
}

Response body structure (TS notation)

interface RefundResponse {
data: {
transactionId?: string; // Transaction id of the executed refund
isPartial: boolean; // Whether this was a partial refund or not
employeeId?: string; // The employee id of the staff member effected by the refund if partial refund was set
createdAt: string; // Date time of refund in ISO format
status: string; // Status of refund (for this use case the value will always set to `completed`)
reason: string; // Reason for refund if specified upon request
gross: number; // Refunded amount out of the gross value of the transaction
net: number; // Refunded amount out of the net tip received by the employee
fee: number; // Refunded amount out of the fee paid by the customer
}[];
}

Refund response is an array of objects. If you refund the entire transaction, a response will be single refund object without employeeId information. If you initiate a partial refund with a specific amount, you will receive response object for each of the employees involved in the original transaction.

Request examples

POST /v1/display/transaction/refund HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"transactionId": "t_132984982",
}
}
POST /v1/display/transaction/refund HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"transactionId": "t_132984982",
"partial": true,
"amount": 21.2,
}
}
POST /v1/display/transaction/refund HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"transactionId": "t_132984982",
"amount": 50,
"talents": [
{
"employeeId": "employee_1234",
"amount": 30
},
{
"employeeId": "employee_1235",
"amount": 20
}
]
}
}

Response example

Full refund:

{
"data": [
{
"transactionId": "8e24b727-dbd0-4853-a017-f24a4e7b1130-1677691291322",
"isPartial": false,
"createdAt": "2022-03-01T17:21:31.357Z",
"status": "completed",
"reason": null,
"gross": 13,
"net": 11.92,
"fee": 1.08
}
]
}

Partial refund (amount $6):

{
"data": [
{
"transactionId": "44ce6a10-09ca-4dad-afb3-cc0213ed5826-8254-1677691403832",
"isPartial": true,
"employeeId": "4839",
"createdAt": "2022-03-01T17:23:23.880Z",
"status": "completed",
"reason": null,
"gross": 3.23,
"net": 2.7,
"fee": 0.53
},
{
"transactionId": "44ce6a10-09ca-4dad-afb3-cc0213ed5826-8253-1677691404444",
"isPartial": true,
"employeeId": "1020",
"createdAt": "2022-03-01T17:23:24.480Z",
"status": "completed",
"reason": null,
"gross": 2.77,
"net": 2.4,
"fee": 0.37
}
]
}

v1/display/transaction/transfer#

If you want to re-distribute tips between different staff members once you already collected payments, and do not want to swipe card again, notify Tippy by calling v1/display/transaction/transfer. That way, the tip net amount will be re-distributed.

If you want to control distribution of the transferred tips between staff members who were part of a service, use the talents field. talents is an array of object, each object containing an employeeId field that uniquely identifies the staff member, it's firstName, lastName, and avatar. Those fields are required if we want to distribute tips to staff member that is not created yet on Tippy side. The last field of talents object is amount. That field specifies what amount should be re-distributed to each talent. If you specify amounts, it should be specified for each talent, if not endpoint will return an error, also amounts sum must match the net tip amount of transaction. If you don't specify amount to any talent, we split the net amount evenly between staff members.

Returns a 200 response on success.

Request body structure (TS notation)

interface TxTransferRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business where the tx is taking place
payload: {
transactionId: string; // A unique identifier for this transaction
talents?: {
// List of objects specifying distribution amount staff members
employeeId: string; // The employee id of the staff member
firstName: string; // First name of staff member
lastName: string; // Last name of staff member
avatar: string; // URL for displaying staff member's picture
amount?: number; // The amount of each staff member to distribute
}[];
};
}

Request examples

POST /v1/display/transaction/transfer HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"transactionId": "t_132984982",
"talents": [
{
"employeeId": "employee_1234",
"firstName": "John",
"lastName": "Doe",
"avatar": "https://i.pravatar.cc/150?img=15",
"amount": 20
},
{
"employeeId": "employee_1235",
"firstName": "Lindsay",
"lastName": "Von",
"avatar": "https://i.pravatar.cc/150?img=15",
"amount": 10
}
]
}
}
POST /v1/display/transaction/transfer HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"transactionId": "t_132984982",
"talents": [
{
"employeeId": "employee_1234",
"firstName": "John",
"lastName": "Doe",
"avatar": "https://i.pravatar.cc/150?img=15"
},
{
"employeeId": "employee_1235",
"firstName": "Lindsay",
"lastName": "Von",
"avatar": "https://i.pravatar.cc/150?img=15"
}
]
}
}

v1/display/transaction/update-customer#

This API endpoint allows you to modify the customer information associated with a specific transaction record identified by its unique transaction id.

Request body structure (TS notation)

interface UpdateCustomerRequest {
appId: string; // Your app ID
appSecret: string; // Your app secret
accountToken: string; // The account token for the business
payload: {
transactionId: string; // A unique identifier for this transaction
customer: {
firstName: string; // First name of customer
lastName: string; // Last name of customer
};
};
}

Request example

POST /v1/display/transaction/update-customer HTTP/1.1
Host: https://developer.tippy.app
Content-Type: application/json
{
"appId": "YOUR_APP_ID",
"appSecret": "YOUR_APP_SECRET",
"accountToken": "BUSINESS_ACCOUNT_TOKEN",
"payload": {
"transactionId": "t_132984982",
"customer": {
"firstName": "Tippy",
"lastName": "User"
}
}
}

Response example

{
"data": {
"firstName": "Tippy",
"lastName": "User"
}
}