Captures a card present payment on a physical terminal in a single step, combining authorization and capture. Use this endpoint to charge a guest's card for a completed ticket at the point of sale. The terminal prompts for the card, processes the EMV transaction, and returns the approval or decline outcome.
Request Headers
| Header | Required | Description |
|---|---|---|
| Authorization | Yes | Bearer YOUR_ACCESS_TOKEN |
| Content-Type | Yes | application/json |
Request Parameters
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| location_id | string | Yes | Identifier of the location processing the sale |
Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
| ticket | object | Yes | See ticket object below. |
| ticket.id | string | Yes | The unique identifier of the ticket. |
| ticket.void | boolean | No | Indicates whether the ticket has been voided. |
| payment | object | Yes | Payment instruction for the terminal. |
| payment.id | string | Yes | Unique identifier for the payment. |
| payment.amount | integer | Yes | Payment amount, in cents. |
| payment.cashback | integer | No | EBT cashback amount, in cents. |
| payment.inv_number | string | No | Invoice number to associate with the payment. |
| user_id_external | integer | No | External user ID associated with the card being tokenized (if applicable). |
| ignoreAvsResult | boolean | No | Continue processing even if AVS validation fails. |
| webhook_id | string | Yes | Unique identifier for the webhook. |
| terminal_id | string | Yes | Unique identifier for the POS terminal. |
Object Fields
The ticket object may be sent as a compact reference (just id) or as a fully embedded structure. When embedded, it supports the fields below. All embedded fields are optional, and monetary values are expressed in minor units (cents).
ticket object
ticket object| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | The unique identifier of the ticket. |
| name | string | No | The name of the ticket. |
| open | boolean | No | Indicates whether the ticket is still open. |
| opened_at | timestamp | No | The timestamp when the ticket was opened. |
| closed_at | timestamp | No | The timestamp when the ticket was closed. |
| ticket_number | integer | No | The ticket number. |
| totals | object | No | Ticket totals information. See ticket.totals object below. |
| void | boolean | No | Indicates whether the ticket has been voided. |
| _embedded | object | No | Embedded ticket resources. See ticket._embedded object below. |
ticket.totals object
ticket.totals objectAll values are integers in minor units (cents).
| Field | Type | Description |
|---|---|---|
| discounts | integer | Total ticket-level discounts, in cents. |
| due | integer | Remaining unpaid amount (total - paid), in cents. |
| exclusive_tax | integer | Taxes not included in item or service charge prices, in cents. |
| inclusive_tax | integer | Taxes included in item or service charge prices, in cents. |
| items | integer | Total cost of all items, in cents. |
| other_charges | integer | Total cost of all other charges, in cents. |
| paid | integer | Total amount paid, in cents. |
| service_charges | integer | Total service charges, in cents. |
| sub_total | integer | Subtotal before tax (items + other_charges - discounts), in cents. |
| tax | integer | Total tax, in cents. |
| tips | integer | Total tips, in cents. |
| total | integer | Final amount due (subtotal + service_charges + exclusive_tax), in cents. |
ticket._embedded object
ticket._embedded objectContainer for nested ticket entities. May include ebt_type, discounts[], items[], and payments[]. Item-level entities (employee, order_type, revenue_center, table, and modifiers[]) are nested within each items[] entry.
ebt_type
ebt_type| Field | Type | Description |
|---|---|---|
| ebt_type | string | EBT type. Possible values: FOOD_STAMP, CASH_BENEFIT, VOUCHER, INQUIRY_CASH, INQUIRY_FOOD. |
discounts[] (array)
discounts[] (array)| Field | Type | Description |
|---|---|---|
| id | string | Unique identifier of the discount. |
| comment | string | Discount comment. |
| name | string | Discount name as it appears on the ticket. |
| value | integer | Discount value, in cents. |
items[] (array)
items[] (array)| Field | Type | Description |
|---|---|---|
| id | string | Unique identifier of the item. |
| inclusive_tax | integer | Tax included in the item price, in cents. |
| name | string | Item name. |
| price | integer | Total item price including quantity, modifiers, and discounts, in cents. |
| quantity | integer | Item quantity numerator. |
| sent | boolean | Indicates whether the item has been sent for preparation. |
| sent_at | timestamp | Time the item was sent for preparation. |
| split | integer | Item quantity denominator. |
| _embedded | object | Embedded item resources. |
items[].employee (object)
items[].employee (object)| Field | Type | Description |
|---|---|---|
| check_name | string | Employee name as printed on receipts. |
| first_name | string | Employee first name. |
| id | string | Unique employee identifier. |
| last_name | string | Employee last name. |
| login | string | Employee POS login/PIN. |
| middle_name | string | Employee middle name. |
| pos_id | string | Employee ID shown in the POS. |
| start_date | string | Employee start date (YYYY-MM-DD). |
items[].order_type (object)
items[].order_type (object)| Field | Type | Description |
|---|---|---|
| available | boolean | Whether tickets can be opened using this order type. |
| id | string | Unique order type identifier. |
| name | string | Order type name. |
| pos_id | string | Order type ID shown in the POS. |
items[].revenue_center (object)
items[].revenue_center (object)| Field | Type | Description |
|---|---|---|
| default | boolean | Indicates whether this is the default revenue center. |
| id | string | Unique revenue center identifier. |
| name | string | Revenue center name. |
| pos_id | string | Revenue center ID shown in the POS. |
items[].table (object)
items[].table (object)| Field | Type | Description |
|---|---|---|
| available | boolean | Whether the table is available for seating. |
| id | string | Unique table identifier. |
| name | string | Table name. |
| number | integer | Table number. |
| pos_id | string | Table ID shown in the POS. |
| seats | integer | Number of seats at the table. |
payments[] (array)
payments[] (array)| Field | Type | Description |
|---|---|---|
| id | string | Unique payment identifier. |
| amount | integer | Payment amount, in cents. |
| tender_types_id | integer | Tender type identifier applied to the payment. |
| tip | integer | Tip amount, in cents. |
| inv_number | integer | Reference number for the transaction (15-character limit). |
items[]._embedded.modifiers[] (array)
items[]._embedded.modifiers[] (array)| Field | Type | Description |
|---|---|---|
| id | integer | Unique modifier identifier. |
| name | string | Modifier name. |
| price | integer | Modifier price, in cents. |
| quantity | integer | Modifier quantity. |
| comment | string | Comment attached to the modifier. |
Request Example
{
"ticket": {
"void": false,
"id": "rika19882"
},
"payment": {
"id": "desk41231",
"amount": 666,
"inv_number": "INV-1024"
},
"user_id_external": 5488,
"webhook_id": "7614fc1bd4saf6",
"terminal_id": 100
}
cURL Example
curl --request POST \
--url https://sandbox-api.softpoint.io/interface/v1/locations/YOUR_LOCATION_ID/webhook/sale \
--header "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"ticket": { "void": false, "id": "rika19882" },
"payment": { "id": "desk41231", "amount": 666, "inv_number": "INV-1024" },
"user_id_external": 5488,
"webhook_id": "7614fc1bd4saf6",
"terminal_id": 100
}'
Success Response
An approved sale returns response: "APPROVED" together with the settled transaction details.
{
"response": "APPROVED",
"data": {
"id": 7137,
"id_external": null,
"amount": 1081,
"tip": 400,
"surcharge": 0,
"transaction_id": "28040181",
"card_holder": "Test/Card 01",
"reference_id": "8",
"auth_code": "TAS547",
"card_type": "VISA",
"last4": "0010",
"entry_type": "Chip",
"customer_id_external": "inf3509263926"
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response | string | Transaction outcome. Either APPROVED or DECLINE. |
| data | object | Transaction detail object. |
| data.id | integer | Softpoint transaction identifier. |
| data.id_external | string | External transaction reference; may be null. |
| data.amount | integer | Total amount charged, in minor units (cents). |
| data.tip | integer | Tip amount applied at the terminal, in minor units. |
| data.surcharge | integer | Surcharge amount applied, in minor units. |
| data.transaction_id | string | Processor transaction identifier. |
| data.card_holder | string | Cardholder name; may be null. |
| data.reference_id | string | Reference identifier returned by the processor. |
| data.auth_code | string | Authorization code returned by the issuer. |
| data.card_type | string | Card brand, for example VISA or AMEX. |
| data.last4 | string | Last four digits of the card number. |
| data.entry_type | string | How the card was read, for example Chip or Swipe. |
| data.customer_id_external | string | External customer identifier associated with the card, when present. |
Error Responses
Declined Transaction
A decline is returned with HTTP 200 and response: "DECLINE". Inspect the response field rather than relying solely on the status code.
{
"response": "DECLINE",
"data": {
"id": 17845,
"id_external": "7da29929a5548",
"amount": 0,
"tip": 0,
"total": 0,
"refunded_amount": 0,
"surcharge": 0,
"transaction_id": "",
"card_holder": "DI Test/Card 01",
"reference_id": "",
"auth_code": "",
"card_type": "AMEX",
"last4": "0119",
"entry_type": "Chip",
"status": 2
}
}
Terminal Not Found
Returned when the supplied terminal_id does not correspond to a registered terminal.
{
"error": {
"code": 10107,
"message": "The selected terminal doesn't exist.",
"details": null
}
}
Missing Required Parameter
Returned when a required field, such as terminal_id, is absent from the request body.
{
"error": {
"code": 10109,
"message": "The request is missing one or more required parameter(s).",
"details": {
"terminal_id": [
"missing_required_parameters"
]
}
}
}
Status Codes
| Code | Description |
|---|---|
| 200 | Request processed. Inspect response for the outcome. |
| 400 | Missing or invalid request parameters. |
| 401 | Missing or invalid access token. |
| 404 | The selected terminal does not exist. |
Workflow Placement
The Sale endpoint is the terminal step in the card-present payment flow:
-
Generate an interface access token.
-
Register and configure the terminal.
-
Open or reference a ticket.
-
Submit the sale to the terminal.
-
Reconcile the result using the returned transaction details, or issue a refund if required.
Notes
-
Amounts are expressed in minor units (cents). The returned
amountreflects the final total, which may include tip and surcharge applied at the terminal and can therefore differ from the requestedpayment.amount. -
A decline is reported with HTTP 200 and
response: "DECLINE". Always branch on theresponsefield. -
The terminal interaction is asynchronous from the cardholder's perspective. Supply a
webhook_idto correlate the eventual result. -
The
ticketobject may be supplied either as a compact reference containing anidor as a fully embedded ticket structure. Both forms are accepted.
Common Mistakes
-
Treating an HTTP 200 response as a guaranteed approval without checking the
responsefield. -
Sending the amount in major units (dollars) instead of minor units (cents).
-
Omitting
terminal_id, which results in error code 10109. -
Referencing a terminal that has not been registered for the location, which results in error code 10107.

