Skip to content

Hendrickx Integration Setup

Looking for guided setup? Start Emily in the backoffice for step-by-step guided setup.

Purpose

The Hendrickx integration connects Upvendo to Hendrickx POS registers via the Kassanet protocol. It enables restaurants and hospitality businesses in Belgium to accept orders through Upvendo channels (Kiosk, Online Ordering, Table QR) and have them appear directly on the Hendrickx POS register for kitchen preparation and fulfillment. The integration also pulls the full product catalog from Hendrickx so merchants do not need to re-enter their menu in Upvendo.


Key Concepts

Kassanet Protocol

Hendrickx uses the Kassanet communication protocol, a proprietary API standard shared with Vanhoutte POS systems. Both providers are handled by the same backend service (AbstractKassanetService) and differ only in the provider identifier. All API calls follow this pattern:

  1. The request body is serialized to JSON
  2. If encryption is enabled, the JSON is encrypted with Blowfish CBC using the merchant's encryption token and a null IV (8 zero bytes), then base64-encoded
  3. An HMAC-SHA256 signature is computed over the ISO 8601 date string concatenated with the (possibly encrypted) body, using the merchant's signature key
  4. The request is sent with headers: Date, Signature, Content-Type, and a unique request-id

Location-Specific Scope

Each Hendrickx connection is tied to a single location. If a merchant has multiple locations each with its own Hendrickx register, each location must be connected separately with its own set of credentials (Connection URL, Signature Key, Encryption Token).

A location may have only one in-house POS at a time. Hendrickx, Vanhoutte, and ShopCaisse are mutually exclusive per location, and also conflict with the merchant-scoped in-house POS (Square, MplusKassa, Lightspeed). Adding a second in-house POS is rejected with "Another in-house channel is already enabled for this merchant." (Source: ThirdPartyIntegrationHelper::IN_HOUSE_CHANNELS, checkForInhouseChannel.)

One-Way Menu Sync (Hendrickx to Upvendo)

Menu items must exist in Hendrickx POS before connecting. Upvendo cannot create items in Hendrickx -- it only reads the existing catalog. The sync imports:

  • Categories and subcategories from Hendrickx product groups
  • Products with names, prices, and PLU (Price Look-Up) codes
  • Modifier groups and modifiers (product variants/selections)
  • Display groups that control how items are organized in the Upvendo menu
  • Table numbers from the POS for table management

Order Forwarding (Upvendo to Hendrickx)

After payment is confirmed, Upvendo sends orders to Hendrickx using the CreateOrder endpoint. The order includes the table number, item article IDs, quantities, modifiers, and optional pickup time and notes. After order creation, Upvendo retrieves the bill (GetBill) and then marks the bill as paid (PayBill) using the configured webshop payment type.

Table Number Management

Hendrickx supports two table assignment modes:

  • Non-physical tables -- Upvendo calls GetFreeTableNumber to get an available virtual table from the POS. A database lock prevents concurrent orders from claiming the same table.
  • Physical tables -- Upvendo allocates a table from a configured table section, checking each table's bill status to ensure it is free. Used table numbers are tracked per month to avoid collisions.

Both modes use distributed locking (MongoDB-based, 5-minute TTL) to prevent race conditions when multiple orders arrive simultaneously. (The precise allocation/lock retry internals are backend implementation detail and not exhaustively re-verified here.)

Stock Tracking

After menu sync, Upvendo can pull stock levels from Hendrickx using the GetCountdownPluState endpoint. Items with countdown PLUs have their inventory updated in Upvendo, enabling out-of-stock indicators on Kiosk and Online Ordering. A threshold parameter can be set to only return items below a certain stock level.

Available Kassanet API Endpoints

The following Kassanet endpoints are used by the Hendrickx integration:

EndpointMethodPurpose
testGETTest connection and retrieve system info (including tipping settings)
GetAllProductsGETFetch the complete product catalog (categories, products, modifiers)
GetProductSelectionGETFetch product selections / display groups
TableNumbersGETFetch all configured table numbers
GetFreeTableNumberGETGet the next available virtual table number
GetBill?tableNumber=NGETRetrieve the current bill for a table
GetPaymentTypesGETFetch available payment types on the POS
CreateOrderPOSTSend an order to the POS register
PayBillPOSTMark a bill as paid with a specified payment type
PostDepositPOSTPost a deposit payment to a table
GetCountdownPluStateGETFetch stock levels for countdown PLU items

Connection Status and Auto-Reconnection

The integration tracks connection status per location (stored in sync_status.connection_status). If an API call fails due to a timeout or server error, the location is marked as disconnected. The kassanet:check-and-requeue command periodically tests disconnected locations and restores them when the POS comes back online; a successful test also requeues orders that failed while the POS was offline. A manual Test Connection does the same. (Daily menu auto-sync is driven separately by the in-house:auto-sync command.)


Actions

Connect Hendrickx

Route: /hendrickx

  1. Complete all prerequisites (see checklist below)
  2. Select the target location from the topbar
  3. Click Add Channel and select Hendrickx (HendrickxChannelDialog)
  4. Enter credentials:
    • Connection URL (base_url) -- The Hendrickx Kassanet API base URL (provided by Hendrickx)
    • Signature Key (signature_key) -- The HMAC signing key for API authentication
    • Encryption Token (encryption_token) -- The Blowfish encryption key for payload encryption
  5. Save the credentials -- the first menu import starts automatically; you do not have to click Sync Now for it. Use Sync Now for later manual re-imports.
  6. Verify sync results in the Hendrickx Logs

Reconnecting a Hendrickx integration on a location where one was previously removed restores the stored per-table usage history (settings.tables_usage) from the old record.

Sync Menu

Route: /hendrickx

Click Sync Now to manually trigger a full menu import. The sync runs as a background job chain:

  1. ImportKassanetMenuJob -- kicks off the import
  2. ImportKassanetCategoriesJob -- fetches GetAllProducts, processes categories and subcategories
  3. ImportKassanetProductsJob -- processes products and modifiers from the same data
  4. ImportKassanetDisplayGroupsJob -- processes display groups, syncs table numbers, merges modifier groups, syncs stock levels, and cleans up placeholder items

Each job retries up to 3 times with a 30-second backoff. Per-job timeouts are 300 seconds (the products job allows 600 seconds).

Configure Auto Sync

Set a daily sync time (sync_time, e.g., 05:00; minutes must be 00 or 30) to automatically re-import menu changes from Hendrickx once per day. The time is the local time at the location and is stored as that local time, so it does not shift when the clocks change. A caption under the field names the timezone the sync will run in. Clear the field to disable auto-sync. The daily run is dispatched by the in-house:auto-sync command.

There is no separate auto-sync endpoint: the sync time is submitted with the same connect/settings save -- POST /api/back-office/kassanet/hendrickx/{locationId} carries sync_time alongside base_url, signature_key and encryption_token, and re-submitting updates the record in place. Validation is HH:MM with minutes 00 or 30; the value is stored verbatim as settings.sync_time (a local wall-clock, not converted to UTC) together with the resolved IANA zone in settings.sync_timezone, and settings.next_run_time is cleared so the scheduler recalculates the absolute instant. The zone defaults to the integration's own location timezone on a first write and falls back to Europe/Brussels; an optional sync_timezone key is accepted but the back-office does not send it, and omitting it preserves whatever zone is already stored. Sending sync_time: null disables auto-sync.

Test Connection

Click Test Connection to verify the Kassanet API is reachable. The test calls the /test endpoint, checks the IsSuccessStatusCode response field, and updates the connection status. If the POS was previously disconnected, a successful test automatically requeues any failed orders.

Delete / Disconnect (Disabled)

Disconnecting a Hendrickx integration through the API is currently disabled. DELETE /api/back-office/kassanet/hendrickx/{locationId} unconditionally returns HTTP 400 with "Disconnecting Hendrickx integration is disabled. Please contact administrator." The back office still shows a Delete Integration button and its confirmation dialog, but clicking it just produces that error -- the underlying cleanup routine is not reachable. To remove or change a Hendrickx connection, contact an administrator.

View Logs

Route: /hendrickx

The Hendrickx Logs panel shows the most recent sync and order events with three columns:

  • Timestamp -- when the event occurred
  • Status -- the entry type: info, success, warning, or error
  • Message -- a description of what happened (e.g., "Successfully synced 42 products", "Failed to create order #1234")

The panel also supports free-text search, filtering by entry type (info / success / warning / error) and by date, plus a refresh button. Logs can be exported (CSV, JSON, or Text) from the panel. Logs are capped at 1000 entries per location (ThirdPartyIntegration::SYNC_LOGS_LIMIT), newest first. Duplicate consecutive entries (same type and normalized message, where numbers are normalized to #) are collapsed: the timestamp is updated and an occurrences counter is incremented rather than adding a new row.


Business Rules

  • Menu items must exist in Hendrickx POS before they can appear in Upvendo. There is no push from Upvendo to Hendrickx for menu data.
  • Orders are only forwarded to Hendrickx after payment is confirmed via Viva Wallet (Kiosk) or Stripe (Online Ordering).
  • If the Hendrickx register is offline when an order is placed, the order is flagged with need_requeue = true. When the POS comes back online (detected via the periodic kassanet:check-and-requeue command or a manual test), flagged orders are automatically requeued.
  • Each API request includes a unique request-id header for traceability.
  • API requests use a 15-second total timeout and a 10-second connection timeout. The main request path does not retry by default (maxRetries = 1, and no caller raises it): a failure -- retryable or not -- marks the location disconnected and throws immediately. Errors classified as retryable are timeouts, connection failures, and HTTP 408/500/502/503/504. The only path that actually retries is the PayBill / PostDeposit "bill locked" handling, up to 3 attempts at 250ms / 750ms / 1500ms.
  • Table number locks (MongoDB-based) expire after 5 minutes (300s) to prevent permanent table lockout if a process fails.
  • The integration logs up to 1000 recent entries per location (SYNC_LOGS_LIMIT), newest first. Duplicate consecutive entries are collapsed (timestamp updated, occurrences incremented), with numbers in the message normalized to # for matching.
  • The in-house:auto-sync artisan command runs on a schedule to trigger automatic daily syncs for all Hendrickx integrations that have a sync_time configured.
  • If a bill total from Hendrickx does not match the Upvendo transaction total (but is still non-zero), the mismatch is logged as an error and recorded on the transaction (bill_total_mismatch), and the order still proceeds to PayBill. However, if the bill total is €0 while the transaction has items (table was cleared on the POS), the order is marked unrecoverable and does not proceed. (When a prior CreateOrder attempt is being retried and the POS bill is non-zero but inconsistent, the retry refuses to re-post and marks the order unrecoverable so staff can intervene.)
  • When encryption is enabled on the integration, the URL path includes a SEC1 segment between the base URL and the endpoint name. This signals the Kassanet server to expect encrypted payloads.
  • The sync cleans up placeholder items after import. Items with a kitchen name of "zz" or matching the pattern * pager * or items where the kitchen name equals a numeric product name are automatically removed.
  • After a successful import, the guided setup progress is automatically updated for the merchant.

Prerequisites Checklist

Before integrating with Hendrickx, complete these steps in order:

Only the branding profile (and having no other in-house POS on the location) is enforced when you connect. Payment and billing profiles are optional at connect time and can be set up later -- but you need a payment provider before you can take a paid order.

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

3. Branding Profile Setup (Required)

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

4. Location Creation (Required)

  1. Go to Settings -> Locations (/settings/locations)
  2. Create your location with:
    • Business name and address
    • Business hours
    • Contact information
  3. Select this location from the topbar before connecting Hendrickx

Order Flow

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

Three-Step POS Flow

text
1. CreateOrder    ->  Sends items + table number to Hendrickx POS
         |
2. GetBill        ->  Retrieves the bill total for the table
         |
3. PayBill        ->  Marks the bill as paid with the webshop payment type

Kiosk Flow

text
Customer submits order on Kiosk
            |
Viva Wallet terminal prompts for payment
            |
Customer pays the prompted amount
            |
Upvendo receives successful payment webhook
            |
ProcessOrderJob dispatches CreateOrder to Hendrickx
            |
GetKassanetBillJob retrieves the bill
            |
PayKassanetBillJob marks the bill as paid

Online Ordering Flow

text
Customer submits order online
            |
Payment processed (Bancontact redirect or card details via Stripe)
            |
Customer pays the prompted amount
            |
Upvendo receives successful payment webhook
            |
ProcessOrderJob dispatches CreateOrder to Hendrickx
            |
GetKassanetBillJob retrieves the bill
            |
PayKassanetBillJob marks the bill as paid

Order Payload Structure

Each order sent to Hendrickx includes:

  • TableNumber -- the assigned table (physical or virtual)
  • OperatorId -- a code (901-905) derived from the order source and dining option (e.g. 904 = Kiosk dine-in, 905 = QR ordering dine-in)
  • TableText -- order metadata (channel, order number, pager ID if any, customer name/phone/email)
  • Items[] -- each item has ArticleId (the Kassanet PLU), Text, Quantity, UnitPrice, optional InfoText (notes), and nested Variants[] for modifiers
  • PickupTime -- for non-dine-in orders, formatted HH:mm in the location's timezone

(Exact field-level serialization and KDS marker PLUs are backend internals and not re-verified in full here.)


FAQs

Q: Can I create menu items in Upvendo and push them to Hendrickx? A: No. The Hendrickx integration is one-way for menu data. Items must be created in the Hendrickx POS first, then synced to Upvendo via the Sync Now button or auto-sync schedule.

Q: What happens if the Hendrickx POS is turned off when an order comes in? A: The order is saved with a need_requeue flag. A background command (kassanet:check-and-requeue) periodically checks disconnected locations and automatically requeues failed orders when the POS comes back online. You can also manually click Test Connection to trigger a requeue.

Q: Can I disconnect a Hendrickx integration myself? A: No. The disconnect/delete endpoint is intentionally disabled and always returns an error asking you to contact an administrator, even though the Delete Integration button is still shown in the back office.

Q: Can I connect multiple Hendrickx registers to one Upvendo location? A: No. Each location supports one in-house POS, and only one Hendrickx integration. Hendrickx, Vanhoutte, and ShopCaisse are mutually exclusive per location (and conflict with the merchant-scoped Square/MplusKassa/Lightspeed). If you have multiple registers, they should share the same Kassanet connection endpoint.

Q: Does the integration support tipping? A: Yes. Whether tipping is enabled is driven by the Hendrickx system: if the test response reports SystemInfo.tipCalculatorActivated = "true", tipping is enabled in Upvendo, and the tip amount is included as Tip within the PayBill payment. (This is controlled by the POS, not by a separate tipping toggle in the Hendrickx settings UI.)

Q: How often does auto-sync run? A: Auto-sync runs once per day at the configured sync time. The time you set in the UI is your location's local time and is stored as such, so it keeps firing at the same local time across daylight-saving changes. If no sync time is set, auto-sync is disabled.

Q: What encryption does the integration use? A: The integration uses Blowfish CBC encryption with an 8-byte null IV. The encryption token provided by Hendrickx serves as the Blowfish key. If encryption is enabled on the integration, all request bodies are encrypted before sending and all responses are decrypted after receiving.

Q: Can I use Hendrickx with both Kiosk and Online Ordering at the same location? A: Yes. Both channels send orders through the same Hendrickx integration. Kiosk orders use Viva Wallet for payment and Online Ordering uses Stripe.

Q: What is a PLU code? A: PLU stands for Price Look-Up. It is a unique identifier assigned to each product in the Hendrickx POS. Upvendo uses PLU codes (stored as ArticleId) to reference items when creating orders and tracking stock levels.

Q: Does the integration support order notes or special instructions? A: Yes. Per-item notes are sent as InfoText in the order payload. These appear on the kitchen display or receipt in the Hendrickx POS.


Troubleshooting

Orders not syncing to Hendrickx

  • Verify the Hendrickx register is powered on and connected to the internet
  • Go to /hendrickx and click Test Connection to check the current status
  • Check Hendrickx Logs for specific error messages
  • If the connection status shows "disconnected", the POS may be offline. Orders will automatically requeue when the connection is restored

"Connection test failed" error

  • Double-check the Connection URL -- it should be the full Kassanet API base URL without a trailing slash
  • Verify the Signature Key is correct -- an incorrect key produces HMAC signature mismatches
  • Verify the Encryption Token is correct -- an incorrect token causes Blowfish decryption error or Invalid JSON response: possibly incorrect encryption token
  • Ensure the Hendrickx register's firewall allows incoming connections from Upvendo's servers

"No free table numbers available" error

  • This means all virtual or physical tables are occupied or locked
  • Check if there are stale table locks (they auto-expire after 5 minutes)
  • For physical tables, verify the table section is properly configured and has enough tables
  • Check if the table number range in the Hendrickx POS matches the Upvendo configuration
  • Verify the Hendrickx POS has products configured
  • Check if the API returns data by clicking Test Connection first
  • Review the Hendrickx Logs for errors during the ImportKassanetCategoriesJob or ImportKassanetProductsJob steps
  • If the sync was interrupted, try clicking Sync Now again -- each job retries up to 3 times

Bill total mismatch warning in logs

  • This occurs when the bill total from Hendrickx does not match the Upvendo transaction total
  • Common causes: price differences between Upvendo and Hendrickx (menu not recently synced), rounding differences, or modifier pricing discrepancies
  • Run a menu sync to ensure prices are up to date
  • For a non-zero mismatch the order still proceeds to payment (and is flagged bill_total_mismatch on the transaction), but the warning should be investigated
  • If the bill comes back €0 while the order has items, the table was likely cleared on the POS — the order is marked unrecoverable and needs manual handling

Orders stuck in "error" status

  • Check the external_data.hendrickx.errMessage field on the transaction for the specific error
  • Common causes: POS was offline during order creation, invalid article IDs (item removed from POS but still in Upvendo menu), or table assignment failure
  • If the POS is now online, click Test Connection to trigger automatic requeue of failed orders
  • Blowfish decryption error -- the encryption token is likely incorrect. Re-enter the correct token from your Hendrickx provider
  • Invalid base64 input -- the response from Hendrickx was not properly encrypted. Check if encryption is enabled on both sides
  • If you recently changed the encryption token in Hendrickx, update it in Upvendo as well