# Dojo Pay at Counter Documentation > Guides for integrating Pay at Counter (PAC) — terminal sessions, captures, refunds, and pre-authorisation. API quick reference: - **API base URL**: https://api.dojo.tech - **Environment selection**: Sandbox and production both use `https://api.dojo.tech`; the API key determines which environment receives the request - **Authentication**: `Authorization: Basic ` (literal `Basic ` prefix; do not base64-encode `api_key:`) - **Version header**: `version: 2026-02-27` PAC integrations usually create a payment intent first, then bind it to a terminal session for collection on a Dojo card terminal. Sandbox and production both use https://api.dojo.tech; the API key determines which environment receives the request. On terminal endpoints, send `software-house-id` and `reseller-id`. In sandbox you can use the literal values `softwareHouse1` and `reseller1`; production integrations receive assigned values from Dojo. Terminal-session statuses follow the OpenAPI enum. Distinguish them from payment-intent states such as `Reversed` or `Refunded`. Machine-readable specs: - [Dojo API v3](https://docs.dojo.tech/api/v3/bundled.json) - [Payments API (legacy)](https://docs.dojo.tech/payments/bundled.json) For complete PAC integration, also load: - `llms-payment-intents.txt` — payment lifecycle, create, refund, capture, and setup intent guidance - `llms-epos.txt` — EPOS capability registration and POS contract details This file contains all documentation content in a single document following the llmstxt.org standard. ## Pay at Counter >Learn how to capture or refund payments on a terminal, create a terminal session or retrieve a terminal. [![](https://docs.dojo.tech/images/dojo-icons/Bank.svg) **Terminals** The first step in any terminals integration.](terminals) [![](https://docs.dojo.tech/images/dojo-icons/Coins.svg) **Capture a payment** Step-by-step guide to capture a payment on a terminal.](./terminals/step-by-step-guide) [![](https://docs.dojo.tech/images/dojo-icons/Calculator.svg) **Create a session** Create a terminal session to start accepting payments.](./terminals/create-session) [![](https://docs.dojo.tech/images/dojo-icons/Email.svg) **Retrieve a terminal** Retrieve a terminal and accept payments.](./terminals/get-terminal) [![](https://docs.dojo.tech/images/dojo-icons/PenNib.svg) **Verify signature** Verify customer signatures in verification-required payments.](./terminals/signature-verification) [![](https://docs.dojo.tech/images/dojo-icons/PaymentCards.svg) **Cancel session** Cancel a terminal session.](./terminals/cancel-session) [![](https://docs.dojo.tech/images/dojo-icons/Tick.svg) **Go-live checklist** Ensure your integration is ready for production.](./go-live-checklist-f2f) [![](https://docs.dojo.tech/images/dojo-icons/Headset.svg) **Need a hand?** Our support team is always happy to help with any questions you have.](https://support.dojo.tech/hc/en-gb) --- ## Go-live checklist To ensure you have a smooth onboarding with face-to-face payments on your EPOS, Dojo has prepared a comprehensive testing checklist that you can follow before going live with your integration. Please note that we mandate that at least one method of refunding is achievable through your integration to our API. This can be achieved directly through the [Payment Intent](https://docs.dojo.tech/api#tag/Refunds/operation/Refunds_Create) to refund to the original card used for payment, or by [creating a terminal session](https://docs.dojo.tech/api/#tag/Terminal-sessions/operation/TerminalSession_Create) either matched to a Payment Intent, or unmatched. Configuration These tests relate to how your EPOS is setup to interact with the Dojo API, taking into account authorization flow, user experience, and versioning. | Scenario | Steps to Reproduce | Expected Outcome | |-------|-----|----| | Authorization | See where these details are configured. This might not need to be in the UI of the EPOS software itself, but it could be in a configuration file, a database elsewhere, or even in a back-office system that end-users log in to. Also, record who is expected to enter the Cloud credentials: yourself, an installer, or the merchant. | POS allows configuring different API Keys. The API Key should never be hard-coded. | | Incorrect authorization credentials | Input an invalid API Key (You can simply input an extra character in the API key) | The EPOS should display an error prompting the user to check that the API key is correct. | | Retrieve terminal(s) | EPOS must be able to check for available terminals, preferably using the GET [/terminals](https://docs.dojo.tech/api#tag/Terminals) endpoint, or at a bare minimum offer the ability to manually enter the required `TID`. | POS should be able to check for available terminals or enable you to configure preferred `TID`. This should never be hard-coded. | | API version | Note which version of the API you are testing| This data is to be provided during QA.| | POS version | For our records, we prefer to keep a note of the minimum version of your POS which is compatible with this API - this allows us to track historic versions which may be incompatible. If this isn't providable (such as in the instance where only one version of the POS exists) then this step can be omitted. | Data to be provided during QA completion. | Payment intents These tests concern the creation of and management of payment intents, as well as additional features like `itemLines` and Webhooks. | Scenario | Steps to Reproduce | Expected Outcome | |------------|------|---------------------------| | [Create] Payment intent (Auto) | Initiate a sale using capture mode `AUTO` | Payment intent created successfully with the capture mode set to `AUTO` | | [Create] Payment intent (Manual) [Optional] | Initiate a sale using `captureMode: Manual` | Payment intent created successfully with the capture mode set to `Manual` (Optional) | | [Retrieve] Payment intent | GET [/terminals](https://docs.dojo.tech/api#tag/Payment-intents/operation/PaymentIntents_Get) the final result/transaction details from the [Payment Intents](https://docs.dojo.tech/api#tag/Payment-intents)API after the terminal session has concluded. | The POS recognizes the status of the intent and records the sale on the POS | | [Change] Payment intent amount [Optional] | Using the payment intent from a successful pre-authorization, use the POST [/payment-intents/{paymentIntentId}/amount](https://docs.dojo.tech/api#tag/Payment-intents/operation/PaymentIntents_SetCustomAmount) endpoint - This is before any pre-authorization or terminal session has taken place | Payment intent amount successfully updated| | [Cancel] Payment intent [Optional] | Cancel a payment that has not yet been authorized by using DELETE [/payment-intents/{paymentIntentId}](https://docs.dojo.tech/api#tag/Payment-intents/operation/PaymentIntents_Delete) | Payment intent is successfully cancelled | | [List] All Payment intents [Optional] | List all payment intents by posting to POST [/payment-intents/search](https://docs.dojo.tech/api#tag/Payment-intents/operation/PaymentIntents_Search). | Retrieves a list of payment intents. Results are paginated. By default, the method returns up to 50 payment intents. | | Create Payment intent - MOTO (CNP) | Initiate a transaction with `cardHolderNotPresent` set to **true**. | The card machine should skip the Present Card screen and go directly into Key Card Number | | itemLines | Use item lines to define checkout/bill items, and modifiers where applicable | Item lines should be correctly displayed and if discounts are present, these discounts should correctly display as modifiers. | | Webhooks | Subscribe to Webhook events to be notified of specific events. | List of all events subscribed to by sending a GET request to Dojo's webhooks endpoints. | Refunds These tests concern the refunding of payments by directly invoking the Payment Intent, avoiding the need for a card to be re-presented. | Scenario | Steps to Reproduce | Expected Outcome | |------------|------|---------------------------| | Create a full refund | Initiate a refund of a previously completed Payment Intent by POSTing to the refunds endpoint. | A 200 response is returned containing a `refundId`. The POS should then call GET on the Payment Intent ID, parse the final result of the intent (`Refunded`), and display that the payment was refunded. | | Create a partial refund | Initiate a refund of a previously completed Payment Intent by POSTing to the refunds endpoint. Select an amount lower than the original Payment Intent value. | A 200 response is returned containing a `refundId`. The POS should then call GET on the Payment Intent ID, parse the final result of the intent (`Captured`), and display that the payment was partially refunded. | [Auto] Create Terminal Session - Sale These tests cover the sales flow of terminal sessions created using `CaptureMode: Auto`, meaning that the transaction will be completed immediately. We look to cover as many possible transaction outcomes, including less common but still crucial flows such as `Expired` and `Signature Verification`. | Scenario | Steps to reproduce | Expected outcome | |----------|-------|---------| | Create terminal session - Successful while inserting card | Complete a transaction by inserting a card and using PIN entry or by using a Virtual Machine with `TID` ending in **SIP0**. | Must display transaction status of `Captured`. | | Create terminal session - Successful with cashback | Complete a transaction with cashback amount specified. | Cashback should be applied to the amount displayed on the card machine and recorded by the POS. | | Create terminal session - Successful with gratuity | Complete a transaction with gratuity amount specified. | Gratuity amount should be recorded by the POS. | | Create terminal session - Successful with contactless | Complete a transaction by presenting card to contactless reader. | POS should display result of Captured. | | Create terminal session - Successful with signature | Initiate a transaction by using a Virtual Machine with `TID` ending in **SIP0**. | POS should provide user with a dialogue box specifying that signature verification is required, and give the user an option to either accept or reject the signature. Selecting the accept option should send a PUT [/terminal-sessions/{terminalSessionId}/signature](https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Signature) request with `accepted: true` and lead to a successful payment. | | Create terminal session - Successful with signature after 80 seconds | Initiate a transaction by using a Virtual Machine with `TID` ending in **SIP0**. At the status `SignatureVerificationRequired`, do not send a decision for 80 seconds. The signature step should time out as accepted, allowing the sale to continue to a successful outcome. | POS should display result of `Captured` for an auto-capture sale. | | Create terminal session - Declined | Initiate a transaction by using a Virtual Machine with `TID` ending in **DIP0**. | POS should display status Declined. POS should re-use Payment Intent if transaction is re-attempted. | | Create terminal session - Unsuccessful with signature rejection | Initiate a transaction by using a Virtual Machine with `TID` ending in **SIP0**. | POS should provide user with a dialogue box specifying that signature verification is required, and give the user an option to either accept or reject the signature. Selecting the reject option should send a PUT [/terminal-sessions/{terminalSessionId}/signature](https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Signature) request with `accepted: false` and lead to a declined payment. | | Create terminal session - Late signature rejection after 80 seconds | Initiate a transaction by using a Virtual Machine with `TID` ending in **SIP0**. At the status `SignatureVerificationRequired`, do not send a decision for 80 seconds so the signature step times out as accepted. Then attempt to reject it. | POS should still display the successful result that was already finalized after the timeout (`Captured` for an auto-capture sale). | | Create terminal session - Failure with busy terminal | Initiate a sale while PDQ is in menu.| POS should display that PDQ is not available. | | Create terminal session - Cancel transaction from PDQ | Initiate a sale and cancel by selecting the X at the top of the PDQ screen before the card has been presented. | POS should display a result of Canceled. | | Create terminal session - Cancel transaction from POS - Successful | Initiate a sale and cancel by sending a PUT [/terminals](https://docs.dojo.tech/api#tag/Terminal-sessions/operation/TerminalSession_Cancel) to the cancel endpoint before the card has been presented. | POS should confirm status via polling and display a result of Canceled. | | Create terminal session - Cancel transaction from POS - Unsuccessful | Initiate a sale and cancel by sending a PUT [/terminals](https://docs.dojo.tech/api#tag/Terminal-sessions/operation/TerminalSession_Cancel) to the cancel endpoint after the card has been presented. | POS should confirm status via polling and transaction flow should be unaffected. | | Create terminal session - Expired | Initiate a transaction by using a Virtual Machine with `TID` ending in **TIP0** to retrieve a final status of `Expired`. | POS should display a dialogue advising that the result of the transaction cannot be confirmed and provide an option to manually record the sale on the POS as successful or retry if unsuccessful. | | Terminal session - polling rate | POS should call GET on terminal session regularly (once per second) to receive `notificationEvents` and `TerminalSessionStatus` updates | Polling should occur regularly enough that status updates are captured and displayed appropriately. | | Terminal session - display notifications | Complete several transactions invoking successful results, cancelation requests and signature verification to ensure relevant statuses are recorded effectively. | Terminal should display relevant `notificationEvents` and `TerminalSessionStatus` messages to the user to confirm the current state of the card machine. | [Manual] Create Terminal Session - Preauth These tests cover the sales flow of terminal sessions created using `captureMode: Manual`, meaning that the transactions will be pre-authorized and then captured subsequently. We will also look to cover behaviours such as incrementing the pre-authorized amount to ensure that the user experience is consistent. | Scenario | Steps to Reproduce | Expected Outcome | |-----|-------|-------| |Create terminal session - Successful with inserting card | Create a payment intent with capture mode set to manual. Create a terminal session and successfully authorize the payment using Chip and Pin or device verification. | Must display transaction status of Authorized. | | Create terminal session - Successful with contactless | Create a payment intent with capture mode set to manual. Create a terminal session and successfully authorize the payment using device verification. | Must display transaction status of Authorized. | | Create terminal session - Successful with different card type | Create a Payment intent with capture mode set to manual. Create a terminal session and successfully authorise the payment using a different card type to that used previously. | Must display transaction status of Authorized. | | Create terminal session - Declined | Initiate a transaction by by using a Virtual machine with TID ending "DIP0". | POS should display status Declined. POS should re-use Payment Intent if transaction is re-attempted. | | Update intent - Successful with increasing amount | Using the payment intent from a successful pre authorisation, use the "change payment intent amount" endpoint to increase the pre-authorized amount. | POS should receive a response with “Authorized” and display this. | | Update intent - Unsuccessful amount change | Try to update a previously captured payment intent. | The POS would display the error returned from the Dojo API, such as **Invalid request for payment with id `pi_sandbox_EaaC8tw2fUKVVoSUt5JgJQ`. The status `Captured` does not allow this change.** | | Update intent - Failure with non-existing payment intent id | Try to update an intent which does not exist. | The POS would ideally protect the user from this use case ever happening. | | Update intent - Failure when updating amount to zero | Try to update an intent to an amount `0`. | The POS would ideally protect the user from this use case ever happening. The POS would error with amount must be higher than `0`. | [Auto] Create Terminal Session - Refund These tests cover the refund flow of terminal sessions created using `captureMode: Auto`, meaning that the refunds will be completed immediately. | Scenario | Steps to Reproduce | Expected Outcome | |-----|-------|-------| |Create terminal session [Unlinked Refund - Successful]| Initiate an unlinked refund which is then authorized successfully. | The POS should parse the status of `Captured` returned in the terminal-session object and display that the refund was successful. | |Create terminal session [Unlinked Refund - Declined]| Initiate an unlinked refund which fails to authorize. | The POS should display that the refund was declined. | |Create terminal session [Unlinked Refund - Expired]| Initiate an unlinked refund and remove the battery during the authorization, allowing the transaction to expire after several seconds, or by using a Virtual Machine ending in "TIP0" to retrieve a final status of `Expired`. | POS should display a dialogue advising that the result of the refund cannot be confirmed, Please check the card machine screen for the result and provide an option to manually record the refund on the POS as successful or retry if unsuccessful. | |Create terminal session [Unlinked Refund - Cancelled]| Initiate an unlinked refund and cancel by selecting the X at the top of the PDQ screen before the card has been presented. | POS should display a result of Canceled. | |Create terminal session [Unlinked Refund - Successful with signature accepted]| Initiate an unlinked refund by by using a Virtual machine with TID ending "SIS0". | POS should provide user with a dialogue box specifying that signature verification is required, and give the user an option to either accept or reject the signature. Selecting the accept option should send a PUT of `"accepted": true` to the signature endpoint and lead to a successful refund. | |Create terminal session [Unlinked Refund - Unsuccessful with signature rejected]| Initiate an unlinked refund by by using a Virtual machine with TID ending "SIS0". | POS should provide user with a dialogue box specifying that signature verification is required, and give the user an option to either accept or reject the signature. Selecting the Decline option should send a PUT of `"accepted": false` to the signature endpoint and lead to a declined payment. | |Create terminal session [Matched Refund - Successful]| Initiate a matched refund which is then authorized successfully. | The POS should call GET on the Payment Intent, retrieve the status of `Refunded` and confirm this to the user. | |Create terminal session [Matched Refund - Declined]| Initiate a matched refund which fails to authorize. | The POS should display that the refund was declined. | |Create terminal session [Matched Refund - Expired]| Initiate a transaction by by using a Virtual machine with TID ending "TIP0" to retrieve a final status of `Expired`. | POS should display a dialogue advising that the result of the refund cannot be confirmed, Please check the card machine screen for the result and provide an option to manually record the refund on the POS as successful or retry if unsuccessful. | |Create terminal session [Matched Refund - Cancelled]| Initiate a matched refund and cancel by selecting the X at the top of the PDQ screen before the card has been presented. | POS should display a result of Canceled. | |Create terminal session [Matched Refund - Successful with signature accepted]| Initiate a matched refund by by using a Virtual machine with TID ending "SIS0". | POS should provide user with a dialogue box specifying that signature verification is required, and give the user an option to either accept or reject the signature. Selecting the accept option should send a PUT of `"accepted": true` to the signature endpoint and lead to a successful refund. | |Create terminal session [Matched Refund - Unsuccessful with signature rejected]| Initiate an unlinked refund by by using a Virtual machine with TID ending "SIS0". | POS should provide user with a dialogue box specifying that signature verification is required, and give the user an option to either accept or reject the signature. Selecting the Decline option should send a PUT of `"accepted": false` to the signature endpoint and lead to a declined payment. | Error testing These tests ensure that the POS is able to handle errors which will be returned from our API, ensuring that a valid error message and call to action is always displayed to the user. | Error testing | Description / Steps | |--------------|---------------------| |HTTP status code 400 - Bad Request| Occurs in the instance that a bad request is provided - this should be addressed prior to release by resolving the bad request. | |HTTP status code 401 - Unauthorized| Occurs when an invalid API Key is submitted. It's important, particularly in customer-driven installations, to provide clear user error-messaging here to ask that the credentials submitted by checked. | |HTTP status code 404 - Not found (TID unavailable)| In the event that the terminal is unavailable (offline, or powered off), the POS should advise the user accordingly and ask that they check the device status. | |HTTP status code 422 - Unprocessable Entity| This can be tested by attempting to cancel a payment outside of the "Insert Card" screen. The POS should respond and carry on with the previous request, and ideally advise the user of why the error occurred (eg: Payment can only be cancelled during the "Initiated" phase). | --- ## Cancel session >Cancel a terminal session. It might be necessary to cancel a terminal session without payment. ## Path When you need to cancel a terminal session, you can use the endpoint: > See OpenAPI spec for PUT endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Cancel Note that the API method for cancelling a terminal session is `PUT` rather than `DELETE`. The session itself is not deleted, and it can be called up using the [get terminal](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/get-terminal) operation for reference. > **Note:** If payment has been attempted or completed > on a terminal session, it will no longer be possible to cancel it. This includes failed payment attempts. In the successful cancel session response, the `status` of the session will change to `CancelRequested`. The status will first move to `CancelRequested`. Finally, `status` of the session will change to `Canceled`. ## Sample requests ```bash # The sandbox API key passed in 'authorization' is public. # Don't submit any personally identifiable information in any requests made with this key. # Sign in to developer.dojo.tech to create your own private sandbox key and use that instead # for secure testing. curl --location --request PUT 'https://api.dojo.tech/terminal-sessions/ts_sandbox_65af01524a36e6a14356dbc0/cancel' \ --header 'version: 2024-02-05' \ --header 'software-house-id: softwareHouse1' \ --header 'reseller-id: reseller1' \ --header 'Accept: application/json' \ --header 'Authorization: Basic sk_sandbox_1WYDtq7yAdqhmQ7KEUAvPlCCRBYc9HTY9KOPJKZtfWkzsSISj1L8c4GG5l4pBB5Bj85hkJgTL9vmOmki5QnQfQ' ``` [![](https://docs.dojo.tech/images/dojo-icons/TerminalWindow.svg) **API reference** Learn about the API and integrate with API endpoints.](/api/#tag/Terminals) [![](https://docs.dojo.tech/images/dojo-icons/ArrowBendUpLeft.svg) **Introduction** Return to the introduction page.](../introduction) [![](https://docs.dojo.tech/images/dojo-icons/Compass.svg) **Step-by-step guide** See the new terminals endpoints in practice.](step-by-step-guide) [![](https://docs.dojo.tech/images/dojo-icons/Headset.svg) **Support** Our support team is always happy to help with any questions you have.](https://support.dojo.tech/hc/en-gb) --- ## Create session >Create a new session on an available terminal. You can create a terminal session on any `Available` terminal. If, as part of your integration, you need to develop logic for finding out which terminals are available, use one of the GET [/terminals](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/get-terminal) endpoint. A [payment intent](https://docs.dojo.tech/payments/manage-payments/payment-intent) is automatically initiated on the terminal when you create a terminal session. ## Path > See OpenAPI spec for POST endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Create ## Session types There are three Terminal session types: * a `sale`, which is associated with a [payment intent](https://docs.dojo.tech/payments/manage-payments/payment-intent) and therefore requires a `paymentIntentId`. * a `matchedRefund`, which also requires a `paymentIntentId`. * an `unlinkedRefund`, which is a refund that has not yet been associated with a payment intent. For an `unlinkedRefund`, you will have to specify the `amount` (the `value` in minor units, and the `currencyCode`) when you post the request to create a terminal session. For a `matchedRefund`, you will have to specify the `paymentIntentId` as well. ### Matched refund A matched refund requires the card and the customer to be physically present on the premises. The request references a payment intent and uses a terminal to process a *negative transaction*, which is the movement of funds to the card from the merchant account. ### Unlinked refund An unlinked refund also requires the card and the customer to be physically present, but it contains **no reference to a payment intent**. An unlinked refund theoretically allows for any amount of money to be refunded to anyone. This is ideal for EPOS systems which do not store previous transaction data, where refunds are processed without reference to the original sale's `paymentIntentId`. > **Tip:** Use `Authorization: Basic ` on these requests. `Basic ` is a literal prefix in Dojo's auth scheme, so do **not** base64-encode `api_key:`. > Sandbox and production both use `https://api.dojo.tech`; the API key determines the environment. In sandbox, use your assigned `software-house-id` and `reseller-id` if you have them; otherwise use the literal values `softwareHouse1` and `reseller1`. Production integrations always use values assigned by Dojo. ## Sample requests ```bash # The sandbox API key passed in 'authorization' is public. # Don't submit any personally identifiable information in any requests made with this key. # Sign in to developer.dojo.tech to create your own private sandbox key and use that instead # for secure testing. curl --location --request POST 'https://api.dojo.tech/terminal-sessions' \ --header 'version: 2024-02-05' \ --header 'software-house-id: softwareHouse1' \ --header 'reseller-id: reseller1' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'Authorization: Basic sk_sandbox_1WYDtq7yAdqhmQ7KEUAvPlCCRBYc9HTY9KOPJKZtfWkzsSISj1L8c4GG5l4pBB5Bj85hkJgTL9vmOmki5QnQfQ' \ --data-raw '{ "terminalId": "tm_65a7e8c0bee9b6390862337c", "details": { "sale": { "paymentIntentId": "pi_sandbox_81Q7HAZSGkWLKFx_DFEe9Q" }, "sessionType": "Sale" } }' ``` [![](https://docs.dojo.tech/images/dojo-icons/TerminalWindow.svg) **API reference** Learn about the API and integrate with API endpoints.](/api/#tag/Terminals) [![](https://docs.dojo.tech/images/dojo-icons/ArrowBendUpLeft.svg) **Introduction** Return to the introduction page.](../introduction) [![](https://docs.dojo.tech/images/dojo-icons/Compass.svg) **Step-by-step guide** See the new terminals endpoints in practice.](step-by-step-guide) [![](https://docs.dojo.tech/images/dojo-icons/Headset.svg) **Support** Our support team is always happy to help with any questions you have.](https://support.dojo.tech/hc/en-gb) --- ## Get terminal >Get information about terminals and terminal sessions. ## All terminals > See OpenAPI spec for GET endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/Terminals_ListTerminals **Summary:** Retrieve all terminals, or all terminals with a specified `statuses` value. Setting `statuses` to `Available`, for example, will return a list of all terminals that are currently ready to use. Because `statuses` is an optional request field, leaving it blank will return a list of _all_ terminals of all statuses. ```bash # The sandbox API key passed in 'authorization' is public. # Don't submit any personally identifiable information in any requests made with this key. # Sign in to developer.dojo.tech to create your own private sandbox key and use that instead # for secure testing. curl --location --request GET 'https://api.dojo.tech/terminals?statuses=Available' \ --header 'version: 2024-02-05' \ --header 'software-house-id: softwareHouse1' \ --header 'reseller-id: reseller1' \ --header 'Accept: application/json' \ --header 'Authorization: Basic sk_sandbox_1WYDtq7yAdqhmQ7KEUAvPlCCRBYc9HTY9KOPJKZtfWkzsSISj1L8c4GG5l4pBB5Bj85hkJgTL9vmOmki5QnQfQ' ``` ## A specified terminal > See OpenAPI spec for GET endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/Terminals_GetTerminal **Summary:** Retrieve a specified terminal using its `terminalId`. ```bash # The sandbox API key passed in 'authorization' is public. # Don't submit any personally identifiable information in any requests made with this key. # Sign in to developer.dojo.tech to create your own private sandbox key and use that instead # for secure testing. curl --location --request GET 'https://api.dojo.tech/terminals/tm_sandbox_65aaa2a8637e6374ac2447c9' \ --header 'version: 2024-02-05' \ --header 'software-house-id: softwareHouse1' \ --header 'reseller-id: reseller1' \ --header 'Accept: application/json' \ --header 'Authorization: Basic sk_sandbox_1WYDtq7yAdqhmQ7KEUAvPlCCRBYc9HTY9KOPJKZtfWkzsSISj1L8c4GG5l4pBB5Bj85hkJgTL9vmOmki5QnQfQ' ``` ## A terminal session > See OpenAPI spec for GET endpoint details: https://docs.dojo.tech/api/#tag/Terminal-sessions/operation/TerminalSession_Get **Summary:** Retrieve a specified terminal session by searching for the `terminalSessionId` while using the `/terminal-sessions` endpoint. ```bash # The sandbox API key passed in 'authorization' is public. # Don't submit any personally identifiable information in any requests made with this key. # Sign in to developer.dojo.tech to create your own private sandbox key and use that instead # for secure testing. curl --location --request GET 'https://api.dojo.tech/terminal-sessions/ts_sandbox_65aa64bf637e6374ac244115' \ --header 'version: 2024-02-05' \ --header 'software-house-id: softwareHouse1' \ --header 'reseller-id: reseller1' \ --header 'Accept: application/json' \ --header 'Authorization: Basic sk_sandbox_1WYDtq7yAdqhmQ7KEUAvPlCCRBYc9HTY9KOPJKZtfWkzsSISj1L8c4GG5l4pBB5Bj85hkJgTL9vmOmki5QnQfQ' ``` [![](https://docs.dojo.tech/images/dojo-icons/TerminalWindow.svg) **API reference** Learn about the API and integrate with API endpoints.](/api/#tag/Terminals) [![](https://docs.dojo.tech/images/dojo-icons/ArrowBendUpLeft.svg) **Introduction** Return to the introduction page.](../introduction) [![](https://docs.dojo.tech/images/dojo-icons/Compass.svg) **Step-by-step guide** See the new terminals endpoints in practice.](step-by-step-guide) [![](https://docs.dojo.tech/images/dojo-icons/Headset.svg) **Support** Our support team is always happy to help with any questions you have.](https://support.dojo.tech/hc/en-gb) --- ## Pre-authorization Pre-authorization is like opening a digital tab. It allows you to provide a seamless experience without charging the customer multiple times. * **The Start**: You begin by authorizing a base amount (e.g., £10) to verify the card. * **The Build**: As the customer continues to order, you can "top up" or increment that authorization to match their real-time spending. * **The Finish**: When they’re ready to leave, you capture the final total to settle the funds in a single transaction. **Use Case**: This is the gold standard for hospitality environments like bars, restaurants, or hotels where the final bill isn't known upfront. It's also ideal for corporate events where the cardholder does not have to stay until the end to settle the tab. ## The Start ### Step 1: Create a payment intent First, ensure you have already created a [payment intent](https://docs.dojo.tech/payments/manage-payments/payment-intent). This intent stores important information like payment details, amount, and currency. Please see the documentation for more information on configuration options. The example below is the basic request: ```json { "amount": { "value": 1000, "currencyCode": "GBP" }, "reference": "Order 234", "description": "Demo payment intent", "captureMode": "Manual", "autoExpireAction": "Release", "autoExpireIn": "00.10:00:00" } ``` > **Tip:** For pre-authorization, the `captureMode` must be set to `Manual`. ### Step 2: Check which terminals are available > See OpenAPI spec for GET endpoint details: https://docs.dojo.tech/api#tag/Terminals/operation/Terminals_ListTerminals **Main page**: [Retrieve all terminals](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/get-terminal/#all-terminals) You can check which terminals are available by requesting a list of all terminals or by requesting a filtered list of `Available` terminals. ### Step 3: Create a terminal session > See OpenAPI spec for POST endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Create **Main page**: [Create session](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/create-session) In your request, you will need to specify the terminal [`sessionType`](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/create-session/#session-types) and the ID of the payment intent you have already created. ```json { "terminalId": "tm_sandbox_65c5fe8a104a1222b2d8b968", "details": { "sale": { "paymentIntentId": "pi_sandbox_81Q7HAZSGkWLKFx_DFEe9Q" }, "sessionType": "Sale" } } ``` > **Tip:** To allow for bills greater than £100, payment via Chip and PIN or device verification (e.g., a mobile wallet) is required. A session will end once the initial authorization is complete. ## The Build ### Increment the authorized amount > See OpenAPI spec for POST endpoint details: https://docs.dojo.tech/api#tag/Payment-intents/operation/PaymentIntents_SetCustomAmount To update the pre-authorized amount, use the [Change a payment intent amount endpoint.](https://docs.dojo.tech/api#tag/Payment-intents/operation/PaymentIntents_SetCustomAmount) This can be used multiple times for the same pre-authorization before you capture the final amount. ```json { "amount": { "value": 5500, "currencyCode": "GBP" } } ``` ## The Finish ### Capturing a final amount > See OpenAPI spec for POST endpoint details: https://docs.dojo.tech/api#tag/Captures To complete the transaction, specify the final amount you wish to capture. You can capture the full authorized amount or a lower amount. Any remaining balance will be returned to the cardholder immediately after capture. ```json { "amount": 2000 } ``` ### Response example If your capture request is successful, the response will return information about the capture. ```json { "message": "collect payment for the order 3443", "captureId": "cp_itIiJMEAvES3ynYF_Yhs2g" } ``` --- [![](https://docs.dojo.tech/images/dojo-icons/TerminalWindow.svg) **API reference** Learn about the API and integrate with API endpoints.](/api/#tag/Terminals) [![](https://docs.dojo.tech/images/dojo-icons/ArrowBendUpLeft.svg) **Introduction** Return to the introduction page.](../introduction) [![](https://docs.dojo.tech/images/dojo-icons/Headset.svg) **Support** Our support team is always happy to help with any questions you have.](https://support.dojo.tech/hc/en-gb) --- ## Signature verification >Respond to signature verification requests and accept or decline payment. Although only a small number of transactions will require signature verification, to be able to correctly support signature verification an endpoint including logic to handle these requests has been included as part of the Dojo API. > **Tip:** You'll only need to complete this step when the terminal session status changes to `SignatureVerificationRequired`. > > > ## Step 1. Create a payment intent > > First, make sure you've already created a [payment intent](https://docs.dojo.tech/payments/manage-payments/payment-intent). Visit the payment intents > documentation for more information about configuration options. This is where important information like payment details, amount, and currency are stored. > > ## Step 2: Check which terminals are available > > GET [/terminals](https://docs.dojo.tech/api#tag/Terminals/operation/Terminals_ListTerminals) > > **Main page**: [Retrieve all terminals](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/get-terminal/#all-terminals) > > You can check which terminals are available by requesting a list of all terminals, or by requesting a filtered list of `Available` terminals. > > ## Step 3: Create a terminal session > > POST [/terminal-sessions](https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Create) > > **Main page**: [Create session](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/create-session) > > Over the course of a single business day, a terminal > might be associated with hundreds of sessions. > > In your request, determine the terminal [`sessionType`](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/create-session/#session-types). Make sure you've already created your > payment intent before creating a terminal session. You will have to specify the `paymentIntentId` in the request for `Sale` and `MatchedRefund` sessions. > Dojo will initiate the payment intent automatically once you've linked them. > > A session will end once the payment is complete, or once the refund has been fully processed. > > ## Step 4: Respond to a signature verification request > > PUT [/terminal-sessions/{terminalSessionId}/signature](https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Signature) > > **Main page**: [Signature verification](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/signature-verification) > > **Tip:** The operator has 80 seconds to respond after the terminal session enters `SignatureVerificationRequired`. > > If no signature decision is submitted within that window, Dojo finalizes the signature step as accepted and the payment continues through its normal successful flow. For example, an auto-capture sale proceeds to `Captured`, while a manual-capture sale proceeds to `Authorized` until you capture it later. Respond to a signature verification request with **true** (to accept it) or **false** (to decline it). If you accept signature verification and send a request with the `accepted` field set to `true`, you will receive a detailed response object including both a `customerReceipt` and a `merchantReceipt`. ## Step 5: Retrieve the original payment intent Now that the customer has completed the payment on the terminal and the terminal has reached a final state, retrieve the original payment intent to ensure that the payment is complete. > See OpenAPI spec for GET endpoint details: https://docs.dojo.tech/api/#tag/Payment-intents/operation/PaymentIntents_Get --- [![](https://docs.dojo.tech/images/dojo-icons/TerminalWindow.svg) **API reference** Learn about the API and integrate with API endpoints.](/api/#tag/Terminals) [![](https://docs.dojo.tech/images/dojo-icons/ArrowBendUpLeft.svg) **Introduction** Return to the introduction page.](../introduction) [![](https://docs.dojo.tech/images/dojo-icons/Headset.svg) **Support** Our support team is always happy to help with any questions you have.](https://support.dojo.tech/hc/en-gb) ## Sample requests ```bash # The sandbox API key passed in 'authorization' is public. # Don't submit any personally identifiable information in any requests made with this key. # Sign in to developer.dojo.tech to create your own private sandbox key and use that instead # for secure testing. curl --location --request PUT 'https://api.dojo.tech/terminal-sessions/ts_sandbox_65aa64bf637e6374ac244115/signature' \ --header 'version: 2024-02-05' \ --header 'software-house-id: softwareHouse1' \ --header 'reseller-id: reseller1' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'Authorization: Basic sk_sandbox_1WYDtq7yAdqhmQ7KEUAvPlCCRBYc9HTY9KOPJKZtfWkzsSISj1L8c4GG5l4pBB5Bj85hkJgTL9vmOmki5QnQfQ' \ --data-raw '{ "accepted": true }' ``` ## Test your integration You can use your Dojo payment device to test your signature verification integration: 1. Start a new transaction on your EPOS. 2. Insert your [test card](https://docs.dojo.tech/development-resources/testing) into the payment device, wrong way round (not chip first). 3. Wait until the terminal tells you to remove card, then swipe it, too. 4. If successful, the signature verification process will commence. [![](https://docs.dojo.tech/images/dojo-icons/TerminalWindow.svg) **API reference** Learn about the API and integrate with API endpoints.](//api/#tag/Terminals) [![](https://docs.dojo.tech/images/dojo-icons/ArrowBendUpLeft.svg) **Introduction** Return to the introduction page.](../introduction) [![](https://docs.dojo.tech/images/dojo-icons/Compass.svg) **Step-by-step guide** See the new terminals endpoints in practice.](step-by-step-guide) [![](https://docs.dojo.tech/images/dojo-icons/Headset.svg) **Support** Our support team is always happy to help with any questions you have.](https://support.dojo.tech/hc/en-gb) --- ## Step-by-step guide >A quick example of a Terminals flow. For ease of use, the request samples here will be cURL. The response samples are JSON. The language you use for your own integration may differ, and throughout Dojo's documentation there are code samples in [PHP, cURL, Python](https://github.com/dojo-engineering/dojo-samples), and other common languages and formats. Depending upon the `sessionType` you choose, perform the following steps for a successful terminal session: ![Payment Intent Flow](https://docs.dojo.tech/images/session-type-sale.jpg) **Flow diagram: Terminal Session — Sale** ```mermaid sequenceDiagram actor U as User participant T as Dojo Terminal participant E as EPOS system participant B as dojo backend U->>E: Places an order E->>B: POST /payment-intents (Sends payment information) B-->>E: Payment intent object E->>B: GET /terminals?statuses=Available (Requests for available terminals) B-->>E: List of available terminals E->>B: POST /terminal-sessions (details: sale) B-->>E: Terminal session object with amount to capture E->>T: Displays amount to capture U->>T: Presents card T->>B: Processes payment loop Poll for notifications E->>B: GET /terminal-sessions/{terminalSessionId} B-->>E: Terminal session object with status end T->>B: Sends response with the payment result B-->>E: Sends response with the payment result T-->>U: Print device receipt E-->>U: Print order receipt E->>B: GET /payment-intents/{paymentIntentId} B-->>E: Payment intent object ``` ![Payment Intent Flow](https://docs.dojo.tech/images/session-type-matched-refund.jpg) **Flow diagram: Session Type Matched Refund** ```mermaid sequenceDiagram actor U as User participant T as Dojo Terminal participant E as EPOS system participant B as dojo backend U->>E: Requests for a refund against a previous transaction E->>B: POST /payment-intents (Sends payment information) B-->>E: Payment intent object E->>B: GET /terminals?statuses=Available (Requests for available terminals) B-->>E: List of available terminals E->>B: POST /terminal-sessions (details: matchedRefund) B-->>E: Terminal session object with amount to capture E->>T: Displays amount to capture U->>T: Presents card T->>B: Processes payment loop Poll for notifications E->>B: GET /terminal-sessions/{terminalSessionId} B-->>E: Terminal session object with status end T->>B: Sends response with the refund transaction result B-->>E: Sends response with the refund result T-->>U: Print device receipt E-->>U: Print order receipt E->>B: GET /payment-intents/{paymentIntentId} B-->>E: Payment intent object ``` ![Payment Intent Flow](https://docs.dojo.tech/images/session-type-unlinked-refund.jpg) **Flow diagram: Session Type Unlinked Refund** ```mermaid sequenceDiagram actor U as User participant T as Dojo Terminal participant E as EPOS system participant B as dojo backend U->>E: Requests refund for a custom amount E->>B: GET /terminals?statuses=Available (Requests for available terminals) B-->>E: List of available terminals E->>B: POST /terminal-sessions (details: unlinkedRefund) B-->>E: Terminal session object with amount to capture E->>T: Displays amount to capture U->>T: Presents card T->>B: Processes payment loop Poll for notifications E->>B: GET /terminal-sessions/{terminalSessionId} B-->>E: Terminal session object with status end T->>B: Sends response with the refund transaction result B-->>E: Sends response with the refund result T-->>U: Print device receipt E-->>U: Print order receipt E->>B: GET /payment-intents/{paymentIntentId} B-->>E: Payment intent object ``` > **Note:** An `unlinkedRefund` refund session type contains no reference to a payment intent and can allow any amount to be refunded directly from the EPOS systems. **LLM walkthrough: complete Pay at Counter happy path** Use this as the canonical PAC template when your POS or middleware owns the basket, takes a card-present payment on a Dojo terminal, and later needs to issue a refund. All Dojo REST calls in this walkthrough use: - `Authorization: Basic ` (literal `Basic ` prefix; do not base64-encode `api_key:`) - `version: 2026-02-27` - `Content-Type: application/json` - `https://api.dojo.tech` for both sandbox and production; the API key determines the environment - `software-house-id: softwareHouse1` and `reseller-id: reseller1` on terminal endpoints in sandbox; production integrations receive assigned values from Dojo **Minimal end-to-end example (bash + curl)** This sample auto-accepts a signature request for demonstration purposes. In production, replace that with the operator's actual decision. ```bash API_KEY="sk_sandbox_your_key" BASE_URL="https://api.dojo.tech" VERSION="2026-02-27" COMMON_HEADERS=(-H "Authorization: Basic $API_KEY" -H "version: $VERSION" -H "Content-Type: application/json" -H "Accept: application/json") TERMINAL_HEADERS=("${COMMON_HEADERS[@]}" -H "software-house-id: softwareHouse1" -H "reseller-id: reseller1") PAYMENT_INTENT_ID=$( curl -sS "$BASE_URL/payment-intents" "${COMMON_HEADERS[@]}" \ -d '{"amount":{"value":1000,"currencyCode":"GBP"},"reference":"Order-234","captureMode":"Auto"}' \ | jq -r '.id' ) TERMINAL_ID=$( curl -sS "$BASE_URL/terminals?statuses=Available" "${TERMINAL_HEADERS[@]}" \ | jq -r '.[0].id' ) SESSION_ID=$( curl -sS "$BASE_URL/terminal-sessions" "${TERMINAL_HEADERS[@]}" \ -d "{\"terminalId\":\"$TERMINAL_ID\",\"details\":{\"sessionType\":\"Sale\",\"sale\":{\"paymentIntentId\":\"$PAYMENT_INTENT_ID\"}}}" \ | jq -r '.id' ) while :; do SESSION=$(curl -sS "$BASE_URL/terminal-sessions/$SESSION_ID" "${TERMINAL_HEADERS[@]}") STATUS=$(jq -r '.status' <<<"$SESSION") case "$STATUS" in InitiateRequested|Initiated|CancelRequested) sleep 1 ;; SignatureVerificationRequired) curl -sS -X PUT "$BASE_URL/terminal-sessions/$SESSION_ID/signature" "${TERMINAL_HEADERS[@]}" \ -d '{"accepted":true}' >/dev/null sleep 1 ;; Authorized|Captured|SignatureVerificationAccepted|SignatureVerificationRejected|Canceled|Declined|Expired) break ;; *) echo "Unexpected terminal session status: $STATUS" >&2; break ;; esac done curl -sS "$BASE_URL/payment-intents/$PAYMENT_INTENT_ID" "${COMMON_HEADERS[@]}" | jq ``` **1. Create the payment intent** **POST** `/payment-intents` **Request body** ```json { "amount": { "value": 1000, "currencyCode": "GBP" }, "reference": "Order-234", "description": "Card terminal sale for table 12", "captureMode": "Auto" } ``` **Response body** ```json { "id": "pi_sandbox_G_FeegU8WESxtY_Nct8jqw", "captureMode": "Auto", "status": "Created", "paymentMethods": [ "Card" ], "amount": { "value": 1000, "currencyCode": "GBP" }, "totalAmount": { "value": 1000, "currencyCode": "GBP" }, "refundedAmount": 0, "createdAt": "2024-04-07T23:25:44.802258Z", "updatedAt": "2024-04-07T23:25:44.802258Z", "reference": "Order-234", "description": "Card terminal sale for table 12", "paymentLink": "https://pay.dojo.tech/checkout/pi_sandbox_G_FeegU8WESxtY_Nct8jqw", "paymentEvents": [], "terminalSessionHistory": [] } ``` **2. Create the terminal session** **POST** `/terminal-sessions` **Request body** ```json { "terminalId": "tm_sandbox_65c5fe8a104a1222b2d8b968", "details": { "sale": { "paymentIntentId": "pi_sandbox_G_FeegU8WESxtY_Nct8jqw" }, "sessionType": "Sale" } } ``` **Response body** ```json { "createdAt": "2024-02-15T00:48:10.756Z", "details": { "sale": { "paymentIntentId": "pi_sandbox_G_FeegU8WESxtY_Nct8jqw" }, "sessionType": "Sale" }, "expireAt": "2024-02-15T00:48:20.756Z", "id": "ts_sandbox_65cd5f4a57ba13ad7424acd0", "notificationEvents": [], "status": "InitiateRequested", "statusEvents": [ { "createdAt": "2024-02-15T00:48:10.756Z", "debugMessage": "", "status": "InitiateRequested" } ], "terminalId": "tm_sandbox_65c5fe8a104a1222b2d8b968", "updatedAt": "2024-02-15T00:48:10.756Z" } ``` **3. Poll the terminal session until it reaches a terminal state** **GET** `/terminal-sessions/ts_sandbox_65cd5f4a57ba13ad7424acd0` Repeat this lookup while `status` is still in flight (`InitiateRequested`, `Initiated`, or `CancelRequested`). For a sale flow, the next meaningful outcome will be one of: - `Captured` for an auto-capture sale - `Authorized` for a manual-capture sale that still needs a later capture request - `SignatureVerificationRequired` when you must call **PUT** `/terminal-sessions/{terminalSessionId}/signature` - `Canceled`, `Declined`, or `Expired` when the session cannot complete successfully `Refunded` and `Reversed` are payment-intent states, not terminal-session states. **Response body** ```json { "createdAt": "2024-02-15T00:48:10.756Z", "details": { "sale": { "paymentIntentId": "pi_sandbox_G_FeegU8WESxtY_Nct8jqw" }, "sessionType": "Sale" }, "expireAt": "2024-02-15T00:49:40.756Z", "id": "ts_sandbox_65cd5f4a57ba13ad7424acd0", "notificationEvents": [ { "createdAt": "2024-02-15T00:48:11.980Z", "notificationType": "PresentCard" }, { "createdAt": "2024-02-15T00:48:16.066Z", "notificationType": "PleaseWait" }, { "createdAt": "2024-02-15T00:48:16.176Z", "notificationType": "EnterPin" }, { "createdAt": "2024-02-15T00:48:18.354Z", "notificationType": "RemoveCard" } ], "status": "Captured", "statusEvents": [ { "createdAt": "2024-02-15T00:48:10.756Z", "debugMessage": "", "status": "InitiateRequested" }, { "createdAt": "2024-02-15T00:48:10.972Z", "debugMessage": "", "status": "Initiated" }, { "createdAt": "2024-02-15T00:48:19.448Z", "debugMessage": "", "status": "Captured" } ], "terminalId": "tm_sandbox_65c5fe8a104a1222b2d8b968", "updatedAt": "2024-02-15T00:48:19.448Z" } ``` **4. Handle the successful payment notification and fetch the final payment intent** Use the webhook as the asynchronous confirmation, then read the payment intent for the full card-present result. **Webhook payload** ```json { "id": "evt_pac_01HPC2KQY7P5LZ5Q2B4W0A1B2C", "event": "payment_intent.status_updated", "accountId": "acc_test", "createdAt": "2024-02-15T00:48:19.448Z", "data": { "paymentIntentId": "pi_sandbox_G_FeegU8WESxtY_Nct8jqw", "paymentStatus": "Captured", "captureMode": "Auto" } } ``` **GET** `/payment-intents/pi_sandbox_G_FeegU8WESxtY_Nct8jqw` **Response body** ```json { "id": "pi_sandbox_G_FeegU8WESxtY_Nct8jqw", "captureMode": "Auto", "status": "Captured", "paymentMethods": [ "Card" ], "amount": { "value": 1000, "currencyCode": "GBP" }, "totalAmount": { "value": 1000, "currencyCode": "GBP" }, "refundedAmount": 0, "reference": "Order-234", "description": "Card terminal sale for table 12", "paymentDetails": { "transactionId": "19e4535e-ef6e-48e2-90be-d2b313c1cefd", "transactionDateTime": "2024-07-25T08:49:20.973410318Z", "message": "DEPOSITED", "authCode": "123456", "card": { "cardNumber": "44565300****1096", "cardName": "test", "expiryDate": "2024-12-31", "cardType": "VISA", "entryMode": "Contactless", "verificationMethod": "Pin" } }, "paymentEvents": [ { "transactionId": "19e4535e-ef6e-48e2-90be-d2b313c1cefd", "transactionDateTime": "2024-07-25T08:49:20.973410318Z", "eventType": "Captured", "authCode": "123456", "cardNumber": "44565300****1096", "expiryDate": "2024-12-31", "cardType": "VISA", "cardholderName": "test", "paymentMethodId": "" } ] } ``` **5. Refund the payment later with the Payment Intents API** Use the stored `paymentIntentId` for server-side refunds when the customer is no longer present. **POST** `/payment-intents/pi_sandbox_G_FeegU8WESxtY_Nct8jqw/refunds` **Request body** ```json { "amount": 1000, "refundReason": "Demo refund", "notes": "Customer returned the order in store" } ``` **Response body** ```json { "message": "refund for the order 3443", "refundId": "rfnd_g8mCx87TykeQ6BOXqxZ9NQ" } ``` For a card-present refund on the terminal instead of a server-side API refund, create a `matchedRefund` terminal session and reuse the original `paymentIntentId`. ## Step 1. Create a payment intent First, make sure you've already created a [payment intent](https://docs.dojo.tech/payments/manage-payments/payment-intent). Visit the payment intents documentation for more information about configuration options. This is where important information like payment details, amount, and currency are stored. ## Step 2: Check which terminals are available > See OpenAPI spec for GET endpoint details: https://docs.dojo.tech/api#tag/Terminals/operation/Terminals_ListTerminals **Main page**: [Retrieve all terminals](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/get-terminal/#all-terminals) You can check which terminals are available by requesting a list of all terminals, or by requesting a filtered list of `Available` terminals. ## Step 3: Create a terminal session > See OpenAPI spec for POST endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Create **Main page**: [Create session](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/create-session) Over the course of a single business day, a terminal might be associated with hundreds of sessions. In your request, determine the terminal [`sessionType`](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/create-session/#session-types). Make sure you've already created your payment intent before creating a terminal session. You will have to specify the `paymentIntentId` in the request for `Sale` and `MatchedRefund` sessions. Dojo will initiate the payment intent automatically once you've linked them. A session will end once the payment is complete, or once the refund has been fully processed. ## (Optional) Step 4: Cancel a terminal session > See OpenAPI spec for PUT endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Cancel ```curl curl --location --request PUT 'https://api.dojo.tech/master/terminal-sessions/ts_sandbox_65af01524a36e6a14356dbc0/cancel ``` **Main page**: [Cancel session](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/cancel-session) You can cancel a terminal session as long as no card has been presented, inserted or swiped. If payment has been attempted or completed on a terminal session, it will no longer be possible to cancel it. ## (Optional) Step 5: Respond to a signature verification request > See OpenAPI spec for PUT endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Signature **Main page**: [Signature verification](https://docs.dojo.tech/payments/accept-payments/in-person-payments/pay-at-counter/terminals/signature-verification) > **Tip:** You'll only need to complete this step if the terminal session status changes to `SignatureVerificationRequired`. Respond to a signature verification request with **true** (to accept it) or **false** (to decline it). If you accept signature verification and send a request with the `accepted` field set to `true`, you will receive a detailed response object including both a `customerReceipt` and a `merchantReceipt`. ## Step 6: Retrieve the original payment intent Now that the customer has completed the payment on the terminal and the terminal has reached a final state, retrieve the original payment intent to ensure that the payment is complete. > See OpenAPI spec for GET endpoint details: https://docs.dojo.tech/api/#tag/Payment-intents/operation/PaymentIntents_Get --- [![](https://docs.dojo.tech/images/dojo-icons/TerminalWindow.svg) **API reference** Learn about the API and integrate with API endpoints.](/api/#tag/Terminals) [![](https://docs.dojo.tech/images/dojo-icons/ArrowBendUpLeft.svg) **Introduction** Return to the introduction page.](../introduction) [![](https://docs.dojo.tech/images/dojo-icons/Headset.svg) **Support** Our support team is always happy to help with any questions you have.](https://support.dojo.tech/hc/en-gb) --- ## Terminals >Integrate with the Dojo API to use Terminals operations ## Introduction The [Terminals endpoints](https://docs.dojo.tech/api/#tag/Terminals) included in the [Dojo API](https://docs.dojo.tech/api) integrate ["Pay at Counter"](https://support.dojo.tech/hc/en-gb/articles/360020950520-Using-Pay-at-Counter) functions. Similar to payment intents, you can capture payments on a terminal in one of the following capture modes: - `Auto`: by default, payments on a terminal are captured automatically without delay. This is ideal for immediate transactions. - `Manual`: use this mode when you need to reserve an amount on the customer's payment method to capture later. This is ideal for hotels to authorize a terminal before a guest checks in. For a successful transaction on the terminal, note how the Payment Intents endpoints and Terminals endpoints progress through some combination of statuses listed in the following diagrams: These diagrams combine **payment-intent** states and **terminal-session** states. The current terminal-session status enum is `InitiateRequested`, `Initiated`, `Authorized`, `Captured`, `CancelRequested`, `Canceled`, `SignatureVerificationAccepted`, `SignatureVerificationRejected`, `SignatureVerificationRequired`, `Expired`, and `Declined`. Payment-intent states such as `Reversed` and `Refunded` appear here only to show what can happen **after** a successful terminal collection flow. They are not terminal-session statuses. ![Payment Intent Flow](https://docs.dojo.tech/images/terminals-auto.jpg) **Flow diagram: Terminals Auto** ```mermaid stateDiagram-v2 state "PaymentIntent: Created" as created state "PaymentIntent: Canceled" as pi_canceled state "TerminalSession: InitiateRequested" as init_req state "TerminalSession: CancelRequested" as cancel_req state "TerminalSession: Initiated" as initiated state "TerminalSession: Canceled" as ts_canceled state "TerminalSession: Expired" as expired state "TerminalSession: Declined" as declined state "TerminalSession: SignatureVerificationRequired" as sig_req state "TerminalSession: SignatureVerificationRejected" as sig_rej state "TerminalSession: SignatureVerificationAccepted" as sig_acc state "TerminalSession / PaymentIntent: Captured" as captured state "PaymentIntent: Reversed" as reversed state "PaymentIntent: Refunded" as refunded [*] --> created: POST /payment-intents created --> pi_canceled: DELETE /payment-intents/{paymentIntentId} created --> init_req: POST /terminal-sessions init_req --> cancel_req: PUT /terminal-sessions/{terminalSessionId}/cancel cancel_req --> ts_canceled init_req --> initiated initiated --> expired initiated --> declined initiated --> ts_canceled initiated --> sig_req: Customer enters card details sig_req --> sig_rej sig_req --> sig_acc: PUT /terminal-sessions/{terminalSessionId}/signature sig_acc --> captured: (automatically captured) captured --> reversed: POST /payment-intents/{paymentIntentId}/reversal captured --> refunded: POST /payment-intents/{paymentIntentId}/refunds ``` ![Payment Intent Flow](https://docs.dojo.tech/images/terminals-manual.jpg) **Flow diagram: Terminals Manual** ```mermaid stateDiagram-v2 state "PaymentIntent: Created" as created state "PaymentIntent: Canceled" as pi_canceled state "TerminalSession: InitiateRequested" as init_req state "TerminalSession: CancelRequested" as cancel_req state "TerminalSession: Initiated" as initiated state "TerminalSession: Canceled" as ts_canceled state "TerminalSession: Expired" as expired state "TerminalSession: Declined" as declined state "TerminalSession: SignatureVerificationRequired" as sig_req state "TerminalSession: SignatureVerificationRejected" as sig_rej state "TerminalSession: SignatureVerificationAccepted" as sig_acc state "TerminalSession / PaymentIntent: Authorized" as authorized state "TerminalSession / PaymentIntent: Captured" as captured state "PaymentIntent: Reversed" as reversed state "PaymentIntent: Refunded" as refunded [*] --> created: POST /payment-intents created --> pi_canceled: DELETE /payment-intents/{paymentIntentId} created --> init_req: POST /terminal-sessions init_req --> cancel_req: PUT /terminal-sessions/{terminalSessionId}/cancel cancel_req --> ts_canceled init_req --> initiated initiated --> expired initiated --> declined initiated --> ts_canceled initiated --> sig_req: Customer enters card details sig_req --> sig_rej sig_req --> sig_acc: PUT /terminal-sessions/{terminalSessionId}/signature sig_acc --> authorized authorized --> reversed: POST /payment-intents/{paymentIntentId}/reversal authorized --> captured: POST /payment-intents/{paymentIntentId}/captures captured --> refunded: POST /payment-intents/{paymentIntentId}/refunds ``` ### Create a terminal session > See OpenAPI spec for POST endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Create **Summary**: [Create a session on a terminal](https://docs.dojo.tech/plugins/woocommerce/terminals/create-session) to initiate an in-person payment. This endpoint allows you to establish a connection with the terminal and prepare it for processing payments. Creating a terminal session also automatically initiates whichever [payment intent](https://docs.dojo.tech/manage-payments/payment-intent) ID you specified in the request. ### Retrieve all terminals > See OpenAPI spec for GET endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/Terminals_ListTerminals **Summary:** [Retrieve all terminals](https://docs.dojo.tech/plugins/woocommerce/terminals/get-terminal#all-terminals) associated with your account. This endpoint provides information about each terminal in an array. ### Retrieve a terminal > See OpenAPI spec for GET endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/Terminals_GetTerminal **Summary:** [Retrieve a terminal](https://docs.dojo.tech/plugins/woocommerce/terminals/get-terminal#a-specified-terminal) by providing its `terminalId`. ### Retrieve a terminal session > See OpenAPI spec for GET endpoint details: https://docs.dojo.tech/api#tag/Terminal-sessions/operation/TerminalSession_Get **Summary:** [Retrieve a terminal session](https://docs.dojo.tech/plugins/woocommerce/terminals/get-terminal#a-terminal-session) by providing its `terminalSessionId`. ### Signature verification > See OpenAPI spec for PUT endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Signature **Summary:** [Respond to a signature verification request](https://docs.dojo.tech/plugins/woocommerce/terminals/signature-verification). Useful for authorizing and finalizing the small number of modern transactions that still require a customer signature to confirm payment. ### Cancel a terminal session > See OpenAPI spec for PUT endpoint details: https://docs.dojo.tech/api/#tag/Terminals/operation/TerminalSession_Cancel **Summary**: [Cancel a terminal session](https://docs.dojo.tech/plugins/woocommerce/terminals/cancel-session), as long as no payment has been attempted or completed on it. > **Tip:** `tid` and `terminalId` are separate concepts. A `tid` is a unique industry-wide eight-digit ID that is assigned by the terminal manufacturer to troubleshoot hardware issues. A `terminalId` is a terminal identifier used by Dojo during the payment process. ## Expired state During a terminal session, the object may conclude in a status of `Expired`. This can occur in the event of complete disconnection between API and terminal during a transaction, and results in the **Dojo API** being unable to resolve the session, leading to a timed-out state. In the event of an `Expired` result, the POS should provide the user with clarification of the error, a prompt to check the result on the payment device screen or receipt, and with the ability to manually conclude the payment on the POS by either manually recording the payment as successful, or by re-initiating the terminal-session if the payment has failed. [![](https://docs.dojo.tech/images/dojo-icons/TerminalWindow.svg) **API reference** Learn about the API and integrate with API endpoints.](/api/#tag/Terminals) [![](https://docs.dojo.tech/images/dojo-icons/ArrowBendUpLeft.svg) **Introduction** Return to the introduction page.](introduction) [![](https://docs.dojo.tech/images/dojo-icons/Compass.svg) **Step-by-step guide** See the new terminals endpoints in practice.](./terminals/step-by-step-guide) [![](https://docs.dojo.tech/images/dojo-icons/Headset.svg) **Support** Our support team is always happy to help with any questions you have.](https://support.dojo.tech/hc/en-gb)