PaymentsIn-Person PaymentsTerminal management

Terminal management

Activate, assign, configure, and remotely manage your VINR terminal fleet.

View as MarkdownInstall skills

Provision and maintain any number of terminals from the VINR Dashboard or the Management API. Whether you operate a single countertop device or hundreds of handhelds across multiple sites, every terminal in your fleet shares the same lifecycle: activate once, assign to a location, push configuration, and monitor health in real time. No on-site IT visit is required after the initial power-on.

Activate a terminalAsk

Every VINR terminal arrives factory-reset and ready to pair. Activation registers the device under your account and downloads your location configuration.

Power on the terminal

Press and hold the power button until the VINR setup screen appears. All five models — Nexgo N92, Nexgo N86Pro, Nexgo CT20, Nexgo CT20P, and Ciontek CM30 — display an activation QR code and a six-character alphanumeric activation code on first boot.

Record the activation code

You can activate by scanning the QR code with the VINR mobile app (Dashboard → Hardware → Scan) or by copying the activation code for use with the API.

Add the terminal in the Dashboard or via SDK

In the Dashboard go to Hardware → Terminals → Add terminal and enter the activation code. Alternatively, activate programmatically:

import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const terminal = await vinr.terminal.terminals.activate({
  activationCode: 'ABC123',
  label: 'Counter 1 — Main Street',
  locationId: 'loc_01HZ9RSTORE',
});

console.log(terminal.id);
console.log(terminal.status);
curl -X POST https://api.vinr.com/v1/terminal/terminals/activate \
  -H "X-Api-Key: $VINR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "activationCode": "ABC123",
    "label": "Counter 1 — Main Street",
    "locationId": "loc_01HZ9RSTORE"
  }'

Terminal downloads its configuration

Once activated the device connects to VINR, downloads its location profile (currency, receipt template, feature flags, idle screen), and moves to online status. This takes under thirty seconds on a typical connection.

An activation code is single-use and expires after 24 hours. If the code expires before you complete activation, reboot the terminal to generate a fresh code.

Assign to a locationAsk

A location is the unit of configuration in VINR. Assigning a terminal to a location automatically inherits that location's currency, tax rules, receipt settings, and feature profiles. Move a terminal to a different location at any time — the device re-downloads configuration within seconds.

import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const updated = await vinr.terminal.terminals.update({
  id: 'term_01HZ5QXYZ',
  locationId: 'loc_01HZ9RNORTH',
});

console.log(updated.locationId);
curl -X PATCH https://api.vinr.com/v1/terminal/terminals/term_01HZ5QXYZ \
  -H "X-Api-Key: $VINR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "locationId": "loc_01HZ9RNORTH" }'

Reassigning a terminal clears any per-terminal configuration overrides that differ from the new location profile. Review the destination location's settings before moving a device.

Configure a terminalAsk

Configuration can be set at the location level (inherited by all terminals in that location) or overridden per terminal. Per-terminal overrides take precedence. Configurable properties include:

  • Feature flags — tipping, cashback, DCC, offline authorization, contactless limit
  • Receipt settings — header logo, footer text, email/SMS receipt prompt
  • Idle screen — custom image or promotional message displayed between transactions
  • Tipping — preset percentages, fixed amounts, or free-entry; prompt placement (pre- or post-authorization)
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

await vinr.terminal.terminals.configure({
  id: 'term_01HZ5QXYZ',
  tipping: {
    enabled: true,
    presets: [10, 15, 20],
    allowCustom: true,
  },
  receipt: {
    footerText: 'Thank you for visiting VINR Demo Store!',
    promptDigitalReceipt: true,
  },
  idleScreen: {
    imageUrl: 'https://assets.example.com/idle-banner.png',
  },
});
curl -X POST https://api.vinr.com/v1/terminal/terminals/term_01HZ5QXYZ/configure \
  -H "X-Api-Key: $VINR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tipping": { "enabled": true, "presets": [10, 15, 20], "allowCustom": true },
    "receipt": { "footerText": "Thank you!", "promptDigitalReceipt": true },
    "idleScreen": { "imageUrl": "https://assets.example.com/idle-banner.png" }
  }'

Remote actionsAsk

Trigger administrative actions on any terminal without physical access. Actions are queued and delivered when the device is next online.

ActionEffect
rebootGracefully restarts the terminal OS. Pending transactions are not affected.
updateDownloads and installs the latest approved software version for the device model.
lockPrevents new transactions until the terminal is explicitly unlocked. Use when a device is reported missing.
unlockRe-enables transaction processing after a lock.
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

await vinr.terminal.terminals.action({
  id: 'term_01HZ5QXYZ',
  action: 'reboot',
});

await vinr.terminal.terminals.action({
  id: 'term_01HZ5QXYZ',
  action: 'lock',
});
curl -X POST https://api.vinr.com/v1/terminal/terminals/term_01HZ5QXYZ/actions \
  -H "X-Api-Key: $VINR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "action": "lock" }'

Locking a terminal immediately blocks new payment sessions. Any session already in progress on the device will complete normally. Notify affected locations before locking devices during business hours.

Monitor terminal healthAsk

Every terminal reports its status continuously. Use the Dashboard Hardware → Health tab for a visual fleet overview, or query the API to integrate terminal health into your own operations tooling.

Terminal statuses

StatusMeaning
onlineDevice is reachable and ready to accept payments.
offlineDevice has not checked in within the expected heartbeat window.
needs_attentionDevice requires action — low battery, pending required update, or connectivity degradation.
lockedDevice is administratively locked; no transactions accepted.
deactivatedDevice has been permanently removed from the fleet.
import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const { data: terminals } = await vinr.terminal.terminals.list({
  locationId: 'loc_01HZ9RSTORE',
  status: 'needs_attention',
});

for (const terminal of terminals) {
  console.log(terminal.id, terminal.model, terminal.status, terminal.lastSeen);
}
curl "https://api.vinr.com/v1/terminal/terminals?locationId=loc_01HZ9RSTORE&status=needs_attention" \
  -H "X-Api-Key: $VINR_SECRET_KEY"

Bulk operationsAsk

For fleets with many terminals, use bulk operations to import devices and push configuration changes in a single call.

CSV import

Download the bulk activation template from Hardware → Terminals → Import. Each row specifies an activation code, a label, and an optional location ID. Upload the completed CSV to register up to 500 terminals at once.

Bulk configuration push

Push a shared configuration to every terminal in a location without iterating over individual devices:

import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

await vinr.terminal.locations.bulkConfigure({
  locationId: 'loc_01HZ9RSTORE',
  tipping: {
    enabled: true,
    presets: [15, 18, 20],
  },
  receipt: {
    footerText: 'Come back soon!',
  },
});
curl -X POST https://api.vinr.com/v1/terminal/locations/loc_01HZ9RSTORE/bulk-configure \
  -H "X-Api-Key: $VINR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tipping": { "enabled": true, "presets": [15, 18, 20] },
    "receipt": { "footerText": "Come back soon!" }
  }'

Bulk configuration pushes are applied as location-level defaults. Any existing per-terminal overrides remain in place unless you also call vinr.terminal.terminals.configure with clearOverrides: true on the individual devices.

Automate terminal managementAsk

For large fleets, use the Management API to automate provisioning, configuration, and monitoring. The patterns below are common starting points.

Provision terminals from a CI/CD pipeline

If terminals arrive in batches — for example, before a seasonal store opening — automate activation from your deployment pipeline rather than the Dashboard:

import { Vinr } from '@vinr/sdk';

const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

async function provisionBatch(
  activationCodes: string[],
  locationId: string,
) {
  const results = await Promise.allSettled(
    activationCodes.map((code, i) =>
      vinr.terminal.terminals.activate({
        activationCode: code,
        locationId,
        label: `Counter ${i + 1}`,
      })
    )
  );

  const failed = results.filter(r => r.status === 'rejected');
  if (failed.length > 0) {
    console.error(`${failed.length} terminals failed to activate`);
  }
}

Monitor fleet health

Subscribe to terminal.offline webhooks or poll the list API to keep an operations dashboard up to date. Use lastSeen as the primary staleness signal:

const OFFLINE_THRESHOLD_MINUTES = 10;

const { data: terminals } = await vinr.terminal.terminals.list({ limit: 200 });

const offline = terminals.filter(t => {
  const minutesSinceHeartbeat =
    (Date.now() - new Date(t.lastSeen).getTime()) / 60_000;
  return minutesSinceHeartbeat > OFFLINE_THRESHOLD_MINUTES;
});

if (offline.length > 0) {
  await alertOpsChannel(
    `${offline.length} terminals offline: ${offline.map(t => t.id).join(', ')}`
  );
}

Push config changes across all locations

const { data: locations } = await vinr.terminal.locations.list();

await Promise.all(
  locations.map(loc =>
    vinr.terminal.locations.bulkConfigure({
      locationId: loc.id,
      tipping: { enabled: true, presets: [15, 18, 20] },
      receipt: { footerText: 'Thank you!' },
    })
  )
);

Deactivate and reassign returned devices

When a field agent returns a device, deactivate it from the old location and prepare it for reuse:

await vinr.terminal.terminals.action({
  id: 'term_01HZ5QXYZ',
  action: 'lock',
  reason: 'Returned from field agent — pending reassignment',
});

await vinr.terminal.terminals.action({
  id: 'term_01HZ5QXYZ',
  action: 'rotate_keys',
  reason: 'Pre-reassignment security rotation',
});

await vinr.terminal.terminals.update({
  id: 'term_01HZ5QXYZ',
  locationId: 'loc_warehouse',
  label: 'Spare — unassigned',
});

Terminal object fieldsAsk

Prop

Type

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page