Skip to content

Hendrickx Onboarding

Overview

Hendrickx is a location-specific in-house POS integration commonly used in Belgium, built on the shared Kassanet protocol (the same engine that powers Vanhoutte). Each location must be connected separately with its own credentials. Hendrickx does NOT process the customer's payment - Upvendo collects the money (Viva Wallet for Kiosk, Stripe for Online Ordering / QR) and Kassanet only records the payment tender on the POS bill.

Important: The only profile enforced at connect time is the Branding Profile. Payment and Billing profiles are optional and can be set up later (ThirdPartyIntegrationHelper::checkRequiredProfiles only requires a branding profile for Hendrickx). You must also create a location and select it from the backoffice topbar before connecting. The guided-setup checklist still lists Payment and Billing as steps, but they are not blocking prerequisites for the connect itself.


Integration Type

  • Scope: Location-specific (each location connects separately, with its own base_url / signature_key / encryption_token)
  • Family: Kassanet (Hendrickx and Vanhoutte share AbstractKassanetService; HendrickxService only sets the provider name hendrickx)
  • Payment Processing: Not included. Upvendo collects payment via Viva Wallet (Kiosk) or Stripe (Online Ordering / QR); Kassanet records the tender on the POS bill via PayBill
  • In-House Exclusivity: A location may have only one in-house POS. Hendrickx, Vanhoutte, and ShopCaisse are mutually exclusive per location, and conflict with the merchant-scoped Square / MplusKassa / Lightspeed
  • Menu Sync: One-way pull from Hendrickx (menu items must exist in the POS first; Upvendo imports the catalog)
  • Order Sync: One-way push to Hendrickx (orders sent after payment is confirmed)

Discovery Flow (JSON - for deterministic parsing)

discovery_questions
[
  {
    "id": "channels",
    "text": "What channels do you want to set up with Hendrickx?",
    "text_i18n": {
      "nl": "Welke kanalen wil je instellen met Hendrickx?",
      "fr": "Quels canaux souhaitez-vous configurer avec Hendrickx ?",
      "de": "Welche Kanäle möchten Sie mit Hendrickx einrichten?",
      "es": "¿Qué canales quieres configurar con Hendrickx?",
      "it": "Quali canali vuoi configurare con Hendrickx?",
      "pt": "Que canais quer configurar com o Hendrickx?"
    },
    "options": [
      "Kiosk only",
      "Online ordering only",
      "Both"
    ],
    "labels": {
      "nl": {
        "Kiosk only": "Alleen kiosk",
        "Online ordering only": "Alleen online bestellen",
        "Both": "Beide"
      },
      "fr": {
        "Kiosk only": "Kiosque uniquement",
        "Online ordering only": "Commande en ligne uniquement",
        "Both": "Les deux"
      },
      "de": {
        "Kiosk only": "Nur Kiosk",
        "Online ordering only": "Nur Online-Bestellung",
        "Both": "Beides"
      },
      "es": {
        "Kiosk only": "Solo quiosco",
        "Online ordering only": "Solo pedidos en línea",
        "Both": "Ambos"
      },
      "it": {
        "Kiosk only": "Solo chiosco",
        "Online ordering only": "Solo ordini online",
        "Both": "Entrambi"
      },
      "pt": {
        "Kiosk only": "Apenas quiosque",
        "Online ordering only": "Apenas pedidos online",
        "Both": "Ambos"
      }
    }
  },
  {
    "id": "menu_ready",
    "text": "Do you already have your menu set up in Hendrickx?",
    "text_i18n": {
      "nl": "Heb je je menu al ingesteld in Hendrickx?",
      "fr": "Votre menu est-il déjà configuré dans Hendrickx ?",
      "de": "Ist Ihr Menü bereits in Hendrickx eingerichtet?",
      "es": "¿Ya tienes tu menú configurado en Hendrickx?",
      "it": "Hai già configurato il tuo menu in Hendrickx?",
      "pt": "Já tem o seu menu configurado no Hendrickx?"
    },
    "options": [
      "Yes, my menu is in Hendrickx",
      "No, I need to create it"
    ],
    "labels": {
      "nl": {
        "Yes, my menu is in Hendrickx": "Ja, mijn menu staat in Hendrickx",
        "No, I need to create it": "Nee, ik moet het nog aanmaken"
      },
      "fr": {
        "Yes, my menu is in Hendrickx": "Oui, mon menu est dans Hendrickx",
        "No, I need to create it": "Non, je dois le créer"
      },
      "de": {
        "Yes, my menu is in Hendrickx": "Ja, mein Menü ist in Hendrickx",
        "No, I need to create it": "Nein, ich muss es erst erstellen"
      },
      "es": {
        "Yes, my menu is in Hendrickx": "Sí, mi menú está en Hendrickx",
        "No, I need to create it": "No, necesito crearlo"
      },
      "it": {
        "Yes, my menu is in Hendrickx": "Sì, il mio menu è in Hendrickx",
        "No, I need to create it": "No, devo crearlo"
      },
      "pt": {
        "Yes, my menu is in Hendrickx": "Sim, o meu menu está no Hendrickx",
        "No, I need to create it": "Não, preciso de o criar"
      }
    },
    "abort_if": "No, I need to create it",
    "abort_message": "Hendrickx requires your menu to be set up in the POS first. Please create your menu items in Hendrickx, then come back to continue the setup.",
    "abort_message_i18n": {
      "nl": "Hendrickx vereist dat je menu eerst in de kassa staat. Maak je menu-items aan in Hendrickx en kom daarna terug om de installatie te voltooien.",
      "fr": "Hendrickx nécessite que votre menu soit d'abord configuré dans le système de caisse. Créez vos articles dans Hendrickx, puis revenez pour poursuivre la configuration.",
      "de": "Hendrickx erfordert, dass Ihr Menü zuerst im Kassensystem eingerichtet ist. Legen Sie Ihre Menüartikel in Hendrickx an und kehren Sie dann zurück, um die Einrichtung fortzusetzen.",
      "es": "Hendrickx requiere que tu menú esté configurado primero en el TPV. Crea tus artículos en Hendrickx y luego vuelve para continuar la configuración.",
      "it": "Hendrickx richiede che il menu sia prima configurato nel POS. Crea i tuoi articoli in Hendrickx e poi torna per continuare la configurazione.",
      "pt": "O Hendrickx exige que o seu menu esteja primeiro configurado no POS. Crie os seus artigos no Hendrickx e depois volte para continuar a configuração."
    }
  }
]
setup_steps
{
  "Kiosk only": [
    {"id": "branding_profile", "title": "Set up Store Branding", "route": "/settings/brand"},
    {"id": "create_location", "title": "Create Location", "route": "/settings/locations"},
    {"id": "select_location", "title": "Select Location from Topbar", "action": "open_location_selector"},
    {"id": "connect_pos", "title": "Connect Hendrickx", "route": "/hendrickx", "channel": "hendrickx"},
    {"id": "sync_menu", "title": "Sync Menu", "route": "/hendrickx"},
    {"id": "menu_for_kiosk", "title": "Build Menu for Kiosk", "route": "/menus/menu-builder"},
    {"id": "in_house_settings", "title": "Set up In-House Settings", "route": "/in-house/settings"},
    {"id": "device_profile", "title": "Create Device Profile", "route": "/device-management/profiles"},
    {"id": "kiosk_device", "title": "Set up Kiosk & Activate Payment", "route": "/device-management/devices"},
    {"id": "payment_profile", "title": "Set up Payment Profile", "route": "/settings/payments", "optional_for_go_live": true},
    {"id": "billing_profile", "title": "Set up Billing Profile", "route": "/settings/billing", "optional_for_go_live": true},
    {"id": "test_kiosk", "title": "Activate Kiosk & Test Order", "optional_for_go_live": true}
  ],
  "Online ordering only": [
    {"id": "branding_profile", "title": "Set up Store Branding", "route": "/settings/brand"},
    {"id": "create_location", "title": "Create Location", "route": "/settings/locations"},
    {"id": "select_location", "title": "Select Location from Topbar", "action": "open_location_selector"},
    {"id": "connect_pos", "title": "Connect Hendrickx", "route": "/hendrickx", "channel": "hendrickx"},
    {"id": "sync_menu", "title": "Sync Menu", "route": "/hendrickx"},
    {"id": "menu_for_online_ordering", "title": "Build Menu for Online Ordering", "route": "/menus/menu-builder"},
    {"id": "online_settings", "title": "Set up Online Settings", "route": "/online-settings"},
    {"id": "configure_online_ordering", "title": "Set up Online Ordering", "route": "/online/online-ordering"},
    {"id": "payment_profile", "title": "Set up Payment Profile", "route": "/settings/payments", "optional_for_go_live": true},
    {"id": "billing_profile", "title": "Set up Billing Profile", "route": "/settings/billing", "optional_for_go_live": true},
    {"id": "test_online_ordering", "title": "Open Online Ordering URL & Test Order", "route": "/online/online-ordering", "optional_for_go_live": true}
  ],
  "Both": [
    {"id": "branding_profile", "title": "Set up Store Branding", "route": "/settings/brand"},
    {"id": "create_location", "title": "Create Location", "route": "/settings/locations"},
    {"id": "select_location", "title": "Select Location from Topbar", "action": "open_location_selector"},
    {"id": "connect_pos", "title": "Connect Hendrickx", "route": "/hendrickx", "channel": "hendrickx"},
    {"id": "sync_menu", "title": "Sync Menu", "route": "/hendrickx"},
    {"id": "menu_for_kiosk", "title": "Build Menu for Kiosk", "route": "/menus/menu-builder"},
    {"id": "menu_for_online_ordering", "title": "Build Menu for Online Ordering", "route": "/menus/menu-builder"},
    {"id": "in_house_settings", "title": "Set up In-House Settings", "route": "/in-house/settings"},
    {"id": "device_profile", "title": "Create Device Profile", "route": "/device-management/profiles"},
    {"id": "kiosk_device", "title": "Set up Kiosk & Activate Payment", "route": "/device-management/devices"},
    {"id": "online_settings", "title": "Set up Online Settings", "route": "/online-settings"},
    {"id": "configure_online_ordering", "title": "Set up Online Ordering", "route": "/online/online-ordering"},
    {"id": "payment_profile", "title": "Set up Payment Profile", "route": "/settings/payments", "optional_for_go_live": true},
    {"id": "billing_profile", "title": "Set up Billing Profile", "route": "/settings/billing", "optional_for_go_live": true},
    {"id": "test_kiosk", "title": "Activate Kiosk & Test Order", "optional_for_go_live": true},
    {"id": "test_online_ordering", "title": "Open Online Ordering URL & Test Order", "route": "/online/online-ordering", "optional_for_go_live": true}
  ]
}

The step list above uses the canonical step ids the back office derives from the /api/back-office/setup-status flags (src/utils/emily/setupStepIds.ts). The "Select Location from Topbar" step uses the action literal open_location_selector (the value topbar_location_select only appears as an inbound alias from Emily's API and is normalized to it). Payment and Billing carry optional_for_go_live — they are labelled as go-live prerequisites but gate nothing; step gating is by declared prerequisites, not list order. The BASE_SETUP_STEPS constants in components/dialogs/guided-setup/types.ts are legacy and are not what a merchant sees. See Guided Setup.


Connect Hendrickx

Route: /hendrickx (backend API: POST /api/back-office/kassanet/hendrickx/{locationId}KassanetIntegrationController::initiate)

  1. Configure a Branding Profile (/settings/brand) - the only profile enforced at connect time. Payment (/settings/payments) and Billing (/settings/billing) are optional and can be added later.
  2. Create a location (/settings/locations) and select it from the topbar.
  3. On /hendrickx, open the connect form and enter the three Kassanet credentials:
    • Connection URL (base_url) - the Kassanet API endpoint URL (validated as a URL)
    • Signature Key (signature_key) - the HMAC-SHA256 signing key
    • Encryption Token (encryption_token) - the Blowfish encryption key
  4. Optionally set a Daily Sync Time (sync_time) for automatic menu sync. The value must be HH:00 or HH:30 (minutes restricted to 00 or 30). The time you type is the local time at the location and it stays at that local time all year - it does not move when the clocks change for daylight saving. A caption under the field names the timezone the sync will run in ("Runs in the ... timezone"). Leave it blank to disable auto-sync.
  5. Saving the form auto-triggers a menu sync immediately after connecting. You can also click Sync Now to re-run the import.

The connect form takes exactly the three credentials plus sync_time. enable_encryption is not a form field - it defaults to true in code (KassanetIntegration::getEnableEncryption()), so payloads are encrypted by default. The provider value (hendrickx) is injected from the route, not entered by the merchant.


Prerequisites Checklist

Complete these before (or alongside) connecting Hendrickx:

1. Branding Profile Setup (Required)

  1. Go to Settings -> Brand (/settings/brand)
  2. Configure your store's branding (logo, colors, etc.)

This is the only prerequisite the backend enforces at connect time. Connecting without a branding profile is rejected with HTTP 400: "Missing required profiles: branding profile. Please create them before setting up an integration."

2. Payment Profile Setup (Optional)

  1. Go to Settings -> Payments (/settings/payments)
  2. Set up your payment providers:
    • Viva Wallet for Kiosk payments (terminal-based)
    • Stripe for Online Ordering / QR payments
  3. Configure payment methods (Card, Bancontact, etc.)

Payment is not blocked at connect time, but you will need a configured provider before you can take a real order on a channel.

3. Billing Profile Setup (Optional)

  1. Go to Settings -> Billing (/settings/billing)
  2. Complete your billing information

4. Location Creation (Required to connect)

  1. Go to Settings -> Locations (/settings/locations)
  2. Create your location with business name/address, hours, and contact info
  3. Select this location from the topbar before connecting Hendrickx (each Hendrickx connection is tied to a single selected location)

In-House Exclusivity

A location can have only one in-house POS at a time. When you try to add a second in-house channel, the connect is rejected with HTTP 400: "Another in-house channel is already enabled for this merchant."

The check uses two channel lists (ThirdPartyIntegrationHelper):

  • Location-scoped in-house channels (IN_HOUSE_CHANNELS): Hendrickx, Vanhoutte, ShopCaisse. Connecting Hendrickx is blocked if another of these is already connected on the same location.
  • Merchant-scoped in-house channels (IN_HOUSE_MERCHANT_SCOPED_CHANNELS): Square, MplusKassa, Lightspeed. Connecting Hendrickx is blocked if any of these is connected anywhere for the merchant.

So Hendrickx conflicts per-location with Vanhoutte/ShopCaisse, and per-merchant with Square/MplusKassa/Lightspeed. (See In-House Settings.)


Catalog & Order Flow

Menu items must exist in the Hendrickx POS first; Upvendo only reads the catalog. Clicking Sync Now (or connecting, or the daily auto-sync) dispatches a chained import: ImportKassanetMenuJob -> ImportKassanetCategoriesJob -> ImportKassanetProductsJob -> ImportKassanetDisplayGroupsJob, importing categories/subcategories, products (with prices and PLU codes), modifier groups/modifiers, display groups, table numbers, and stock levels. The synced menu appears under Menus.

Order Flow (Upvendo -> Hendrickx, one-way, three stages)

Orders are created in Upvendo (Kiosk, Online Ordering, or Table QR) and pushed to Hendrickx after payment is confirmed, in three stages:

text
1. CreateOrder  ->  Registers items + table number on the POS guest check
        |
2. GetBill      ->  Reads the POS bill total for the table
        |
3. PayBill      ->  Records the payment tender on the bill (Viva Wallet for Kiosk, Stripe for Online/QR, falling back to the Webshop type)

The create stage runs via ProcessOrderJob; the bill and pay stages run as GetKassanetBillJob and PayKassanetBillJob. If the POS is offline, the order is flagged need_requeue and reprocessed when connectivity is restored. (POS-side preparation, fulfillment, and the storefront checkout flow are not verified here.)

For full protocol, endpoint, and order-stage details, see the Hendrickx Integration and Kassanet Integration docs.


Guided-Setup Steps

The back-office guided-setup wizard (Emily) drives onboarding. The fallback step order for Hendrickx is:

  1. Set up Store Branding - /settings/brand
  2. Create Location - /settings/locations
  3. Select Location from Topbar - action open_location_selector
  4. Connect Hendrickx - /hendrickx (channel hendrickx)
  5. Sync Menu - /hendrickx
  6. Build Menu for Kiosk - /menus/menu-builder
  7. Build Menu for Online Ordering - /menus/menu-builder
  8. Set up In-House Settings - /in-house/settings
  9. Create Device Profile - /device-management/profiles
  10. Set up Kiosk & Activate Payment - /device-management/devices
  11. Set up Online Settings - /online-settings
  12. Set up Online Ordering - /online/online-ordering
  13. Set up Payment Profile - /settings/payments
  14. Set up Billing Profile - /settings/billing
  15. Activate Kiosk & Test Order
  16. Open Online Ordering URL & Test Order - /online/online-ordering

Once the base steps are complete, the wizard offers channel checklists:

  • Kiosk: Build Menu for Kiosk (/menus/menu-builder) -> Set up In-House Settings (/in-house/settings) -> Create Device Profile (/device-management/profiles) -> Set up Kiosk & Activate Payment (/device-management/devices) -> Set up Payment Profile (/settings/payments) -> Set up Billing Profile (/settings/billing) -> Activate Kiosk & Test Order
  • Online Ordering: Build Menu for Online Ordering (/menus/menu-builder) -> Set up Online Settings (/online-settings) -> Set up Online Ordering (/online/online-ordering) -> Set up Payment Profile (/settings/payments) -> Set up Billing Profile (/settings/billing) -> Open Online Ordering URL & Test Order (/online/online-ordering)

Step completion is auto-detected from the merchant's real data via GET /api/back-office/setup-status; merchants generally do not mark steps manually. The "Connect POS" step only completes when the integration is both connected and tested. See Guided Setup.