Skip to content

synergis rio developer guide

Andre Lafleur edited this page Oct 8, 2026 · 6 revisions

Synergis RIO developer guide

Synergis RIO (Readers, Inputs, and Outputs) is an HTTPS/XML protocol that lets third-party access control hardware connect to a Synergis appliance. The hardware reports reader activity, input changes, connection state, and optional offline decisions. Synergis Softwire remains the access control engine. It evaluates access rules, schedules, door state, interlocks, antipassback, zones, elevator logic, and the other access control features configured in Security Center.

Use this guide when you are building a RIO device, a RIO simulator, or a gateway that presents third-party hardware to a Synergis appliance.

For a broader comparison between RIO and access control plugins, see Access control integration architecture.

What you are building

A RIO integration is not a Security Center plugin. It is a hardware-level integration with Synergis Softwire.

Your RIO implementation is responsible for:

  • Declaring the hardware topology that exists on the RIO bus
  • Reporting whether interfaces and devices are connected
  • Reporting input state changes
  • Reporting card reads and PIN entries
  • Receiving output commands from Softwire and applying them to relays or local hardware
  • Receiving reader feedback commands from Softwire and applying them to LEDs, buzzers, displays, or equivalent hardware
  • Reporting decisions that were made offline, if your hardware supports an offline operating mode

Softwire is responsible for:

  • Creating and exposing the hardware endpoints to Security Center
  • Mapping readers, inputs, and outputs to doors, elevators, zones, and other access control entities
  • Evaluating access rules and schedules
  • Making online grant or deny decisions
  • Producing access control events and alarms
  • Sending output and reader feedback commands back to the RIO device

RIO integrates third-party hardware with Synergis. It does not integrate an independent access control system that makes its own access decisions.

Architecture

flowchart LR
    subgraph Hardware["Third-party hardware or gateway"]
        R1["Readers"]
        I1["Inputs"]
        O1["Outputs"]
        Runtime["RIO runtime"]
    end

    subgraph Appliance["Synergis appliance"]
        Api["ExternalIntegrations API"]
        Softwire["Synergis Softwire"]
        Bus["RIO bus configuration"]
    end

    subgraph SecurityCenter["Security Center"]
        AM["Access Manager"]
        CT["Config Tool"]
        SD["Security Desk"]
    end

    R1 --> Runtime
    I1 --> Runtime
    Runtime --> O1
    Runtime <-->|"HTTPS XML"| Api
    Api --> Softwire
    Bus --> Softwire
    Softwire <-->|"Access control data and events"| AM
    CT --> AM
    SD --> AM
Loading

Your integration communicates with the Synergis appliance over HTTPS.

Prepare the Synergis appliance

RIO development requires a Synergis appliance running Synergis Softwire, reachable over HTTPS on TCP 443. Enroll the appliance in Security Center under an Access Manager role as a unit of type Synergis; for the procedure, find Enrolling Synergis units in Security Center for your version on the Genetec TechDoc Hub.

Prepare the appliance for RIO development:

  • Create a dedicated user for the RIO runtime in the Synergis Appliance Portal, on the account management page at https://UNIT-ADDRESS/smc/accountmanagement.html, logged on as an administrator. The password is case sensitive, and the username you list in the channel's AllowUsers field must match the account name exactly, including case.
  • Use an administrator account to create, modify, or delete channels. The runtime user cannot manage channels; it can only send updates and poll commands for the channels that list it in AllowUsers.

The unit's Hardware page in Config Tool does not display the RIO configuration tab by default. To display it, request https://UNIT-ADDRESS/Features/DuiRIO/Enabled/Set?value=true while logged on to the appliance as an administrator, for example by opening the URL in a browser. GET https://UNIT-ADDRESS/Features/DuiRIO/Enabled returns the current value, and value=false disables the tab after the next restart.

Protocol summary

RIO uses XML payloads over HTTPS. The API is exposed under the following base path on the Synergis appliance:

/ExternalIntegrations

The main resources are:

Operation Method and path Purpose
List channels GET /ExternalIntegrations/ Lists configured external integration channels.
Read a channel GET /ExternalIntegrations/{channel-name} Returns the current RIO bus configuration for one channel.
Create or replace a channel PUT /ExternalIntegrations/{channel-name} Creates a RIO bus or replaces the full configuration of an existing RIO bus.
Delete a channel DELETE /ExternalIntegrations/{channel-name} Removes the RIO bus configuration and the corresponding hardware endpoints.
Discover channel protocol GET /ExternalIntegrations/{channel-name}/Protocol Returns the protocol endpoints available for the channel.
View update sample GET /ExternalIntegrations/{channel-name}/Update Returns a sample update payload with an HTTP 405 response. This is useful during development.
Send runtime updates POST /ExternalIntegrations/{channel-name}/Update Sends keepalive, connectivity, input, card, PIN, or offline-decision updates to Softwire.
Start command polling GET /ExternalIntegrations/{channel-name}/Events/StartSession Starts a long-polling session for output and reader commands.
Poll commands GET {LongPollUri}?reqid={n} Retrieves downstream commands for the active session.
End command polling GET {HangupUri} Ends the long-polling session.

Send this request over HTTPS to the Synergis appliance. Replace USERNAME and PASSWORD with the appliance account credentials. URL-encode each value before inserting it into the request body.

POST /Login
Content-Type: application/x-www-form-urlencoded

username=USERNAME&password=PASSWORD

A successful logon returns HTTP 200 and a .AUTHENTICATION cookie in the Set-Cookie response header. Configure your HTTP client to save this cookie and send it in the Cookie header of subsequent RIO requests.

Use an administrator account for channel configuration. Use the non-admin account listed in AllowUsers to send hardware updates and receive commands.

Production implementations must use TLS 1.2 or later.

Authorization model

There are two distinct authorization paths.

Channel administration:

  • Creating, replacing, reading, and deleting a RIO channel is an administrative operation.
  • Do this from a setup tool or installer, not from the normal runtime loop if you can avoid it.
  • Treat PUT /ExternalIntegrations/{channel-name} as a full replace operation.

Runtime access:

  • The RIO runtime must authenticate to the Synergis appliance.
  • Add your integration's non-admin account to the channel's AllowUsers field so it can send hardware updates and receive commands.
  • A dedicated RIO runtime user can be restricted to the RIO channels it needs. It does not need broad Security Center administrative privileges.
  • Being allowed on one RIO channel does not grant access to other RIO channels.

Recommended practice:

  • Use one dedicated runtime account per product, site, or device fleet.
  • Put that account in AllowUsers.
  • Keep channel administration separate from runtime operation.
  • Reauthenticate cleanly if the appliance returns a redirect or authentication failure.

Channel configuration model

A RIO channel describes a bus of one or more third-party interfaces. Each interface contains readers, inputs, and outputs. Device IDs are generated by position.

Configuration element Meaning
RioBus The RIO bus configuration for the channel.
Interfaces The list of interfaces exposed by the RIO bus.
CustomInterface A single third-party interface on the bus.
Address The stable interface address used by runtime messages and hardware paths.
Template Manufacturer, model, description, and labels for readers, inputs, and outputs.
InputLabels Labels used to create inputs. They become I1, I2, I3, and so on.
OutputLabels Labels used to create outputs. They become O1, O2, O3, and so on.
ReaderLabels Labels used to create readers. They become R1, R2, R3, and so on.
ReportAsSimulated Optional. Reports the interface as simulated hardware. Use this for simulators and test tools, not production hardware.
AllowUsers Optional runtime access list. Add the RIO runtime username here.

Device IDs are not copied from the label text. They are positional:

Label list Generated device IDs
ReaderLabels R1, R2, R3, ...
InputLabels I1, I2, I3, ...
OutputLabels O1, O2, O3, ...

Use stable channel names, interface addresses, and label order. Changing them can create a different hardware identity from the point of view of Softwire and Security Center.

Hardware paths

RIO devices have stable hardware paths.

/Devices/Bus/External/{channel-name}/{interface-address}/Reader/{reader-id}
/Devices/Bus/External/{channel-name}/{interface-address}/Input/{input-id}
/Devices/Bus/External/{channel-name}/{interface-address}/Output/{output-id}

Examples:

/Devices/Bus/External/DemoCampus/MainEntrance/Reader/R1
/Devices/Bus/External/DemoCampus/MainEntrance/Input/I1
/Devices/Bus/External/DemoCampus/MainEntrance/Output/O1

In Config Tool, the hardware is displayed using the manufacturer, model, channel name, and interface address. For example:

Acme Controls - ACME-2DR (DemoCampus - MainEntrance)

Create a RIO channel

Create or replace a channel with PUT /ExternalIntegrations/{channel-name}.

The request body is a Request wrapper that contains a RioBus.

In the examples, REX stands for request to exit.

<Request>
  <RioBus>
    <Interfaces>
      <Interface>
        <CustomInterface>
          <Address>MainEntrance</Address>
          <Template>
            <Manufacturer>Acme Controls</Manufacturer>
            <ModelName>ACME-2DR</ModelName>
            <Description>Two door RIO interface</Description>
            <InputLabels>
              <String>Door contact 1</String>
              <String>REX 1</String>
              <String>Door contact 2</String>
              <String>REX 2</String>
            </InputLabels>
            <OutputLabels>
              <String>Lock relay 1</String>
              <String>Lock relay 2</String>
            </OutputLabels>
            <ReaderLabels>
              <String>Reader 1</String>
              <String>Reader 2</String>
            </ReaderLabels>
          </Template>
        </CustomInterface>
      </Interface>
    </Interfaces>
    <ReportAsSimulated>false</ReportAsSimulated>
    <AllowUsers>
      <String>rio-runtime</String>
    </AllowUsers>
  </RioBus>
</Request>

The response contains the channel's API path:

/ExternalIntegrations/DemoCampus/

After PUT returns successfully, poll GET /ExternalIntegrations/{channel-name} until the channel can be read before sending discovery requests or runtime updates. A newly created channel can briefly return 404 Integration not found while Softwire finishes registering it.

To define multiple interfaces on the same channel, add more Interface entries. Each Interface entry contains one CustomInterface.

<Request>
  <RioBus>
    <Interfaces>
      <Interface>
        <CustomInterface>
          <Address>MainEntrance</Address>
          <Template>
            <Manufacturer>Acme Controls</Manufacturer>
            <ModelName>ACME-2DR</ModelName>
            <Description>Main entrance controller</Description>
            <InputLabels>
              <String>Door contact</String>
              <String>REX</String>
            </InputLabels>
            <OutputLabels>
              <String>Lock relay</String>
            </OutputLabels>
            <ReaderLabels>
              <String>Entry reader</String>
            </ReaderLabels>
          </Template>
        </CustomInterface>
      </Interface>
      <Interface>
        <CustomInterface>
          <Address>ShippingDoor</Address>
          <Template>
            <Manufacturer>Acme Controls</Manufacturer>
            <ModelName>ACME-1DR</ModelName>
            <Description>Shipping door controller</Description>
            <InputLabels>
              <String>Door contact</String>
              <String>REX</String>
            </InputLabels>
            <OutputLabels>
              <String>Lock relay</String>
            </OutputLabels>
            <ReaderLabels>
              <String>Entry reader</String>
            </ReaderLabels>
          </Template>
        </CustomInterface>
      </Interface>
    </Interfaces>
    <AllowUsers>
      <String>rio-runtime</String>
    </AllowUsers>
  </RioBus>
</Request>

Read, modify, and delete a channel

Read a channel:

GET /ExternalIntegrations/DemoCampus

List channels:

GET /ExternalIntegrations/

Delete a channel:

DELETE /ExternalIntegrations/DemoCampus

A delete removes the RIO bus and the corresponding hardware endpoints. Do not delete and recreate a channel as a normal update strategy. If you need to modify the bus, read the current configuration, preserve stable names and label order, and then send a replacement RioBus with PUT.

Runtime lifecycle

Use an administrator account to check that the RIO channel exists. The non-admin account cannot read the channel configuration.

Add the non-admin account to the channel's AllowUsers field. Your integration uses this account to send hardware updates and receive commands.

A production RIO runtime follows this lifecycle:

  1. Authenticate to the Synergis appliance.
  2. Start the keepalive watchdog by posting StatusKeepalive.
  3. Mark the interface or devices online by posting SetConnected.
  4. Start a long-polling session with Events/StartSession.
  5. Poll for downstream commands using the returned LongPollUri.
  6. Send card, PIN, input, and connectivity updates as hardware activity occurs.
  7. Refresh the keepalive before the previous duration expires.
  8. Apply output and reader feedback commands idempotently.
  9. On clean shutdown, mark affected interfaces or devices offline and call the HangupUri.

Keepalive and connected state are separate:

  • StatusKeepalive starts or refreshes the watchdog and sets its duration.
  • SetConnected reports whether interfaces or devices are online or offline.
  • If the keepalive expires, Softwire marks the RIO interfaces offline and sends a downstream KeepaliveTimeout message.
  • A later StatusKeepalive replaces the previous watchdog duration.

Use a keepalive duration that is longer than the send interval. A common pattern is to send a keepalive every 5 seconds with a 10-second duration. Slower devices can use a longer interval, but set the duration to about twice the send interval.

BusUpdate envelope

Runtime updates are sent with POST /ExternalIntegrations/{channel-name}/Update.

The body is a Request wrapper that contains one or more BusUpdate elements.

<Request>
  <BusUpdate>
    <StatusKeepalive>
      <Duration>00:00:10</Duration>
    </StatusKeepalive>
  </BusUpdate>
  <BusUpdate>
    <SetConnected>
      <Interface>MainEntrance</Interface>
      <IsConnected>true</IsConnected>
      <SpecificDevices>
        <None />
      </SpecificDevices>
    </SetConnected>
  </BusUpdate>
</Request>

Batching is supported, but use it carefully. Softwire validates each update and returns errors for invalid items. Do not mix critical updates with experimental or optional updates in the same request, because a partially valid batch can still have side effects before the error response is returned.

You can request a sample update payload during development:

GET /ExternalIntegrations/DemoCampus/Update

The expected response status is HTTP 405, because updates must be sent with POST. The response body is still useful because it shows the expected XML structure.

StatusKeepalive

StatusKeepalive starts or refreshes the RIO watchdog.

<Request>
  <BusUpdate>
    <StatusKeepalive>
      <Duration>00:00:10</Duration>
    </StatusKeepalive>
  </BusUpdate>
</Request>
Field Required Notes
Duration Yes Watchdog duration in .NET TimeSpan format, such as 00:00:10. ISO 8601 values such as PT10S are rejected.

Implementation guidance:

  • Send StatusKeepalive before SetConnected when the runtime starts.
  • Refresh it periodically before it expires.
  • If the runtime loses communication with the appliance, reconnect and send a new keepalive before resuming normal event traffic.
  • Treat KeepaliveTimeout from the downstream poll as a signal that Softwire marked the channel offline.

SetConnected

SetConnected reports whether a full interface or selected devices are online.

Bring a full interface online:

<Request>
  <BusUpdate>
    <SetConnected>
      <Interface>MainEntrance</Interface>
      <IsConnected>true</IsConnected>
      <SpecificDevices>
        <None />
      </SpecificDevices>
    </SetConnected>
  </BusUpdate>
</Request>

Bring a full interface offline:

<Request>
  <BusUpdate>
    <SetConnected>
      <Interface>MainEntrance</Interface>
      <IsConnected>false</IsConnected>
      <SpecificDevices>
        <None />
      </SpecificDevices>
    </SetConnected>
  </BusUpdate>
</Request>

Report specific devices online:

<Request>
  <BusUpdate>
    <SetConnected>
      <Interface>MainEntrance</Interface>
      <IsConnected>true</IsConnected>
      <SpecificDevices>
        <String>R1</String>
        <String>I1</String>
        <String>O1</String>
      </SpecificDevices>
    </SetConnected>
  </BusUpdate>
</Request>

Report specific devices offline:

<Request>
  <BusUpdate>
    <SetConnected>
      <Interface>MainEntrance</Interface>
      <IsConnected>false</IsConnected>
      <SpecificDevices>
        <String>R2</String>
      </SpecificDevices>
    </SetConnected>
  </BusUpdate>
</Request>
Field Required Notes
Interface Yes Interface address from the channel configuration.
IsConnected Yes true for online, false for offline.
SpecificDevices Yes Use <None /> for the full interface, or include device IDs directly for selected readers, inputs, and outputs.

Behavior details:

  • With <None />, Softwire applies the state to the interface and all devices on the interface.
  • With specific device IDs and IsConnected=true, Softwire marks the interface online and then marks the listed devices online.
  • With specific device IDs and IsConnected=false, Softwire marks only the listed devices offline.
  • Use specific devices for partial failures, such as one reader disconnected while the controller remains online.

InputStatusUpdate

InputStatusUpdate reports a state change for one input.

<Request>
  <BusUpdate>
    <InputStatusUpdate>
      <Interface>MainEntrance</Interface>
      <Input>I1</Input>
      <State>
        <Active />
      </State>
      <Timestamp>2026-07-08T14:25:10Z</Timestamp>
    </InputStatusUpdate>
  </BusUpdate>
</Request>
Field Required Notes
Interface Yes Interface address from the channel configuration.
Input Yes Input ID such as I1.
State Yes One of <Normal />, <Active />, <Trouble />, <Cut />, or <Short /> inside the State element.
Timestamp No Softwire ignores this field for input state updates.

Input states:

RIO state Meaning in Softwire
Normal Input is in normal or inactive state.
Active Input is active.
Trouble Input trouble state, unknown trouble type.
Cut Input trouble state, cut.
Short Input trouble state, short.

Use Normal and Active for normal input transitions. Use Trouble, Cut, and Short only when the hardware can detect those conditions. Do not serialize the state as text, such as <State>Normal</State>.

CardSwipe

CardSwipe reports a credential read from a reader.

<Request>
  <BusUpdate>
    <CardSwipe>
      <Interface>MainEntrance</Interface>
      <Reader>R1</Reader>
      <BitCount>26</BitCount>
      <CardCode>026E0D06</CardCode>
      <Timestamp>2026-07-08T14:26:00Z</Timestamp>
    </CardSwipe>
  </BusUpdate>
</Request>
Field Required Notes
Interface Yes Interface address from the channel configuration.
Reader Yes Reader ID such as R1.
BitCount Yes Number of meaningful credential bits.
CardCode Yes Hexadecimal card data. The value must contain full bytes.
Timestamp No UTC timestamp. If omitted, Softwire uses the appliance time.

Credential encoding rules:

  • CardCode is hexadecimal byte data.
  • BitCount specifies the number of meaningful bits in CardCode.
  • A card code can be up to 512 bits.
  • For an N-byte CardCode, valid BitCount values are from N * 8 - 7 through N * 8.
  • Example: a 4-byte CardCode allows BitCount values from 25 through 32.
  • A 26-bit credential is therefore encoded in 4 bytes, for example 026E0D06 with BitCount set to 26.

After Softwire receives a card swipe, it evaluates the access decision. If access is granted, Softwire can send reader feedback and output commands through the long-polling channel. If access is denied, Softwire can send denied reader feedback.

Deriving the CardCode value

CardCode contains the credential's raw card data with the meaningful bits right-aligned: pad the leading end with zero bits to fill full bytes. Set every pad bit to zero; a read whose pad bits are not zero does not match any credential.

In the standard 26-bit Wiegand format, the bits are, in order: one even parity bit computed over the first 12 data bits, the 8-bit facility code, the 16-bit card number, and one odd parity bit computed over the last 12 data bits.

To derive the value for facility code 55, card number 1667:

  1. Write the data bits: facility code 55 is 00110111, and card number 1667 is 0000011010000011.
  2. Compute the parity bits. The first 12 data bits 001101110000 contain five 1 bits, so the even parity bit is 1. The last 12 data bits 011010000011 contain five 1 bits, so the odd parity bit is 0.
  3. Assemble the 26 bits, 1 00110111 0000011010000011 0, and pad with six leading zero bits to fill 4 bytes.
  4. The result is CardCode 026E0D06 with BitCount 26.

For a custom card format, apply the same procedure using the field masks and parity checks of the format definition; see custom card formats. To create credentials that match your test reads, see About cardholders and credentials.

License plate credentials

A license plate credential is matched when the plate text is reported as a CardSwipe: encode each character as two bytes, the character's ASCII value followed by 00, and set BitCount to 16 times the number of characters. For the plate FET2605, CardCode is 4600450054003200360030003500 and BitCount is 112.

  • The match is exact on the plate text stored in the credential; unlike an ALPR reader, no misread tolerance is applied.
  • The resulting access events are not attributed to the credential entity, so reports such as Door activities do not identify the credential.

PinEntry

PinEntry reports a PIN entered at a reader.

<Request>
  <BusUpdate>
    <PinEntry>
      <Interface>MainEntrance</Interface>
      <Reader>R1</Reader>
      <Pin>1234</Pin>
      <Timestamp>2026-07-08T14:27:00Z</Timestamp>
    </PinEntry>
  </BusUpdate>
</Request>
Field Required Notes
Interface Yes Interface address from the channel configuration.
Reader Yes Reader ID such as R1.
Pin Yes Digits only.
Timestamp No UTC timestamp. If omitted, Softwire uses the appliance time.

PIN validation:

  • The PIN can contain digits only.
  • The maximum length is controlled by the appliance access control settings.
  • If the hardware sends keypad digits as individual 4-bit reader values, report them as card swipes only if that is the expected reader behavior for the configured reader.
  • For normal card-and-PIN operation, use CardSwipe for the card read and PinEntry for the PIN.

OfflineDecision

OfflineDecision reports an access decision that the hardware made while operating offline.

Use this only when the hardware made the decision locally because it was unable to obtain an online decision from Softwire. Do not use OfflineDecision during normal online operation.

Granted offline decision with a card:

<Request>
  <BusUpdate>
    <OfflineDecision>
      <Interface>MainEntrance</Interface>
      <Reader>R1</Reader>
      <Timestamp>2026-07-08T14:28:00Z</Timestamp>
      <Card>
        <Some>
          <BitCount>26</BitCount>
          <CardCode>026E0D06</CardCode>
        </Some>
      </Card>
      <Granted>true</Granted>
    </OfflineDecision>
  </BusUpdate>
</Request>

Denied offline decision with a PIN:

<Request>
  <BusUpdate>
    <OfflineDecision>
      <Interface>MainEntrance</Interface>
      <Reader>R1</Reader>
      <Timestamp>2026-07-08T14:28:30Z</Timestamp>
      <Card>
        <None />
      </Card>
      <Pin>1234</Pin>
      <Granted>false</Granted>
    </OfflineDecision>
  </BusUpdate>
</Request>
Field Required Notes
Interface Yes Interface address from the channel configuration.
Reader Yes Reader ID such as R1.
Timestamp Yes UTC timestamp for the offline decision.
Card No Optional card credential. Use <Card><None /></Card> when you need to explicitly report no card.
Pin No Optional PIN. If present, use the PIN digits as the element text.
Granted Yes true if the local hardware granted access, false if it denied access.

Behavior details:

  • If a card is included, the same card encoding rules as CardSwipe apply.
  • If a PIN is included, the same PIN validation rules as PinEntry apply.
  • Softwire records an autonomous access decision event.
  • If the offline decision is granted and Softwire can associate the credential with a cardholder, Softwire can also create the corresponding movement event.

Downstream command polling

RIO uses long polling for commands from Softwire to the runtime. Long polling is a normal HTTP GET that Softwire holds open until one of these conditions occurs:

  • A command or event is available for the runtime.
  • The maximum response timeout expires.
  • The session is ended.
  • The request violates the session rules.

Long polling is not a WebSocket and it is not a background callback from Softwire to your device. Your runtime always initiates the request.

Start a session

Start a long-polling session with this request:

GET /ExternalIntegrations/DemoCampus/Events/StartSession

The response contains the values your runtime needs for the polling loop.

Field Meaning
LongPollUri URI to call for command polling. This value does not include reqid.
HangupUri URI to call when the session is no longer needed.
InitialState Current bus state, including the appliance time and known output states.
MaxResponseTimeout Maximum time the server can hold a poll request before returning an empty response.
SupportsRetry Indicates whether retry behavior is supported for request IDs.
AdditionalEndpoints Extra endpoint metadata, if any.

A typical response looks like this:

<Response>
  <LongPollUri>
    /ExternalIntegrations/DemoCampus/Events/LongPoll/1554473948
  </LongPollUri>
  <HangupUri>
    /ExternalIntegrations/DemoCampus/Events/EndSession/1554473948
  </HangupUri>
  <AdditionalEndpoints />
  <InitialState>
    <CurrentTime>2026-07-08T09:12:25.419Z</CurrentTime>
    <Outputs />
  </InitialState>
  <MaxResponseTimeout>120</MaxResponseTimeout>
  <SupportsRetry>true</SupportsRetry>
</Response>

The numeric value at the end of LongPollUri is the server session ID. It is not the request ID.

Poll with reqid

Your runtime must append a client-generated reqid query parameter to the LongPollUri.

GET /ExternalIntegrations/DemoCampus/Events/LongPoll/1554473948?reqid=1
GET /ExternalIntegrations/DemoCampus/Events/LongPoll/1554473948?reqid=2
GET /ExternalIntegrations/DemoCampus/Events/LongPoll/1554473948?reqid=3

Start at reqid=1 for each new session. Increment reqid only after receiving the complete HTTP 200 response and processing its commands. An empty response also counts as success.

Understand the held request

When your runtime sends a poll request, Softwire can keep the request open for up to MaxResponseTimeout seconds. A typical value is 120 seconds.

This is expected behavior:

  1. Your runtime sends GET {LongPollUri}?reqid=1.
  2. Softwire has no command ready, so it does not respond immediately.
  3. A command becomes available, or the timeout expires.
  4. Softwire returns an XML response.
  5. Your runtime processes the response.
  6. Your runtime sends the next request with reqid=2.

Configure your HTTP client read timeout to be longer than MaxResponseTimeout, plus a small network margin. If the client times out first, treat that timeout as an ambiguous transport failure and retry the same reqid.

Retry safely

SupportsRetry=true means Softwire can protect your runtime from losing a response when the client cannot determine whether the previous request completed.

Use this retry rule:

  1. Send GET {LongPollUri}?reqid=N.
  2. If you receive a complete HTTP 200 response, process it and increment to N + 1. Handle other HTTP statuses as errors.
  3. If the connection fails before you receive an HTTP response, retry reqid=N.
  4. If Softwire already completed that reqid, it can replay the cached response.
  5. If the retry returns the cached response, process it once and then increment to N + 1.

Do not use the replay behavior as your normal polling loop. It exists for transport failures and ambiguous delivery.

Avoid concurrent polls

Send one poll at a time. Wait for its response before sending the next request ID.

If a connection failure requires you to retry, reuse the same reqid. If the original request is still pending, Softwire returns HTTP 200 with an empty response to both requests.

Sending a different reqid, or omitting it, while a poll is pending causes this error:

Received another request for the same session

After this error, close the session using HangupUri if possible. Start a new session and begin polling with reqid=1.

Handle empty responses

If no commands are available, Softwire holds the request until MaxResponseTimeout expires, then returns an empty response.

<Response />

An empty response is not an error. Process it as "no commands available" and immediately issue the next poll with the next reqid.

Polling loop

This pseudocode shows the required control flow.

Apply the output commands in InitialState.Outputs whenever you start a session. If Outputs is empty, there are no initial output commands to apply.

session = start_session()
apply_initial_outputs(session.InitialState.Outputs)
reqid = 1

while runtime_is_running:
    try:
        response = GET session.LongPollUri + "?reqid=" + reqid
    except transport_failure:
        continue

    if response.status == 200:
        process_bus_messages(response.body)
        reqid = reqid + 1
        continue

    if response.body contains "Received another request":
        GET session.HangupUri
        session = start_session()
        apply_initial_outputs(session.InitialState.Outputs)
        reqid = 1
        continue

    handle_error(response)

Here, apply_initial_outputs represents the integration's code for applying those commands.

During clean shutdown, call the HangupUri for the active session.

InitialState

The long-polling session starts with an initial bus state.

Important fields:

Field Meaning
CurrentTime Appliance time when the initial state was generated.
Outputs Latest command state for outputs known to Softwire.

Outputs can be empty:

<InitialState>
  <CurrentTime>2026-07-08T09:12:25.419Z</CurrentTime>
  <Outputs />
</InitialState>

When output state is available, Outputs contains OutputControlMessage entries. The initial state structure is different from later poll responses, which use BusMessage entries.

<InitialState>
  <CurrentTime>2026-07-08T09:12:25.419Z</CurrentTime>
  <Outputs>
    <OutputControlMessage>
      <Interface>MainEntrance</Interface>
      <Output>O1</Output>
      <StartTime>2026-07-08T09:12:20.000Z</StartTime>
      <Command>
        <Clear />
      </Command>
    </OutputControlMessage>
  </Outputs>
</InitialState>

Use InitialState to reconcile your hardware outputs when a runtime reconnects. If Softwire previously sent a Pulse command and the pulse duration has already expired, the initial output state is reported as Clear.

Output commands

Softwire sends output commands when it needs your runtime to change an output state.

Example output command:

<Response>
  <BusMessage>
    <Output>
      <Interface>MainEntrance</Interface>
      <Output>O1</Output>
      <StartTime>2026-07-08T14:26:00Z</StartTime>
      <Command>
        <Pulse>
          <Duration>00:00:05</Duration>
        </Pulse>
      </Command>
    </Output>
  </BusMessage>
</Response>
Field Meaning
Interface Interface address from the RIO channel.
Output Output ID such as O1.
StartTime Appliance time associated with the command.
Command Set, Clear, or Pulse.

Output command meanings:

Command Meaning
Set Set the output to the active state.
Clear Set the output to the normal state.
Pulse Set the output to the active state for the specified duration, then return it to normal.

Pulse durations use .NET TimeSpan format. Parse values such as 00:00:05 and fractional values such as 00:00:05.5980000.

A pulse ends at StartTime + Duration. If you receive the command after its start time, activate the output only for the remaining time. If that end time has already passed, do not activate the output.

A retry can return the same pulse command with its original StartTime and Duration. Do not restart the pulse or extend its end time when you receive it again.

One access decision or door action can generate more than one downstream message. For example, a granted card read can produce a reader GrantedFeedback message and a separate output Pulse message. These messages can arrive in separate poll responses. Keep polling until you have processed all messages that follow the triggering event.

Door control actions can also generate output commands:

  • A momentary unlock action can generate Pulse.
  • A free-access or unlock override can generate Set.
  • Returning the door to controlled access can generate Clear.

Do not hard-code Set as unlock and Clear as lock. The physical meaning depends on how the output is wired and how the door is configured. Treat Set, Clear, and Pulse as electrical output states.

Reader feedback commands

Softwire sends reader feedback commands after an access decision.

Granted feedback:

<Response>
  <BusMessage>
    <Reader>
      <Interface>MainEntrance</Interface>
      <Reader>R1</Reader>
      <StartTime>2026-07-08T14:26:00Z</StartTime>
      <Command>
        <GrantedFeedback />
      </Command>
    </Reader>
  </BusMessage>
</Response>

Denied feedback:

<Response>
  <BusMessage>
    <Reader>
      <Interface>MainEntrance</Interface>
      <Reader>R1</Reader>
      <StartTime>2026-07-08T14:26:02Z</StartTime>
      <Command>
        <DeniedFeedback />
      </Command>
    </Reader>
  </BusMessage>
</Response>

Reader feedback commands:

Command Suggested hardware behavior
GrantedFeedback Show access granted feedback on the reader.
DeniedFeedback Show access denied feedback on the reader.

The exact LED, buzzer, or display behavior depends on your reader and product requirements.

For a mapped door, a valid card read normally produces GrantedFeedback and an output command for the lock. An unknown or unauthorized card read normally produces DeniedFeedback and no lock output command.

KeepaliveTimeout message

If the keepalive watchdog expires, Softwire marks the RIO interfaces offline and can send a downstream message:

<Response>
  <BusMessage>
    <KeepaliveTimeout />
  </BusMessage>
</Response>

When the runtime receives this message:

  • Refresh the keepalive after reconnecting.
  • Send SetConnected for the current interface or device state.
  • Reconcile outputs using the current polling state.
  • Record the timeout in your device logs.

Online access decision sequence

The typical online sequence is:

  1. Runtime sends StatusKeepalive.
  2. Runtime sends SetConnected for the interface.
  3. Runtime starts long polling.
  4. User presents a card.
  5. Runtime sends CardSwipe.
  6. Softwire evaluates the cardholder, credential, access rules, schedules, door state, and related access control logic.
  7. If access is granted, Softwire sends GrantedFeedback and the required output command, often a Pulse on the lock relay.
  8. If access is denied, Softwire sends DeniedFeedback.
  9. Runtime applies commands to the reader and output hardware.
  10. Runtime continues reporting inputs, card reads, PIN entries, and connectivity changes.

Softwire makes the access decision. Your runtime must not grant online access on its own while Softwire is reachable.

Door configuration in Security Center

After the RIO channel is created, the hardware endpoints can be assigned to access control entities in Config Tool.

Typical door mapping:

Door function RIO device
Entry reader Reader/R1
Exit reader Reader/R2, if present
Door contact Input/I1
REX input Input/I2
Lock output Output/O1

The exact mapping depends on the door design. Once mapped, Softwire uses the RIO devices as part of the normal Synergis access control model. That means the same RIO hardware can participate in supported Synergis features such as doors, elevators, zones, I/O zones, schedules, card and PIN behavior, interlocks, and antipassback, depending on the Security Center configuration and licensed features.

Discover the generated entities

After the channel is created, the Synergis appliance appears as a Unit entity, each RIO interface as an InterfaceModule entity, and each reader, input, and output as a Device entity. For the unit and interface module model, see About access control units and interface modules.

The unit's Devices property returns the GUIDs of its devices, and its InterfaceModules property returns the GUIDs of its interface modules. A device's UniqueId property contains its hardware path, so the path segments identify the channel name, the interface address, and the device ID that BusUpdate messages use. The DeviceType property distinguishes readers, inputs, and outputs, and the InterfaceModule and Unit properties identify the device's interface module and unit. Match devices on the full UniqueId path: a device ID such as R1 repeats on every interface.

void PrintRioDevices(Engine engine, Guid unitGuid, string channelName)
{
    var unit = (Unit)engine.GetEntity(unitGuid);
    string prefix = $"/Devices/Bus/External/{channelName}/";

    foreach (Guid deviceGuid in unit.Devices)
    {
        var device = (Device)engine.GetEntity(deviceGuid);
        if (device.UniqueId != null && device.UniqueId.StartsWith(prefix))
        {
            Console.WriteLine($"{device.DeviceType}: {device.UniqueId}");
        }
    }
}

For the DemoCampus channel, the output contains lines such as Reader: /Devices/Bus/External/DemoCampus/MainEntrance/Reader/R1.

The Web SDK forms:

GET /report/EntityConfiguration?q=EntityTypes@Unit,PageSize=100,ReturnFields@Name
GET /entity?q=entity={unit-guid},Devices,InterfaceModules
GET /entity?q=entity={device-guid},Name,UniqueId,DeviceType

The returned GUIDs are the values that the door wiring calls below expect: SetPreferredInterface takes the InterfaceModule GUID, and UpdateConnection takes the Device GUIDs.

Configure a mapped RIO door for validation

To validate reader feedback and output commands end to end, configure a temporary door that uses the RIO devices.

  1. Create a RIO channel that has at least one reader, one input for the door sensor, one input for the request to exit, and one output for the lock.
  2. Discover the generated Security Center entities. RIO readers, inputs, and outputs appear as Device entities. The RIO interface appears as an InterfaceModule entity. The Synergis appliance appears as a Unit entity.
  3. Create a temporary door, cardholder, credential, and access rule.
  4. Add the cardholder or cardholder group to the access rule.
  5. Apply the access rule to the door side that contains the RIO reader.
  6. Set the door preferred unit, preferred interface, and lock output.
  7. Read the door side access points from DoorSideIn or DoorSideOut.
  8. Use UpdateConnection to connect the RIO reader, request-to-exit input, and door sensor input to the door side access points.
  9. Bring the RIO interface online with StatusKeepalive and SetConnected.
  10. Start long polling.
  11. Trigger a door action or send a CardSwipe.

The Web SDK wiring calls use this format:

POST /entity?q=entity={door-guid},SetPreferredUnit({unit-guid}),SetPreferredInterface({interface-guid}),DoorLockDevice={lock-output-device-guid}
GET /entity?q=entity={door-guid},DoorSideIn,DoorSideOut
POST /entity?q=entity={door-guid},UpdateConnection({reader-access-point-guid},{reader-device-guid}),UpdateConnection({rex-access-point-guid},{rex-input-device-guid}),UpdateConnection({sensor-access-point-guid},{sensor-input-device-guid})

Use UpdateConnection, not AddConnection, for the reader, request-to-exit input, and door sensor access points that already exist on the door side.

Expected validation results:

  • UnlockDoor({door-guid}) generates an output Pulse.
  • OverrideLockingScheduleOnDoor({door-guid},true,Hour,1) generates output Set.
  • OverrideLockingScheduleOnDoor({door-guid},false,Hour,1) generates output Clear.
  • A known card with access generates GrantedFeedback and an output Pulse.
  • An unknown or unauthorized card generates DeniedFeedback.

Error handling

Handle HTTP status and protocol errors explicitly.

Condition Typical result Correct response
Authentication expired or failed Redirect or authentication error Reauthenticate and retry safely.
Account does not have permission for the requested operation Forbidden For hardware updates and command polling, add the non-admin account to the channel's AllowUsers field. For channel configuration, use an administrator account.
Channel does not exist Not found Create the channel or correct the channel name.
GET /Update Method not allowed with sample body Use the sample body for development, but send real updates with POST.
Unknown interface or device Error response with message Fix the channel configuration or runtime device ID.
Invalid card bit count Error response with message Fix CardCode length or BitCount.
Invalid PIN Error response with message Send digits only and respect the configured maximum length.
Concurrent long poll for same session Error response with message End the session if possible, start a new session, and ensure only one in-flight poll per session.
Expired long-polling session Gone or session timeout response Start a new session.

Common protocol error messages include:

Message pattern Likely cause
Interface {name} not found Runtime used an interface address that is not in the channel.
Reader {id} not found on interface {name} Runtime used a reader ID that does not exist on that interface.
Input {id} not found on interface {name} Runtime used an input ID that does not exist on that interface.
Invalid BitCount. A 4 byte card code must have 25 <= BitCount <= 32 Card bit count does not match the number of bytes in CardCode.
Pin may only include digits PIN contained a non-digit character.
Unknown input status "{value}" Input state was not one of the supported values.
Unknown message type "{value}" The BusUpdate contained an unknown message.
Received another request for the same session Long-polling client sent concurrent requests for one session.

Log enough detail to diagnose problems:

  • Channel name
  • Interface address
  • Device ID
  • Update type
  • Timestamp sent by the runtime
  • HTTP status
  • Response message
  • Long-polling session ID and reqid, when applicable

Scalability and limits

Design within the supported limits of the appliance and Security Center version you target.

Limit Value
Interfaces per Softwire appliance 64
Readers per Softwire appliance 256
Credentials 600,000
Maximum credential size 512 bits
Long-polling response timeout Typically 120 seconds
Long-polling command batch size 100 messages

Implementation guidance:

  • Keep names and label order stable between updates.
  • Avoid unnecessary channel replacement.
  • Refresh keepalive on a fixed timer.
  • Debounce noisy inputs before sending state changes.
  • Treat output commands as idempotent. Applying the same command twice must not damage state.
  • Separate runtime health monitoring from access events in your logs.
  • Test with more than one interface per channel if your product supports multiple panels.

Minimal implementation checklist

Channel setup:

  • Choose a stable channel name.
  • Choose stable interface addresses.
  • Define reader, input, and output labels in the intended order.
  • Create a dedicated runtime user.
  • Add the runtime user to AllowUsers.
  • Create the channel with PUT /ExternalIntegrations/{channel-name}.
  • Wait until GET /ExternalIntegrations/{channel-name} returns the channel.
  • Verify the expected hardware endpoints appear in Config Tool.
  • Map readers, inputs, and outputs to doors or other access control entities.

Runtime:

  • Authenticate to the appliance.
  • Send StatusKeepalive.
  • Send SetConnected.
  • Start long polling.
  • Poll with one in-flight request per session.
  • Retry the same reqid after transport failure.
  • Increment reqid only after receiving and processing a complete HTTP 200 response.
  • Apply Set, Clear, and Pulse output commands.
  • Apply GrantedFeedback and DeniedFeedback.
  • Send InputStatusUpdate on input transitions.
  • Send CardSwipe on credential reads.
  • Send PinEntry for PIN entry.
  • Send OfflineDecision only for decisions made locally while offline.
  • Send SetConnected=false for hardware that goes offline.
  • Call the HangupUri during clean shutdown.

Validation:

  • Test an unknown card and confirm denied feedback.
  • Test a known card and confirm granted feedback and output pulse.
  • Test invalid reader, input, and output IDs.
  • Test invalid card bit counts.
  • Test non-digit PIN input.
  • Test keepalive timeout and recovery.
  • Test long-poll transport failure and retry behavior.
  • Test appliance restart or runtime restart.
  • Test full channel replacement without changing stable identity.

See also

Platform SDK

Plugin SDK

Workspace SDK

Media SDK

Macro SDK

Web SDK

Synergis RIO

Media Gateway

Genetec Web Player

Clone this wiki locally