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:

  1. Your POS creates a transaction via the relevant endpoint e.g. POST /v1/transactions for a purchase
  2. Your POS polls for updates via GET /v1/transactions/{transaction_id}
  3. As the transaction progresses, the API instructs your POS on what to display via the Action Framework
  4. Polling stops when the transaction reaches a terminal state - FINALISED or AWAITING_POS
⚠️

Plan for recovery before you build

Your 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:

StatusMeaningWhat to do
PENDINGTransaction in progress on the terminalUpdate your UI from data.message, then immediately poll for the next version
AWAITING_POSTerminal is waiting for an action from the POSStop polling; process the Action Framework to determine what action is required
FINALISEDTransaction is completeStop polling; read result_financial_status and result_amounts

Version handling

⚠️

Always use data.version from the response as the basis for your next min_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 codeMeaningWhat to do
transaction_not_found_within_timeoutThe requested version doesn't exist yetImmediately re-poll - this is expected behaviour, not an error
transaction_not_foundThe transaction ID does not existTreat 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:

ActionWhat to do
PRINT_MERCHANT_RECEIPTImmediately 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 element
  • key - references a matching entry in action_form.properties

Currently supported layout types:

TypeBehaviour
horizontal_layoutAll 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"
}
  • name is used as the JSON key when submitting the input value to a submit_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:

ActionWhat your POS should do
PRINT_MERCHANT_RECEIPTPrint data.merchant_receipt
PRINT_CUSTOMER_RECEIPTPrint data.customer_receipt
TRANSACTION_COMPLETEClose or complete the transaction flow in your POS
RETRY_TRANSACTIONRe-submit the same transaction with identical parameters
SETTLEMENT_COMPLETEClose or complete the settlement flow in your POS
RETRY_SETTLEMENTRe-submit the same settlement
TEST_ACTIONDemonstrate 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"
}
⚠️

Render details values 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:

FieldTypeDescription
idstringUnique identifier for the transaction
versionintegerCurrent version - use as basis for next min_version
statusstringPENDING, AWAITING_POS, or FINALISED
messagestringHuman-readable status message to display on the POS
merchant_receiptstring | nullMerchant receipt data, available when ready for printing
customer_receiptstring | nullCustomer receipt data, available when ready for printing
result_amountsobject | nullPopulated when FINALISED - all amounts in cents
result_financial_statusstring | nullPopulated when FINALISED - APPROVED, DECLINED, CANCELLED, or UNKNOWN
pos_instructionsobjectAction Framework instructions - auto_actions, action_form

result_amounts fields (all in cents, populated when FINALISED):

FieldDescription
purchase_amountPurchase amount if applicable
tip_amountTip amount if applicable
surcharge_amountSurcharge amount if applicable
cashout_amountCashout amount if applicable
refund_amountRefund amount if applicable
moto_amountMOTO 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.