Overview
In this guide, you will learn how SCI processes transactions between your POS and the EFTPOS terminal, and how to build the dynamic transaction UI using the Action Framework.
How transactions work
Every interaction with the terminal - purchases, refunds, settlements - follows the same pattern:
- Your POS creates a transaction via the relevant endpoint e.g.
POST /v1/transactionsfor a purchase - Your POS polls for updates via
GET /v1/transactions/{transaction_id} - As the transaction progresses, the API instructs your POS on what to display via the Action Framework
- Polling stops when the transaction reaches a terminal state -
FINALISEDorAWAITING_POS
Plan for recovery before you buildYour polling loop must have an exit strategy for when connectivity is lost or no response is received within a reasonable time. Without this, your POS risks polling indefinitely and leaving a transaction unresolved.
This is handled through the override flow - see Transaction recovery for full implementation details.
The polling loop
Creating a transaction
All transaction types are created with a single endpoint:
POST /v1/transactions
The initial response always looks like this - version: 1, status: PENDING, with null fields for anything not yet determined:
{
"data": {
"id": "30fd4b36-a63a-4552-ad5e-b0a2d5223a56",
"version": 1,
"status": "PENDING",
"message": "Waiting for terminal to accept transaction",
"merchant_receipt": null,
"customer_receipt": null,
"result_amounts": null,
"result_financial_status": null,
"pos_instructions": {
"auto_actions": null,
"action_form": {
"layout": null,
"properties": null,
"details": {
"Pairing ID": "pid_88d93604-5d7a-4492-b403-c7c688895183",
"Transaction ID": "30fd4b36-a63a-4552-ad5e-b0a2d5223a56",
"Transaction Version": "1"
}
}
}
}
}Store the id - you will use it to poll for updates. Store version - you will use it to request the next update.
Polling for updates
Poll for the next version of the transaction using:
GET /v1/transactions/{transaction_id}?min_version={N}
On each successful response, read data.status to determine what to do next:
| Status | Meaning | What to do |
|---|---|---|
PENDING | Transaction in progress on the terminal | Update your UI from data.message, then immediately poll for the next version |
AWAITING_POS | Terminal is waiting for an action from the POS | Stop polling; process the Action Framework to determine what action is required |
FINALISED | Transaction is complete | Stop polling; read result_financial_status and result_amounts |
Version handling
Always usedata.versionfrom the response as the basis for your nextmin_version- never self-increment from your previous request value.
The API may skip versions if more than one update occurred between polls. For example, if you poll for min_version=2 and the response returns data.version: 4, your next request should use min_version=5.
Long-poll behaviour and 404 responses
If the requested min_version is not yet available, the API will hold the connection open briefly before returning a 404. There are two distinct 404 cases your POS must handle differently:
| Error code | Meaning | What to do |
|---|---|---|
transaction_not_found_within_timeout | The requested version doesn't exist yet | Immediately re-poll - this is expected behaviour, not an error |
transaction_not_found | The transaction ID does not exist | Treat as a genuine error |
The Action Framework
The Action Framework is how the SCI API instructs your POS on what to display during a transaction. Rather than your POS driving its own static UI, the API returns instructions that tell your POS exactly what elements to render and what actions are available to the merchant.
Every transaction response includes a pos_instructions object:
"pos_instructions": {
"auto_actions": [...],
"action_form": {
"layout": [...],
"properties": {...},
"details": {...}
}
}Auto actions
auto_actions is a list of functions your POS must trigger immediately upon receiving the response - before rendering any UI.
Currently supported auto actions:
| Action | What to do |
|---|---|
PRINT_MERCHANT_RECEIPT | Immediately send data.merchant_receipt to the printer |
Auto actions are triggered without any merchant input. For example, PRINT_MERCHANT_RECEIPT is present when pos_auto_print_signature_receipt was set to true when creating the transaction - the receipt prints automatically so the merchant can hand it to the customer for signing.
Action form layout
action_form.layout is an ordered list of layout containers. Each container has a type and an elements array specifying which properties to render inside it, and in what order.
Each element has:
label- the display label for the elementkey- references a matching entry inaction_form.properties
Currently supported layout types:
| Type | Behaviour |
|---|---|
horizontal_layout | All elements within this container are rendered side by side |
Example:
"layout": [
{
"type": "horizontal_layout",
"elements": [
{ "label": "Some Label", "key": "some_key" },
{ "label": "Another Label", "key": "another_key" }
]
}
]Action form properties
action_form.properties is a dictionary of all renderable elements, keyed by a string that matches key values in the layout. Each property has a type field and type-specific fields:
text - static display text
"helper_text": {
"type": "text",
"text": "Signature verification required, print receipt and verify signature."
}button - a clickable button. Either submit_url or action will be non-null, never both:
"print_merchant_receipt": {
"type": "button",
"submit_url": null,
"action": "PRINT_MERCHANT_RECEIPT"
},
"approve_signature": {
"type": "button",
"submit_url": "https://sci-api.<tenant-specific-domain>/v1/transactions/{id}?action=APPROVE_SIGNATURE",
"action": null
}submit_url- your POS POSTs to this URL when the button is clicked (used to send actions back to the API, e.g. signature approval)action- your POS calls this internal function when the button is clicked (see button actions below)
input - a text input field for merchant entry
"input_1": {
"type": "input",
"name": "input_name_1"
}nameis used as the JSON key when submitting the input value to asubmit_url
image - a rendered image
"image_1": {
"type": "image",
"mime": "image/jpeg",
"encoding": "base64",
"data": "/9j/4AAQ..."
}Button actions
When a button's action field is non-null, your POS calls the corresponding internal function:
| Action | What your POS should do |
|---|---|
PRINT_MERCHANT_RECEIPT | Print data.merchant_receipt |
PRINT_CUSTOMER_RECEIPT | Print data.customer_receipt |
TRANSACTION_COMPLETE | Close or complete the transaction flow in your POS |
RETRY_TRANSACTION | Re-submit the same transaction with identical parameters |
SETTLEMENT_COMPLETE | Close or complete the settlement flow in your POS |
RETRY_SETTLEMENT | Re-submit the same settlement |
TEST_ACTION | Demonstrate that button interactions work - used during certification |
Action form details
action_form.details is a flat key/value object containing informational data that should be displayed somewhere on your transaction UI - for example, at the bottom of the dialog:
"details": {
"Transaction ID": "d2e064a5-33a4-4dba-8cf0-fe2e378b7733",
"Pairing ID": "pid_e99b281d-3178-452b-bcfb-ed745858a116",
"Transaction Version": "1",
"TID": "tid123"
}
Renderdetailsvalues as-is for display only. Do not write any logic that depends on specific keys or values - these can change at any time.
Transaction response fields
For reference, here are all fields returned in a transaction response:
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the transaction |
version | integer | Current version - use as basis for next min_version |
status | string | PENDING, AWAITING_POS, or FINALISED |
message | string | Human-readable status message to display on the POS |
merchant_receipt | string | null | Merchant receipt data, available when ready for printing |
customer_receipt | string | null | Customer receipt data, available when ready for printing |
result_amounts | object | null | Populated when FINALISED - all amounts in cents |
result_financial_status | string | null | Populated when FINALISED - APPROVED, DECLINED, CANCELLED, or UNKNOWN |
pos_instructions | object | Action Framework instructions - auto_actions, action_form |
result_amounts fields (all in cents, populated when FINALISED):
| Field | Description |
|---|---|
purchase_amount | Purchase amount if applicable |
tip_amount | Tip amount if applicable |
surcharge_amount | Surcharge amount if applicable |
cashout_amount | Cashout amount if applicable |
refund_amount | Refund amount if applicable |
moto_amount | MOTO amount if applicable |
result_amounts is the source of truth for all final applied amounts - always use these values for receipts, tax invoices, and refund calculations rather than the amounts sent in the original request.
Updated about 20 hours ago