The i3X Connector lets an OutSystems app talk to an i3X (Industrial Information
Interface eXchange) v1 server without writing any HTTP, JSON, or custom code. i3X is a
REST standard for browsing industrial information models — namespaces, object types,
objects, their current and historical values, and live value subscriptions — and this
connector exposes that surface as ready-to-use ODC Server Actions.
Who it's for.Builders integrating ODC apps with industrial data platforms
(historians, asset models, telemetry stores) that speak i3X v1 — dashboards, asset
explorers, monitoring/alerting apps, and back-office tools. It is a clone-and-own
accelerator: it wraps the i3X v1 REST surface but does not provide an i3X server, store
data, cache, or manage credentials — you bring the server URL, the authentication, and
the app.
Key definitions
An i3X server models an industrial system as a browsable tree of typed things, each with
a stable id (element id) you pass back to the connector to drill in.
- Namespace — a named vocabulary grouping related object types (e.g. one per plant).
- Object type — the definition of a kind of thing, like a class (a "Pump" type).
- Object — an instance of an object type (one pump); what you list, read, subscribe to.
- Element id — the stable, server-assigned (opaque) identifier of an element. Discover
it once (e.g. with i3X_GetObjects), keep it, and pass it in later calls.
- Value — an object's data at a point in time: current (i3X_GetObjectValues) or
historical over a range (i3X_GetHistoricalValues).
- Relationship — how objects connect (e.g. "feeds"); walk to related objects with
i3X_QueryRelatedObjects.
- Subscription / monitored item — a server-side registration (i3X_CreateSubscription)
tracking changes to element ids you register on it (i3X_RegisterItems); read by polling.
- Sequence number — a cursor into a subscription's change stream. Each i3X_Sync poll
returns the highest number it saw; pass it back next poll (start negative) to get only
new updates.
- Dynamic node — a response part whose shape is defined by the object type, not the
API, so it cannot be strongly typed. Returned as valid raw JSON text in a field whose
name ends in JSON.
Why use it
- No plumbing. Drag an i3X action into a flow, wire its inputs, read back typed
outputs. The connector owns the HTTP calls, the envelopes, and error mapping — 17 of the
20 i3X v1 endpoints, grouped into discovery, objects and values, and subscriptions.
- Point it at any i3X server. The server URL and authentication are passed per call
through a single i3XConnection input (no-auth, Bearer-token, or API-key), so one app can
talk to many servers with no code change.
- Nothing is lost in translation. Dynamic nodes come back as valid raw JSON text you
can deserialize or display as-is (an absent optional node is an empty string), while
fixed fields stay strongly typed.
- Failures are data, not crashes. Every action returns a Success flag and a normalized
Error you can branch on — never a thrown exception. Success mirrors the API envelope, so
an HTTP 200 carrying "success: false" (a bulk failure) is reported as a failure.
- Observable by default. Each call opens a tracing span (HTTP method, static route
template, status code) and logs failures through ODC's logger; the built URL, bodies, and
auth header are never logged.
- Fully portable. No community dependencies and no server-side JavaScript.
How it works: the connection
An External Logic library is stateless and cannot read the app's Settings or Secrets, so
every action takes an i3XConnection input: BaseUrl (base URL including the /v1 path,
e.g. https://api.i3x.dev/v1), AuthHeaderName (e.g. Authorization or X-API-Key; empty
means no auth header is sent), and AuthHeaderValue (e.g. "Bearer your-token" or an
API key). Bind these from your app's own Settings and Secrets. The input is optional and
trailing: if omitted, the connector targets the public demo server https://api.i3x.dev/v1
with no auth — handy for a first smoke test only. Deserialize a JSON-suffixed (dynamic)
output field with JSONDeserialize when you need it typed, or bind it straight to a viewer.Try it out by searching "i3X Demo" on the forge to find the companion demo app!
How it works: error handling
Every action returns Success (Boolean) and Error (i3XError: Status, Title, Detail).
Error.Status is the connector's normalized outcome code (not the raw HTTP status), 0 when
there is no error. Read them together:
- Success True, Status 0 — clean success (a normal 2xx).
- Success True, Status 206 (Updates dropped) — data is valid, but the server dropped
updates from its staging queue, so your stream has a gap.
- Success False, Status 207 (Partial failure) — a bulk call where some items succeeded
and some failed; the per-item Results say which.
- Success False, Status 4xx or 5xx — the call failed; status, Title, Detail from the API.
- Success False, Status 0 (Communication error) — a transport failure with no HTTP
response at all.
Always check Success first; a failed bulk call still returns its Results list.
Actions
Every action takes its concept-specific inputs, then returns Success, Error, the primary
output(s), and the optional trailing i3XConnection.
Discovery and explore:
- i3X_GetInfo — server info and capabilities.
- i3X_GetNamespaces — lists all namespaces.
- i3X_GetObjectTypes — object types, optionally filtered by NamespaceUri.
- i3X_QueryObjectTypesById — object types by their element ids.
- i3X_GetRelationshipTypes — relationship types, optionally by NamespaceUri.
- i3X_QueryRelationshipTypesById — relationship types by their element ids.
Objects and values:
- i3X_GetObjects — objects, optionally by TypeElementId, IncludeMetadata, and Root.
- i3X_ListObjectsById — objects by their element ids, with IncludeMetadata.
- i3X_QueryRelatedObjects — objects related to the given element ids, optionally by
RelationshipType.
- i3X_GetObjectValues — current values for the given element ids to a MaxDepth.
- i3X_GetHistoricalValues — historical values over a StartTime/EndTime range to a MaxDepth.
Subscriptions (poll loop):
- i3X_CreateSubscription — creates a subscription for a ClientId, optional DisplayName.
- i3X_RegisterItems — registers monitored element ids on a subscription.
- i3X_Sync — polls for updates since LastSequenceNumber (negative to start). Returns
Updates and MaxSequenceNumber.
- i3X_ListSubscriptions — lists subscriptions for a ClientId.
- i3X_RemoveMonitoredItems — removes monitored element ids from a subscription.
- i3X_DeleteSubscriptions — deletes subscriptions.
Install and set up
You need an ODC tenant (Portal and Studio) and a reachable i3X v1 server with, if secured,
its auth header name and value. To try without a server, use the public demo
https://api.i3x.dev/v1.
1. In ODC Portal, open Forge, search for i3X Connector, open the asset, and select Install.
Choose the stage and confirm; it is added as an External Logic library.
2. In ODC Studio, use Add public element (or Manage Dependencies), find i3X Connector, and
select the actions and the output, i3XError, and i3XConnection structures you need.
3. In your app's Settings, add entries for the server URL and, if required, the auth header
name and value (store secrets as Secrets).
4. In the logic that calls i3X, create a local i3XConnection, set BaseUrl (include /v1), and
set AuthHeaderName/AuthHeaderValue when secured (leave empty otherwise). Pass this
i3XConnection on every action.
5. Verify: add i3X_GetNamespaces to a flow, run the app, and confirm Success is True and
Namespaces is populated. If not, read Error.Status/Title/Detail (see error handling).
End-to-end example: browse, read a value, then subscribe
1. Browse — call i3X_GetNamespaces, then i3X_GetObjects with IncludeMetadata True, to
populate a list of objects for the user to pick.
2. Read a value — for the selected object, call i3X_GetObjectValues with ElementIds set
to the selected id and MaxDepth 1, then bind the first value's ValueJSON to a viewer (or
deserialize it).
3. Subscribe for live updates — call i3X_CreateSubscription (keep the SubscriptionId),
then i3X_RegisterItems with the selected element ids. On a timer, call i3X_Sync with
LastSequenceNumber (negative the first time), render each update's ValueJSON, and store
the returned MaxSequenceNumber for the next tick. When done, call i3X_DeleteSubscriptions.
Because there is no SSE, live updates follow your timer's cadence — pick an interval that
fits your data's change rate and the platform's execution limits.
Scope and limitations
- 17 of 20 endpoints, v1 (Release). The two write endpoints (PUT /objects/value and
PUT /objects/history) and the SSE stream (POST /subscriptions/stream) are excluded; live
updates use the /sync poll loop.
- External Logic runtime limits apply. A call must finish within the platform's
execution-time and payload ceilings; narrow large i3X_GetObjects or history calls by
TypeElementId or time range.
- No built-in retry. Each action makes exactly one HTTP call; a transient failure returns
Success False rather than being retried. Handle retry — count, backoff, and idempotency —
in your ODC app.