# Ignition 8.3: Guidance/example on end-to-end WebUI / Gateway interaction

**URL:** <https://forum.inductiveautomation.com/t/ignition-8-3-guidance-example-on-end-to-end-webui-gateway-interaction/113054>\
**Category:** Module Development\
**Tags:** ignition83\
**Created:** [January 2, 2026, 4:44pm UTC](https://forum.inductiveautomation.com/t/ignition-8-3-guidance-example-on-end-to-end-webui-gateway-interaction/113054 "2026-01-02T16:44:12Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![mpderbec](https://avatars.discourse-cdn.com/v4/letter/m/bbe5ce/32.png) [@mpderbec](https://forum.inductiveautomation.com/u/mpderbec)\
**Post date:** [January 2, 2026, 4:44pm UTC](https://forum.inductiveautomation.com/t/ignition-8-3-guidance-example-on-end-to-end-webui-gateway-interaction/113054/1 "2026-01-02T16:44:12Z")

</div>

I’ve been able to update my company’s module to function correctly under Ignition 8.3 except for the configuration UI. In other words, I’m successfully migrating the configuration from the old SQLite record stuff to the new JSON-based resource stuff. And I can edit those JSON files as a “brute force” method of configuring my module. But this obviously isn’t good enough for our customers.

I have the [ignition-sdk-examples/webui-webpage at ignition-8.3 · inductiveautomation/ignition-sdk-examples · GitHub](https://github.com/inductiveautomation/ignition-sdk-examples/tree/ignition-8.3/webui-webpage) example integrated into my module.

The missing piece is that I don’t see any example (or even mention) of how to facilitate communication between the web ui and the gateway. In my case, the need is very simple: Read and write a relatively small JSON blob of configuration from the web UI.

Do I need to expose a new API from the Gateway somehow?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [January 2, 2026, 4:59pm UTC](https://forum.inductiveautomation.com/t/ignition-8-3-guidance-example-on-end-to-end-webui-gateway-interaction/113054/2 "2026-01-02T16:59:53Z")

</div>

> [@mpderbec](#):
>
> Do I need to expose a new API from the Gateway somehow?

Yes. That example includes custom APIs for the example.

For parts that write back, see the discussion of the CSRF token in this topic:

> [@8.3 File Upload route permissions issue](https://forum.inductiveautomation.com/t/8-3-file-upload-route-permissions-issue/112007/8):
>
> I banged my head on this. Wrong approach, apparently. My IDE is happy with this: const csrf = useSelector((store: ReduxRootState) =\> store.userSession.csrfToken); Will test tomorrow. My brain is fried for the day.

---

<div class="post-metadata">

**Author:** ![mpderbec](https://avatars.discourse-cdn.com/v4/letter/m/bbe5ce/32.png) [@mpderbec](https://forum.inductiveautomation.com/u/mpderbec)\
**Post date:** [January 2, 2026, 5:22pm UTC](https://forum.inductiveautomation.com/t/ignition-8-3-guidance-example-on-end-to-end-webui-gateway-interaction/113054/3 "2026-01-02T17:22:47Z")

</div>

> [@pturmel](#):
>
> That example includes custom APIs for the example.

I must be blind… I don’t see any Java code in the webui example that looks like an API, nor do I see any Typescript code that does a fetch.

The only thing I see that is somewhat related to a backend API is this placeholder:

> <https://github.com/inductiveautomation/ignition-sdk-examples/blob/92813eb6e9372704926eebca870e3e48b607e487/webui-webpage/web-ui/src/pages/HelloIgnition/HelloIgnition.service.ts>

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [January 2, 2026, 5:33pm UTC](https://forum.inductiveautomation.com/t/ignition-8-3-guidance-example-on-end-to-end-webui-gateway-interaction/113054/4 "2026-01-02T17:33:47Z")

</div>

Hmmm. You're right. The gateway hook's `.mountRouteHandlers()` method is empty.

That's where you need to register your APIs and connect their implementations.

This was where I got the inspiration for my drivers:

> [@Module Development Example Question](https://forum.inductiveautomation.com/t/module-development-example-question/107639/3):
>
> Contributing to the central OpenAPI definition from a module is very easy. Just override/implement mountRouteHandlers in your module's gateway hook. The Route class we provide has an OpenAPI builder attached to it you can use to build up a response. @Override public void mountRouteHandlers(RouteGroup routes) { routes.newRoute("/api/v1/session/:sessionId/pages") .type(RouteGroup.TYPE\_JSON) .method(HttpMethod.GET) .requirePermission(PermissionT…

(I didn't register them in the OpenAPI docs.)

---

<div class="post-metadata">

**Author:** ![David\_Stone](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/david_stone/32/79749_2.png) [@David\_Stone](https://forum.inductiveautomation.com/u/David_Stone)\
**Post date:** [January 2, 2026, 7:19pm UTC](https://forum.inductiveautomation.com/t/ignition-8-3-guidance-example-on-end-to-end-webui-gateway-interaction/113054/5 "2026-01-02T19:19:54Z")

</div>

> [@MQTT Vanilla Client](https://forum.inductiveautomation.com/t/mqtt-vanilla-client/105873/3):
>
> I have now made a version of this for 8.3, with a functional gateway webpage to set the settings for the client. This builds fully on Maven, so it is also a demo of the full React webpack experience running on Maven, not Gradle which is the option on the sdk examples.

This is a working config settings example that I wrote, it’s not perfect but it’s fully functional with the API written inside.

---

<div class="post-metadata">

**Author:** ![mpderbec](https://avatars.discourse-cdn.com/v4/letter/m/bbe5ce/32.png) [@mpderbec](https://forum.inductiveautomation.com/u/mpderbec)\
**Post date:** [January 3, 2026, 2:08pm UTC](https://forum.inductiveautomation.com/t/ignition-8-3-guidance-example-on-end-to-end-webui-gateway-interaction/113054/6 "2026-01-03T14:08:31Z")

</div>

OK thanks for the input, I was able to get everything going.

@pturmel’s pointer to [8.3 File Upload route permissions issue - #8 by pturmel](https://forum.inductiveautomation.com/t/8-3-file-upload-route-permissions-issue/112007/8) was really key for posting – i.e., whenever you use `PermissionType.WRITE`. I was able to access the `ReduxRootState` exactly as he did. Otherwise you will get a non-descript 403 Forbidden error.

For those following along and looking for more examples of what works, here’s what my `mountRouteHandlers()` looked like in my GatewayHook class:

```python
    public void mountRouteHandlers(RouteGroup routes) {
        routes.newRoute("/settings")
                .type(RouteGroup.TYPE_JSON)
                .handler(this::getSettingsJson)
                .method(HttpMethod.GET)
                .requirePermission(PermissionType.READ)
                .mount();

        routes.newRoute("/settings")
                .type(RouteGroup.TYPE_JSON)
                .handler(this::putSettingsJson)
                .method(HttpMethod.PUT)
                .requirePermission(PermissionType.WRITE)
                .mount();
    }

```

and here’s what my `Settings.service.ts` looks like on the frontend:

```python
export const getSettings = async (): Promise<SeeqSettings> => {
  const response = await fetch("/data/seeq/settings", {
    method: "GET",
    headers: {
      Accept: "application/json",
    },
  });

  if (!response.ok) {
    throw new Error(`Failed to fetch settings: ${response.statusText}`);
  }

  const payload: SeeqSettingsPayload = await response.json();

  // Flatten the structure for UI convenience
  return {
    enabled: payload.enabled,
    ...payload.config,
  };
};

export const updateSettings = async (
  settings: Partial<SeeqSettings>,
  csrfToken: string
): Promise<UpdateSettingsResponse> => {
  // Extract enabled and wrap the rest in config
  const { enabled, ...config } = settings;

  const payload: Partial<SeeqSettingsPayload> = {
    enabled,
    config: config as IgnitionModuleConfigurationV2,
  };

  const response = await fetch("/data/seeq/settings", {
    method: "PUT",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
      "X-CSRF-Token": csrfToken,
    },
    body: JSON.stringify(payload),
  });

  const data: UpdateSettingsResponse = await response.json();

  // Check if the operation was successful
  if (!data.success) {
    throw new Error(data.message || "Failed to update settings");
  }

  return data;
};

```

where `csrfToken` was retrieved via this code:

```python
  const csrfToken = useSelector(
    (state: ReduxRootState) => state.userSession.csrfToken || ""
  );

```

---

<div class="post-metadata">

**Author:** ![David\_Stone](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/david_stone/32/79749_2.png) [@David\_Stone](https://forum.inductiveautomation.com/u/David_Stone)\
**Post date:** [January 4, 2026, 9:21am UTC](https://forum.inductiveautomation.com/t/ignition-8-3-guidance-example-on-end-to-end-webui-gateway-interaction/113054/7 "2026-01-04T09:21:59Z")

</div>

Thats actually one thing I had sidestepped in a bit of a hack-fix way, I just did it with an `OPEN_ROUTE` access control strategy. Not exactly secure, but functional. Thanks to your prompting here, I have updated my code to use the token and the READ/WRITE permissions instead, which is working nicely.
