Skip to main content
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.

Prerequisites

You need:
  • A finished frontend from the Build the Frontend guide
  • A finished backend from either the Node.js, C#, or PHP guide
  • A JTL ID (your login to the JTL ecosystem)
  • Access to an organization (tenant) in the Partner Portal (created automatically on first login if you don’t have one yet)
  • JTL-Wawi installed and running locally, with at least one product, and connected to JTL Cloud.
If you don’t have JTL Cloud set up yet, follow the step-by-step guide: Create a Developer Account.

What you’re Building

So far, the frontend and backend are only loosely connected. The frontend can render the app, and the backend can verify authentication tokens, but neither side is yet tied to a real merchant installation. This guide 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

Your app’s manifest declares how it integrates with JTL: the URLs it exposes and its ERP Cloud menu items. Create an app.json file in the project root:
Three of the URLs map directly to the three frontend routes you built: For the full field reference, see the App Manifest.
The example uses http://localhost:5173/.... When you deploy, replace these with your production domain. All URLs must be publicly reachable. localhost works only because the JTL App Shell runs in your browser, not on JTL’s servers.

2. Register your App

Register your app with the manifest you just created. Both paths produce the same app.
With your manifest saved as app.json in your project root, run the CLI:
The CLI signs you in with your JTL ID, lets you select a tenant, and registers the app from your app.json. It then writes your client ID and client secret to a .env file in your project.
Don’t commit your environment files. They contain your client secrets. The CLI writes them to paths that the template’s .gitignore already excludes, but verify this before pushing to a shared repository.

3. Update your Backend Credentials

Replace the placeholder values you set earlier with the real Client ID and Client Secret.
Open backend/.env and replace the placeholder values:
The 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):
Terminal 2 (frontend):
Opening 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

1

Open JTL Hub

Go to JTL Hub and log in.
2

Find your App

Navigate to the Manage apps menu and click the Apps in development tab. You should see your newly registered app.Discover apps
3

Install the App

Once the app is installed, you’ll see a success screen with the option to configure the app. Click the Configure app button to proceed to the next step.Install app on JTL
4

Complete the Setup

Click the Complete Setup button on your setup page. This triggers the full setup handshake between your app and JTL.Installation complete
The setup page now displays a valid tenant ID. This confirms that the session token was successfully verified through your backend and that the App Shell recognizes the installation as complete.

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.
Add the items route to backend/src/server.ts. Place it alongside the existing connect-tenant route:
The route reads the session token from the 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

Replace frontend/src/routes/_shell.erp.tsx with a version that fetches and renders the products:
On mount, the component requests a session token from the AppBridge, sends it to your backend as the 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. This completes the full handshake flow: the App Shell loads your frontend in an iframe, AppBridge provides a short-lived session token, your backend verifies the token and uses its access token to make a tenant-scoped request to the JTL Cloud API, and the response is returned to the browser.

Common Issues

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.
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.
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
Environment variables are only read at startup, so updates to .env require a restart.
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)
Once valid credentials are configured, this state resolves automatically.
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 your App

Publish to the App Store, or share your app privately with activation codes.