iVendNextDevelopers My apps

Register, pull and read the queue

Part 1 of 4 · Reading iVendNext's events reliably

Back: the overviewNext: The labels, the reconciliation and the reference client

1. Register your application (the install does this)

A manifest installs your Registered Application (registered_applications, manifest v2), on, with one feed per app. From POS version 2.1.0.667 the install also makes your app's own user, <app>@integration.invalid, and names it on the registration as the user your service reads as. That user holds one role, <PREFIX> Integration, and works through the API only. The record types you follow (POS Invoice, Sales Invoice, Customer, Item, Stock Entry …) are on from the install; the retailer's administrator gives the role the reads your app needs. A hosted app is then connected by the site itself, after the install or an update (Connect again only if the site could not reach the app, or a key was lost or changed); an app you host yourself gets its key when the retailer presses Generate Keys on that user. An app installed before 2.1.0.667 gets its user at its next version, if its feed names no user. From then on, every change to a followed record adds one row to your queue — after the change commits, so a refused save sends nothing.

Changes made by your own integration user are not sent to you (no echo of your own writes).

Switching it on by script. A partner who sets a site up from a configuration script, for an app whose registration the install did not make, does three things, not one:

  1. Set the registration's user to the integration user (a registration the install made already names the app's own user).
  2. Tick each record type on the registration: registered_application_events[].enable is 1 on every row you follow. iVendNext writes a feed row only for a ticked record type. A feed with the user set and no row ticked reads empty.
  3. Expect the row a moment after the save commits. iVendNext writes it in a background job. A worker that polls every few seconds is fine; a script that saves a record and reads the feed at once finds nothing. In a test, poll until the feed has been quiet for two passes. (Sample S22's switch-on does the first two parts.)

2. Pull, act, mark done — in this order

loop:
  page = events(limit=100)                 # the OLDEST not-done events, oldest first
  for each event in page:
      if event.id in my seen-set: skip     # a repeat: already acted on
      in ONE transaction of my own:        # my business write + the seen-set entry, together
          act on the event                 #   (read the record over the API if I need its content)
          add event.id to my seen-set
      on failure: failed(event.id, note)   # it stays not-done, with the note, for the next pass
  done(ids of the page)                    # one call

3. Two ways to read the queue, one client

The client reads the queue in one of two ways (its --transport). Your handler is the same for both.

The feed every retailer has today (--transport engine) The scoped feed, the door (--transport door)
Calls the record API on Integration Sync Log (list your rows, mark them done with flag = 1, a note in error) events · done · failed, each a POST (the event feed page)
The key needs read + write on Integration Sync Log and read on the record types you follow — the narrowest grant that works read on the record types you follow — nothing on the log
Scope ⚠ none on the server. The key can read and mark done every application's rows, not only yours. The client filters to its own rows, but that is a convention, not a control. Use it only where your service is the retailer's one consumer the server's: only the caller's own application's rows. Another application's event is refused
Labels (sale.booked …) applied by the client, from the same published table decided by the server
Status ✅ works on every retailer today ✅ live: the same POST call was read on a test site on 2026-10-09; the client's crash-and-restart test ran only against our stand-in for it

Write your handler against the labels and both ways run it unchanged. Prefer the door where your service is not the retailer's only consumer (--door-prefix ivendnext_mpos.api.feed, the default).

Back: the overviewNext: The labels, the reconciliation and the reference client

This page in the kit