> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Retail Kiosk

> Build an AI-powered retail kiosk with receipt printing, NFC, and customer help requests

## Overview

**Industry:** Retail

A customer-facing kiosk that prints receipts, reads NFC loyalty cards, displays messages on screen, and lets customers request help from the AI agent.

**Commands:**

* `print_receipt` -- Print a formatted receipt
* `read_nfc_card` -- Read an NFC loyalty card
* `display_message` -- Show a message on the kiosk screen

**Triggers:**

* `customer_help_requested` -- Fires when a customer presses the help button

## Device Client

<CodeGroup>
  ```typescript Node.js theme={null}
  import { DeviceClient } from '@lua-ai-global/device-client';

  // Simulated hardware interfaces
  const printer = {
    print: async (lines: string[]) => {
      console.log('--- RECEIPT ---');
      lines.forEach(l => console.log(l));
      console.log('--- END ---');
      return { printed: true, lineCount: lines.length };
    },
  };

  const nfc = {
    read: async () => ({
      cardId: 'NFC-8847-2024',
      memberName: 'Jane Smith',
      tier: 'Gold',
      points: 2450,
    }),
  };

  const display = {
    show: async (message: string, style: string) => {
      console.log(`[DISPLAY ${style}] ${message}`);
      return { displayed: true };
    },
  };

  const device = new DeviceClient({
    agentId: process.env.LUA_AGENT_ID!,
    apiKey: process.env.LUA_API_KEY!,
    deviceName: 'store-kiosk-01',
    group: 'retail-kiosks',
    commands: [
      {
        name: 'print_receipt',
        description: 'Print a receipt on the kiosk thermal printer. Provide an array of text lines to print.',
        inputSchema: {
          type: 'object',
          properties: {
            lines: {
              type: 'array',
              items: { type: 'string' },
              description: 'Array of text lines to print on the receipt',
            },
            header: { type: 'string', description: 'Store name or header text' },
            footer: { type: 'string', description: 'Footer text (e.g., return policy)' },
          },
          required: ['lines'],
        },
        timeoutMs: 15000,
      },
      {
        name: 'read_nfc_card',
        description: 'Activate the NFC reader and scan a loyalty card. Returns member name, tier, and points balance. Prompts the customer to tap their card.',
        timeoutMs: 30000,
      },
      {
        name: 'display_message',
        description: 'Show a message on the kiosk display screen',
        inputSchema: {
          type: 'object',
          properties: {
            message: { type: 'string', description: 'Message text to display' },
            style: { type: 'string', enum: ['welcome', 'info', 'success', 'error'], default: 'info' },
          },
          required: ['message'],
        },
      },
    ],
  });

  device.onCommand('print_receipt', async (payload) => {
    const lines: string[] = [];

    if (payload.header) {
      lines.push('================================');
      lines.push(`  ${payload.header}`);
      lines.push('================================');
    }

    lines.push('');
    lines.push(...(payload.lines || []));
    lines.push('');

    if (payload.footer) {
      lines.push('--------------------------------');
      lines.push(payload.footer);
    }

    lines.push(`  ${new Date().toLocaleString()}`);

    const result = await printer.print(lines);
    return { ...result, totalLines: lines.length };
  });

  device.onCommand('read_nfc_card', async () => {
    await display.show('Please tap your loyalty card...', 'info');
    const card = await nfc.read();
    await display.show(`Welcome back, ${card.memberName}!`, 'success');
    return card;
  });

  device.onCommand('display_message', async (payload) => {
    const result = await display.show(payload.message, payload.style || 'info');
    return { ...result, message: payload.message };
  });

  async function main() {
    await device.connect();
    console.log('Retail kiosk online');

    await display.show('Welcome! How can I help you today?', 'welcome');

    // Simulate a customer pressing the help button every few minutes
    setInterval(async () => {
      if (Math.random() < 0.1) {
        await device.trigger('customer_help_requested', {
          kiosk: 'store-kiosk-01',
          location: 'entrance',
          timestamp: new Date().toISOString(),
        });
      }
    }, 30000);
  }

  main().catch(console.error);
  ```

  ```python Python theme={null}
  import asyncio
  import os
  import random
  from datetime import datetime, timezone
  from lua_device import DeviceClient, DeviceCommandDefinition

  # Simulated hardware interfaces
  async def printer_print(lines):
      print("--- RECEIPT ---")
      for line in lines:
          print(line)
      print("--- END ---")
      return {"printed": True, "lineCount": len(lines)}

  async def nfc_read():
      return {
          "cardId": "NFC-8847-2024",
          "memberName": "Jane Smith",
          "tier": "Gold",
          "points": 2450,
      }

  async def display_show(message, style):
      print(f"[DISPLAY {style}] {message}")
      return {"displayed": True}

  client = DeviceClient(
      agent_id=os.environ["LUA_AGENT_ID"],
      api_key=os.environ["LUA_API_KEY"],
      device_name="store-kiosk-01",
      group="retail-kiosks",
      commands=[
          DeviceCommandDefinition(
              name="print_receipt",
              description="Print a receipt on the kiosk thermal printer. Provide an array of text lines to print.",
              input_schema={
                  "type": "object",
                  "properties": {
                      "lines": {
                          "type": "array",
                          "items": {"type": "string"},
                          "description": "Array of text lines to print on the receipt",
                      },
                      "header": {"type": "string", "description": "Store name or header text"},
                      "footer": {"type": "string", "description": "Footer text (e.g., return policy)"},
                  },
                  "required": ["lines"],
              },
              timeout_ms=15000,
          ),
          DeviceCommandDefinition(
              name="read_nfc_card",
              description="Activate the NFC reader and scan a loyalty card. Returns member name, tier, and points balance. Prompts the customer to tap their card.",
              timeout_ms=30000,
          ),
          DeviceCommandDefinition(
              name="display_message",
              description="Show a message on the kiosk display screen",
              input_schema={
                  "type": "object",
                  "properties": {
                      "message": {"type": "string", "description": "Message text to display"},
                      "style": {"type": "string", "enum": ["welcome", "info", "success", "error"], "default": "info"},
                  },
                  "required": ["message"],
              },
          ),
      ],
  )

  @client.on_command("print_receipt")
  async def handle_print_receipt(payload):
      lines = []

      if payload.get("header"):
          lines.append("================================")
          lines.append(f"  {payload['header']}")
          lines.append("================================")

      lines.append("")
      lines.extend(payload.get("lines", []))
      lines.append("")

      if payload.get("footer"):
          lines.append("--------------------------------")
          lines.append(payload["footer"])

      lines.append(f"  {datetime.now().strftime('%c')}")

      result = await printer_print(lines)
      return {**result, "totalLines": len(lines)}

  @client.on_command("read_nfc_card")
  async def handle_read_nfc_card(payload):
      await display_show("Please tap your loyalty card...", "info")
      card = await nfc_read()
      await display_show(f"Welcome back, {card['memberName']}!", "success")
      return card

  @client.on_command("display_message")
  async def handle_display_message(payload):
      result = await display_show(payload["message"], payload.get("style", "info"))
      return {**result, "message": payload["message"]}

  async def main():
      await client.connect()
      print("Retail kiosk online")

      await display_show("Welcome! How can I help you today?", "welcome")

      # Simulate a customer pressing the help button every few minutes
      while True:
          if random.random() < 0.1:
              await client.trigger("customer_help_requested", {
                  "kiosk": "store-kiosk-01",
                  "location": "entrance",
                  "timestamp": datetime.now(timezone.utc).isoformat(),
              })
          await asyncio.sleep(30)

  asyncio.run(main())
  ```
</CodeGroup>

## Agent-Side Trigger Handler

```typescript theme={null}
// src/triggers/customer-help.ts
import { defineDeviceTrigger } from 'lua-cli';
import { z } from 'zod';

export const customerHelp = defineDeviceTrigger({
  name: 'customer-help-requested',
  description: 'Fired when a customer presses the help button on a retail kiosk',
  payloadSchema: z.object({
    kiosk: z.string(),
    location: z.string(),
    timestamp: z.string(),
  }),
  execute: async (payload, { agent, device }) => {
    await agent.chat(
      `A customer at kiosk "${payload.kiosk}" (${payload.location}) ` +
      `is requesting help. Please display a helpful greeting and ` +
      `ask what they need assistance with.`
    );
  },
});
```

## Agent Configuration

```typescript theme={null}
// src/index.ts
import { LuaAgent, LuaSkill } from 'lua-cli';
import { customerHelp } from './triggers/customer-help';

const retailSkill = new LuaSkill({
  name: 'retail-kiosk',
  description: 'Retail kiosk customer service',
  context: `
    You operate customer-facing retail kiosks.

    Device tools:
    - print_receipt: Print receipts. Format nicely with header, items, and footer.
    - read_nfc_card: Scan loyalty cards. Use when customer wants to check points or redeem rewards.
    - display_message: Show messages on screen. Use 'welcome' for greetings, 'success' for confirmations.

    Guidelines:
    - Always greet customers warmly
    - After reading a loyalty card, mention their tier and points
    - Include store name in receipt headers
    - Add return policy in receipt footers
  `,
  tools: [],
});

export const agent = new LuaAgent({
  name: 'retail-assistant',
  persona: `You are a friendly retail assistant at a kiosk. Help customers check loyalty points,
    print receipts, and find information. Be warm, concise, and helpful.`,
  skills: [retailSkill],
  deviceTriggers: [customerHelp],
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="CDN Uploads" icon="cloud-arrow-up" href="/devices/cdn-uploads">
    Upload screenshots and logs from kiosk devices
  </Card>

  <Card title="Smart Office" icon="building" href="/devices/examples/smart-office">
    Facilities management example
  </Card>

  <Card title="Warehouse Scanner" icon="warehouse" href="/devices/examples/warehouse-inventory">
    Logistics and inventory management
  </Card>

  <Card title="Agent Tools" icon="wrench" href="/devices/agent-tools">
    How device commands become agent tools
  </Card>
</CardGroup>
