Bring everything together. Register your app with JTL to get real credentials, install it in the JTL Hub, then add an items endpoint to your backend and a product table to your frontend.Documentation Index
Fetch the complete documentation index at: https://developer.jtl-software.com/llms.txt
Use this file to discover all available pages before exploring further.
Prerequisites
You need:- ✅ A finished frontend from the Build the Frontend guide
- ✅ A finished backend from either the Node.js or C# guide
- ✅ JTL-Wawi installed and running locally, with at least one product, and connected to JTL Cloud.
What you’re Building
So far the frontend and backend exist in isolation. The frontend renders, the backend verifies tokens, but nothing connects them to a real merchant. This page closes the loop. By the end, opening the app from the ERP Cloud menu will show a table of real products from JTL-Wawi.1. Create the Manifest
The manifest tells JTL three things: how your app integrates technically, how it appears in the App Store, and where to send merchants when they install or open it. Createmanifest.json in the root of my-jtl-app (alongside the frontend and Backend folders):
capabilities map directly to the three frontend routes:
| Manifest field | Page in your code |
|---|---|
manifest.lifecycle.configurationUrl | _shell.setup.tsx |
manifest.capabilities.hub.appLauncher.redirectUrl | hub.tsx |
manifest.capabilities.erp.menuItems[].url | _shell.erp.tsx |
2. Register your App
Open the Partner Portal
Go to Partner Portal and log in.
Paste your Manifest
Replace the example manifest with the contents of your 
manifest.json file. Click Register app.
3. Update your Backend Credentials
Replace the placeholder values you set earlier with the real Client ID and Client Secret.- Node.js
- C#
Open The
backend/.env and replace the placeholder values:tsx watch command picks up source file changes automatically, but environment variables are read once at startup. Stop and restart the backend to pick up the new values.4. Run Both Processes
You need both the frontend and backend running. Open two terminals from the project root. Terminal 1 (backend):- Node.js
- C#
http://localhost:5173 directly in a browser will show a blank page or the placeholder error state. The app is meant to be rendered inside the JTL App Shell, which is what the next step covers.
5. Install the App in JTL Hub
Open JTL Hub
Go to JTL Hub and log in.
6. Add an Items Endpoint
The backend can now make tenant-scoped calls to the JTL Cloud API. The next step is adding an endpoint that fetches products from JTL-Wawi via GraphQL.Every API request that needs to access tenant data needs an
X-Tenant-ID header. The backend extracts this value from the verified session token payload. See the Session Token Payload reference for details.- Node.js
- C#
Add the items route to The route reads the session token from the
backend/src/server.ts. Place it alongside the existing connect-tenant route:Authorization header (sent by the frontend as a Bearer token), verifies it to extract the tenant ID, fetches a fresh access token, and forwards the GraphQL response back to the frontend as-is.7. Display Items in the ERP Page
Replacefrontend/src/routes/_shell.erp.tsx with a version that fetches and renders the products:
Authorization header, and renders the returned products in a table. The session token round-trip means the backend always knows which tenant the request belongs to, even though the frontend only ever holds a short-lived signed token.
A sample response from the GraphQL API looks like this:
8. Open the App from the ERP Menu
In the ERP Cloud, navigate to the App menu and find the My JTL App menu item that the manifest registered. Clicking it loads/erp inside the App Shell.
You should see a header reading My JTL Cloud App, the connected tenant ID, and a table listing the first ten products from your JTL-Wawi instance.
That’s the full handshake: the App Shell loads your frontend in an iframe, AppBridge supplies a short-lived session token, the backend verifies the token and uses its own access token to make a tenant-scoped call to the JTL Cloud API, and the result lands in the browser.
Common Issues
'Cannot find module ./jtl-auth.js' or similar import error
'Cannot find module ./jtl-auth.js' or similar import error
This error usually means TypeScript and Node are resolving modules differently.With
"type": "module" in package.json and "module": "NodeNext" in tsconfig.json, Node expects ES module imports to include file extensions. Even if your source file is jtl-auth.ts, the import must use ./jtl-auth.js.TypeScript resolves this correctly during development, and Node finds the compiled .js file at runtime.If you prefer not to use .js extensions, switch to "module": "CommonJS" in tsconfig.json and remove "type": "module" from package.json.'CLIENT_ID and CLIENT_SECRET must be defined in .env'
'CLIENT_ID and CLIENT_SECRET must be defined in .env'
This means the backend started without loading your environment variables.The most common cause is running Node without the
--env-file=.env flag. In that case, the .env file exists but is never read.The dev and start scripts already include this flag. If you’re running the server manually, add it back or use npm run dev.Also confirm that the .env file is inside the backend/ directory. The path is resolved relative to where Node is executed.'Failed to fetch JWT (401)' from the auth endpoint
'Failed to fetch JWT (401)' from the auth endpoint
A 401 from the auth endpoint means the credentials are not valid.If you are still using placeholder values, this is expected. Real credentials are provided after registering your app in the Partner Portal.If you have already registered:
- check for typos or extra spaces in
.env - restart the dev server after making changes
.env require a restart.The frontend can reach the backend but still shows 'Waiting for backend'
The frontend can reach the backend but still shows 'Waiting for backend'
This usually means the backend responded, but not with a successful (2xx) response.The frontend treats any non-2xx response as an error and shows the placeholder state.Open the browser console and inspect the
/api/connect-tenant response:- a 401 is expected at this stage without real credentials
- a 500 or network error indicates a backend issue (check server logs)
CORS error in the browser console
CORS error in the browser console
This means the browser blocked the response due to an origin mismatch.The backend allows requests from
http://localhost:5173, which is the default Vite dev server port. If Vite runs on a different port (for example, 5174), the request will be rejected.Update the origin in server.ts to match the actual port, or restart Vite on 5173.If you’re using the Vite dev proxy for /api/*, CORS should not appear. Seeing this error usually means the request is being made directly to the backend instead of going through the proxy.What’s Next?
You’ve built a working JTL Cloud App from scratch: a frontend that runs inside the JTL App Shell, a backend that verifies session tokens and proxies requests to the JTL Cloud API, and a real connection to a tenant pulling live product data. Where to go from here:Test your App
Validate your app in the sandbox with test data.
Using Platform APIs
Use the JTL-Wawi REST and GraphQL APIs, handle responses, and work with tenant-scoped data.
GraphQL Playground
Try queries and mutations interactively against your ERP instance.
App Shell & UI
Learn how to integrate deeper with the JTL UI and App Shell.
Submit to the App Store
Deploy your app and publish it for merchants.


