Documentation

Platform Admin POS Terminals

Manage POS terminals, processor mappings, and terminal ID inventory across all tenants

Overview

Platform admin endpoints for managing POS terminals across all tenants. These endpoints enable terminal provisioning, processor configuration, and inventory management.

Terminal Status

POS terminals have various operational statuses that control their ability to process transactions.

StatusDescriptionCan Process
activeTerminal is operational and can process transactionsYes
inactiveTerminal is registered but not yet activatedNo
suspendedTerminal temporarily disabled, can be reactivatedNo
blockedTerminal permanently blocked due to fraud or policy violationNo

Assignment Status

Terminals can be assigned to businesses or remain in inventory.

StatusDescription
assignedTerminal is linked to a business and settlement account
unassignedTerminal is in inventory, awaiting assignment

Terminal Architecture

Understanding the POS terminal data model.

ComponentDescription
TerminalPhysical POS device with serial number and hardware details
Processor MappingLinks terminal to payment processors (Nibss, Interswitch, etc.)
Settlement AccountBank account where transaction proceeds are settled
Virtual AccountDedicated account number for receiving payments
ProfileConfiguration template defining settlement frequency and fees

List Terminals

Retrieve POS terminals across all tenants with comprehensive filtering and enrichment options.

Endpoint

GET/api/v1/admin/pos-terminals

List all POS terminals with filtering by tenant, profile, status, assignment, and search. Supports optional enrichment to include related data like business details and settlement accounts.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Query Parameters

ParameterTypeRequiredDescription
tenant_idstringNoFilter by tenant UUID
profile_idstringNoFilter by terminal profile UUID
statusstringNoFilter by status (can be repeated for multiple values)Values: active, inactive, suspended, blocked
assignmentstringNoFilter by assignment statusValues: assigned, unassigned
searchstringNoSearch by terminal ID, serial number, name, or business
sortstringNo (default: created_at)Sort fieldValues: created_at, terminal_id, status, terminal_name
orderstringNo (default: desc)Sort orderValues: asc, desc
pageintegerNo (default: 1)Page number
page_sizeintegerNo (default: 50)Page size (max 200)
include_tenantbooleanNo (default: false)Include tenant name in response
include_businessbooleanNo (default: false)Include assigned business details
include_settlementbooleanNo (default: false)Include settlement account details
include_virtual_accountbooleanNo (default: false)Include virtual account details
include_processor_mappingsbooleanNo (default: false)Include processor ID mappings

Response- Paginated list of terminals with optional enrichment

json
{
  "terminals": [
    {
      "id": "term_550e8400-e29b-41d4-a716-446655440000",
      "tenant_id": "tenant_660e8400-e29b-41d4-a716-446655440001",
      "terminal_id": "2070LA12",
      "terminal_name": "Main Store POS",
      "status": "active",
      "processor_id": "proc_770e8400-e29b-41d4-a716-446655440002",
      "serial_number": "PAX-A920-12345678",
      "device_model": "PAX A920",
      "oem": "PAX Technology",
      "assigned": true,
      "activated_at": "2025-01-10T08:00:00Z",
      "created_at": "2025-01-05T10:00:00Z",
      "updated_at": "2025-01-14T12:00:00Z",
      "metadata": {
        "firmware_version": "1.2.3"
      },
      "tenant_name": "Acme Corp",
      "business_name": "Acme Retail Store",
      "business": {
        "id": "biz_880e8400-e29b-41d4-a716-446655440003",
        "name": "Acme Retail Store"
      },
      "settlement_account": {
        "account_code": "0123456789",
        "account_name": "Acme Retail Store",
        "bank_code": "058",
        "bank_name": "GTBank",
        "provider": "paystack"
      },
      "virtual_account": {
        "account_code": "9900123456",
        "bank_code": "101",
        "bank_name": "Providus Bank",
        "provider": "providus",
        "status": "active",
        "virtual_account_id": "va_990e8400-e29b-41d4-a716-446655440004"
      },
      "processor_ids": [
        {
          "processor_id": "nibss",
          "terminal_id": "2070LA12"
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 50,
    "total": 1250
  }
}

Error Responses

StatusCodeDescription
401unauthorizedMissing or invalid admin JWT
403forbiddenAdmin role required
400invalid_filterInvalid filter parameter value

Get Terminal Details

Retrieve detailed information about a specific terminal including assignment history and events.

Endpoint

GET/api/v1/admin/pos-terminals/{terminalID}

Get comprehensive terminal details including contact information, settlement configuration, geolocation, assignment history, and recent events.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Path Parameters

ParameterTypeRequiredDescription
terminalIDstringYesUUID of the terminal

Query Parameters

ParameterTypeRequiredDescription
include_assignmentsbooleanNo (default: false)Include assignment history
include_eventsbooleanNo (default: false)Include last 25 terminal events
include_configbooleanNo (default: false)Include effective configuration
include_processorsbooleanNo (default: false)Include processor mappings

Response- Complete terminal details

json
{
  "id": "term_550e8400-e29b-41d4-a716-446655440000",
  "tenant_id": "tenant_660e8400-e29b-41d4-a716-446655440001",
  "terminal_id": "2070LA12",
  "terminal_name": "Main Store POS",
  "status": "active",
  "processor_id": "proc_770e8400-e29b-41d4-a716-446655440002",
  "device_serial": "PAX-A920-12345678",
  "device_model": "PAX A920",
  "oem": "PAX Technology",
  "activated_at": "2025-01-10T08:00:00Z",
  "created_at": "2025-01-05T10:00:00Z",
  "updated_at": "2025-01-14T12:00:00Z",
  "contact_name": "John Manager",
  "contact_email": "john@acmeretail.com",
  "contact_phone": "+2348012345678",
  "onboarding_notes": "Installed at main checkout counter",
  "settlement_account_id": "acc_110e8400-e29b-41d4-a716-446655440005",
  "settlement_frequency": "end_of_day",
  "settlement_enabled": true,
  "geo_latitude": 6.5244,
  "geo_longitude": 3.3792,
  "tenant_name": "Acme Corp",
  "settlement_account": {
    "account_code": "0123456789",
    "account_name": "Acme Retail Store",
    "bank_code": "058",
    "bank_name": "GTBank"
  },
  "current_assignment": {
    "id": "assign_220e8400-e29b-41d4-a716-446655440006",
    "ledger_account_id": "ledger_330e8400-e29b-41d4-a716-446655440007",
    "business_id": "biz_880e8400-e29b-41d4-a716-446655440003",
    "business_name": "Acme Retail Store",
    "assignment_reason": "New store opening",
    "assigned_by": "admin_440e8400-e29b-41d4-a716-446655440008",
    "assigned_at": "2025-01-10T08:00:00Z"
  },
  "assignments": [
    {
      "id": "assign_220e8400-e29b-41d4-a716-446655440006",
      "ledger_account_id": "ledger_330e8400-e29b-41d4-a716-446655440007",
      "ledger_account_name": "Acme Retail Store",
      "assignment_reason": "New store opening",
      "assigned_at": "2025-01-10T08:00:00Z",
      "business_name": "Acme Retail Store"
    }
  ],
  "events": [
    {
      "event_type": "terminal_activated",
      "timestamp": "2025-01-10T08:00:00Z",
      "actor_id": "admin_440e8400-e29b-41d4-a716-446655440008",
      "actor_name": "Admin User"
    },
    {
      "event_type": "transaction_completed",
      "timestamp": "2025-01-14T15:30:00Z",
      "metadata": {
        "amount": 15000,
        "channel": "card_processor"
      }
    }
  ],
  "effective_config": {
    "settlement_frequency": "end_of_day",
    "max_transaction_amount": 5000000,
    "allowed_channels": [
      "card_processor",
      "nfc_purchase"
    ]
  }
}

Error Responses

StatusCodeDescription
404terminal_not_foundTerminal does not exist
403forbiddenAdmin role required

Terminal Metrics

Platform-wide POS terminal metrics and time series data.

Endpoint

GET/api/v1/admin/pos-terminals/metrics

Get aggregated terminal metrics showing total, active, and assigned terminal counts over time. Useful for dashboards and trend analysis.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Query Parameters

ParameterTypeRequiredDescription
fromstringYesStart date (RFC3339 format)
tostringYesEnd date (RFC3339 format)
intervalstringNo (default: day)Time bucket intervalValues: day, week, month
tenant_idstringNoFilter metrics by tenant UUID

Response- Time series of terminal metrics

json
{
  "series": [
    {
      "bucket": "2025-01-13T00:00:00Z",
      "total": 1200,
      "active": 1150,
      "assigned": 1100
    },
    {
      "bucket": "2025-01-14T00:00:00Z",
      "total": 1250,
      "active": 1180,
      "assigned": 1120
    }
  ]
}

Error Responses

StatusCodeDescription
400invalid_date_rangeInvalid from/to date format or range
403forbiddenAdmin role required

Terminal Analytics

Detailed transaction analytics for a specific terminal.

Endpoint

GET/api/v1/admin/pos-terminals/{terminalID}/analytics

Get comprehensive transaction analytics for a specific terminal including volume, success rates, settlement totals, and channel breakdown.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Path Parameters

ParameterTypeRequiredDescription
terminalIDstringYesUUID of the terminal

Query Parameters

ParameterTypeRequiredDescription
fromstringYesStart date (RFC3339 format)
tostringYesEnd date (RFC3339 format)
intervalstringNo (default: day)Time bucket intervalValues: day, week, month

Response- Terminal analytics with summary and time series

json
{
  "terminal_id": "term_550e8400-e29b-41d4-a716-446655440000",
  "period": {
    "from": "2025-01-01T00:00:00Z",
    "to": "2025-01-14T23:59:59Z",
    "interval": "day"
  },
  "summary": {
    "transaction_count": 1250,
    "transaction_volume": 15750000,
    "successful_transactions": 1200,
    "failed_transactions": 50,
    "success_rate": 96,
    "average_transaction_size": 12600,
    "total_settled": 15500000,
    "total_fees": 250000,
    "last_transaction_at": "2025-01-14T15:30:00Z",
    "days_since_last_transaction": 0
  },
  "series": [
    {
      "bucket": "2025-01-14T00:00:00Z",
      "transaction_count": 85,
      "transaction_volume": 1275000,
      "successful_transactions": 82,
      "failed_transactions": 3,
      "success_rate": 96.47,
      "average_transaction_size": 15000,
      "settled_amount": 1250000,
      "fees_collected": 25000
    }
  ],
  "by_channel": {
    "card_processor": {
      "count": 950,
      "volume": 12000000,
      "success_rate": 97.5
    },
    "nfc_purchase": {
      "count": 200,
      "volume": 2500000,
      "success_rate": 95
    },
    "va_pos_payment": {
      "count": 100,
      "volume": 1250000,
      "success_rate": 92
    }
  }
}

Error Responses

StatusCodeDescription
404terminal_not_foundTerminal does not exist
400invalid_date_rangeInvalid date range parameters

Prepare Collection

Prepare a terminal for collection by creating a pending payment session.

Endpoint

POST/api/v1/admin/pos-terminals/{terminalID}/prepare-collection

Create a pending collection session on a terminal. This prepares the terminal to receive a specific payment amount and returns session details for tracking.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}
Content-TypestringYesapplication/json

Path Parameters

ParameterTypeRequiredDescription
terminalIDstringYesUUID of the terminal

Request Body- Collection preparation details

json
{
  "amount": 50000,
  "currency": "NGN",
  "reference": "INV-2025-001",
  "description": "Payment for order #12345",
  "expires_in_minutes": 30,
  "metadata": {
    "order_id": "12345",
    "customer_id": "cust_123"
  }
}

Response- Collection session created

json
{
  "session_id": "sess_550e8400-e29b-41d4-a716-446655440009",
  "terminal_id": "term_550e8400-e29b-41d4-a716-446655440000",
  "amount": 50000,
  "currency": "NGN",
  "reference": "INV-2025-001",
  "status": "pending",
  "expires_at": "2025-01-14T16:30:00Z",
  "created_at": "2025-01-14T16:00:00Z"
}

Error Responses

StatusCodeDescription
404terminal_not_foundTerminal does not exist
400terminal_inactiveTerminal is not active
400terminal_unassignedTerminal is not assigned to a business

Request Body Parameters

FieldTypeRequiredDescription
amountintegerYesAmount in minor units (kobo for NGN)
currencystringNoCurrency code (default: NGN)
referencestringNoExternal reference for reconciliation
descriptionstringNoPayment description
expires_in_minutesintegerNoSession expiry time in minutes (default: 30)
metadataobjectNoCustom metadata to attach to the session

Processor Mappings

Manage terminal-to-processor ID mappings. Each terminal can have multiple processor mappings with one marked as primary.

List Processor Mappings

GET/api/v1/admin/pos-terminals/{terminalID}/processors

Get all processor ID mappings for a terminal. Returns the list of payment processors configured for this terminal.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Path Parameters

ParameterTypeRequiredDescription
terminalIDstringYesUUID of the terminal

Response- List of processor mappings

json
{
  "processor_ids": [
    {
      "id": "map_550e8400-e29b-41d4-a716-446655440010",
      "processor_code": "nibss",
      "processor_terminal_id": "2070LA12",
      "is_primary": true,
      "active": true,
      "created_at": "2025-01-10T08:00:00Z",
      "updated_at": "2025-01-10T08:00:00Z"
    },
    {
      "id": "map_660e8400-e29b-41d4-a716-446655440011",
      "processor_code": "interswitch",
      "processor_terminal_id": "ISW123456",
      "is_primary": false,
      "active": true,
      "created_at": "2025-01-12T10:00:00Z",
      "updated_at": "2025-01-12T10:00:00Z"
    }
  ]
}

Error Responses

StatusCodeDescription
404terminal_not_foundTerminal does not exist

Create Processor Mapping

POST/api/v1/admin/pos-terminals/{terminalID}/processors

Add a new processor mapping to a terminal. This links the terminal to a payment processor using a processor-specific terminal ID.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}
Content-TypestringYesapplication/json

Path Parameters

ParameterTypeRequiredDescription
terminalIDstringYesUUID of the terminal

Request Body- Processor mapping details

json
{
  "processor_code": "nibss",
  "processor_terminal_id": "2070LA12",
  "is_primary": true
}

Response- Processor mapping created

json
{
  "id": "map_550e8400-e29b-41d4-a716-446655440010",
  "processor_code": "nibss",
  "processor_terminal_id": "2070LA12",
  "is_primary": true,
  "active": true,
  "created_at": "2025-01-14T16:00:00Z",
  "updated_at": "2025-01-14T16:00:00Z"
}

Error Responses

StatusCodeDescription
400duplicate_mappingProcessor mapping already exists for this terminal
400invalid_processorUnknown processor code

Set Primary Mapping

PATCH/api/v1/admin/pos-terminals/{terminalID}/processors/{mappingID}/primary

Set a processor mapping as the primary (default) processor for transactions on this terminal.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Path Parameters

ParameterTypeRequiredDescription
terminalIDstringYesUUID of the terminal
mappingIDstringYesUUID of the processor mapping

Response- No content on success

json

Error Responses

StatusCodeDescription
404mapping_not_foundProcessor mapping does not exist

Delete Mapping

DELETE/api/v1/admin/pos-terminals/{terminalID}/processors/{mappingID}

Deactivate a processor mapping. The mapping is soft-deleted and can no longer be used for transactions.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Path Parameters

ParameterTypeRequiredDescription
terminalIDstringYesUUID of the terminal
mappingIDstringYesUUID of the processor mapping

Response- No content on success

json

Error Responses

StatusCodeDescription
404mapping_not_foundProcessor mapping does not exist
400cannot_delete_primaryCannot delete the primary mapping without setting another as primary first

Terminal ID Inventory

Manage the pool of processor-assigned terminal IDs. Terminal IDs are provisioned by payment processors and must be assigned to terminals before transactions can be processed.

Reserve Terminal ID

POST/api/v1/admin/pos-terminals/{terminalID}/processor-ids/reserve

Reserve a terminal ID from the available pool for a specific terminal. The ID is marked as reserved until assigned or released.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}
Content-TypestringYesapplication/json

Path Parameters

ParameterTypeRequiredDescription
terminalIDstringYesUUID of the terminal to reserve for

Request Body- Reservation request

json
{
  "processor_code": "nibss"
}

Response- Terminal ID reserved

json
{
  "inventory_id": "inv_550e8400-e29b-41d4-a716-446655440012",
  "processor_code": "nibss",
  "processor_terminal_id": "2070LA15",
  "status": "reserved",
  "reserved_for_terminal_id": "term_550e8400-e29b-41d4-a716-446655440000",
  "reserved_at": "2025-01-14T16:00:00Z",
  "expires_at": "2025-01-14T17:00:00Z"
}

Error Responses

StatusCodeDescription
404no_available_idsNo terminal IDs available for the specified processor
400already_reservedTerminal already has a reservation for this processor

Assign Terminal ID

POST/api/v1/admin/pos-terminals/{terminalID}/processor-ids/{inventoryID}/assign

Assign a reserved terminal ID to the terminal. This creates the processor mapping and marks the inventory item as assigned.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Path Parameters

ParameterTypeRequiredDescription
terminalIDstringYesUUID of the terminal
inventoryIDstringYesUUID of the inventory item (reserved terminal ID)

Response- Terminal ID assigned

json
{
  "inventory_id": "inv_550e8400-e29b-41d4-a716-446655440012",
  "processor_code": "nibss",
  "processor_terminal_id": "2070LA15",
  "status": "assigned",
  "assigned_to_terminal_id": "term_550e8400-e29b-41d4-a716-446655440000",
  "assigned_at": "2025-01-14T16:15:00Z",
  "mapping_id": "map_770e8400-e29b-41d4-a716-446655440013"
}

Error Responses

StatusCodeDescription
404inventory_not_foundInventory item not found
400not_reservedTerminal ID is not reserved for this terminal
400reservation_expiredReservation has expired

Import Terminal IDs

POST/api/v1/admin/pos-processors/{processorID}/terminal-ids/import

Bulk import terminal IDs from a payment processor into the inventory pool. Used when receiving new terminal ID allocations from processors.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}
Content-TypestringYesapplication/json

Path Parameters

ParameterTypeRequiredDescription
processorIDstringYesUUID or code of the payment processor

Request Body- Terminal IDs to import

json
{
  "terminal_ids": [
    "2070LA20",
    "2070LA21",
    "2070LA22",
    "2070LA23",
    "2070LA24"
  ],
  "batch_reference": "BATCH-2025-001",
  "notes": "Q1 2025 allocation from NIBSS"
}

Response- Import results

json
{
  "imported": 5,
  "duplicates": 0,
  "failed": 0,
  "batch_reference": "BATCH-2025-001",
  "inventory_ids": [
    "inv_880e8400-e29b-41d4-a716-446655440014",
    "inv_990e8400-e29b-41d4-a716-446655440015",
    "inv_aa0e8400-e29b-41d4-a716-446655440016",
    "inv_bb0e8400-e29b-41d4-a716-446655440017",
    "inv_cc0e8400-e29b-41d4-a716-446655440018"
  ]
}

Error Responses

StatusCodeDescription
400invalid_processorUnknown processor
400empty_listNo terminal IDs provided

List Terminal IDs

GET/api/v1/admin/pos-processors/{processorID}/terminal-ids

List terminal IDs in inventory for a specific processor. Supports filtering by status (available, reserved, assigned, retired).

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Path Parameters

ParameterTypeRequiredDescription
processorIDstringYesUUID or code of the payment processor

Query Parameters

ParameterTypeRequiredDescription
statusstringNoFilter by inventory statusValues: available, reserved, assigned, retired
searchstringNoSearch by terminal ID
limitintegerNo (default: 50)Page size
offsetintegerNo (default: 0)Page offset

Response- Paginated list of terminal IDs

json
{
  "data": [
    {
      "id": "inv_880e8400-e29b-41d4-a716-446655440014",
      "processor_code": "nibss",
      "processor_terminal_id": "2070LA20",
      "status": "available",
      "batch_reference": "BATCH-2025-001",
      "created_at": "2025-01-14T16:00:00Z"
    },
    {
      "id": "inv_550e8400-e29b-41d4-a716-446655440012",
      "processor_code": "nibss",
      "processor_terminal_id": "2070LA15",
      "status": "assigned",
      "assigned_to_terminal_id": "term_550e8400-e29b-41d4-a716-446655440000",
      "assigned_at": "2025-01-14T16:15:00Z",
      "created_at": "2025-01-10T08:00:00Z"
    }
  ],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "total_count": 150
  }
}

Error Responses

StatusCodeDescription
400invalid_processorUnknown processor

Release Terminal ID

POST/api/v1/admin/pos-processors/terminal-ids/{inventoryID}/release

Release a terminal ID back to the available pool. Used when unassigning a terminal or canceling a reservation.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Path Parameters

ParameterTypeRequiredDescription
inventoryIDstringYesUUID of the inventory item

Request Body- Release reason (optional)

json
{
  "reason": "Terminal decommissioned"
}

Response- Terminal ID released

json
{
  "inventory_id": "inv_550e8400-e29b-41d4-a716-446655440012",
  "processor_terminal_id": "2070LA15",
  "status": "available",
  "released_at": "2025-01-14T17:00:00Z"
}

Error Responses

StatusCodeDescription
404inventory_not_foundInventory item not found
400already_availableTerminal ID is already available

Retire Terminal ID

PATCH/api/v1/admin/pos-processors/terminal-ids/{inventoryID}/retire

Permanently retire a terminal ID. Used when a terminal ID is no longer valid or has been revoked by the processor.

Headers

ParameterTypeRequiredDescription
AuthorizationstringYesBearer {admin_access_token}

Path Parameters

ParameterTypeRequiredDescription
inventoryIDstringYesUUID of the inventory item

Request Body- Retirement reason

json
{
  "reason": "Revoked by NIBSS due to compliance issue"
}

Response- Terminal ID retired

json
{
  "inventory_id": "inv_dd0e8400-e29b-41d4-a716-446655440019",
  "processor_terminal_id": "2070LA05",
  "status": "retired",
  "retired_at": "2025-01-14T17:30:00Z",
  "retired_reason": "Revoked by NIBSS due to compliance issue"
}

Error Responses

StatusCodeDescription
404inventory_not_foundInventory item not found
400currently_assignedCannot retire an assigned terminal ID - release it first

Inventory Status Reference

Terminal ID inventory status lifecycle.

Status Definitions

StatusDescriptionCan Be AssignedNext States
availableIn pool, ready for reservation or direct assignmentYesreserved, assigned, retired
reservedTemporarily held for a specific terminalNoassigned, available (on expiry/release)
assignedActively linked to a terminalNoavailable (on release)
retiredPermanently removed from circulationNoNone (terminal state)

Inventory Lifecycle

Typical flow: Import → Available → Reserved → Assigned → Released → Available (or Retired if invalid)