Skip to main content
Cloud Apps receive events through two channels:
ChannelTransportDirectionUse case
AppBridge events (coming soon)iframe postMessageBidirectional (app ↔ host)Real-time UI interactions while the app is open
Lifecycle hooksHTTP POST to your serverPlatform → your backendTenant connected or disconnected
AppBridge events are session-scoped: they only fire while a merchant has your app open in the ERP Cloud. Lifecycle hooks fire regardless of whether the merchant is using your app. For a conceptual overview of all three JTL event systems (including SCX polling events for marketplace channels), see the Webhooks & Events page.

AppBridge Events

In development: AppBridge publish/subscribe events are in development and not yet available. This section will be updated when the API ships.
AppBridge events will let your app and the host environment exchange messages in real time while the merchant has your app open. Communication is asynchronous and non-blocking, with no HTTP requests or polling involved. The system is designed around two capabilities:
  • Publishing events notifies the host that your app completed an action (for example, a generated product description is ready to insert).
  • Subscribing to events lets your app react when the host’s context changes (for example, the merchant navigates to a different customer record).
Topics will follow a resource:action naming convention so intent is clear from the name. Event payloads are expected to be small (IDs and changed values, not full objects) since the host or your app can always fetch details from the API if needed. For the conceptual overview of how AppBridge events fit alongside lifecycle hooks and SCX polling, see Webhooks & Events.

Lifecycle Hooks

Lifecycle hooks are HTTP endpoints on your server that JTL calls when a merchant installs or uninstalls your app. Unlike AppBridge events, these are server-to-server requests that happen regardless of whether the merchant has your app open. You define these URLs in your manifest:
{
	"lifecycle": {
		"setupUrl": "https://your-app.example.com/setup",
		"connectUrl": "https://your-app.example.com/api/lifecycle/connect",
		"disconnectUrl": "https://your-app.example.com/api/lifecycle/disconnect"
	}
}
Each hook serves a distinct role in the tenant lifecycle:
HookWhen it firesYour responsibility
setupUrlMerchant clicks Install in JTL HubDisplay onboarding UI, complete the AppBridge handshake
connectUrlAfter setup completesStore the tenant connection in your database
disconnectUrlMerchant uninstalls your appClean up tenant data, revoke sessions

The Setup Handshake

The setupUrl is loaded inside an iframe in the JTL Hub. This is your app’s onboarding screen. The typical flow is:
  1. The AppBridge initializes and provides a session token
  2. Your frontend sends the token to your backend for verification
  3. Your backend verifies the token and stores the tenant connection
  4. Your frontend calls appBridge.method.call('setupCompleted') to signal that setup is done
For the full setup implementation, see the From Scratch quickstart.

Connect Hook

The connectUrl receives a POST request from the JTL Platform after setup completes. Use it to finalize the tenant connection on your server.
// app/api/lifecycle/connect/route.ts

import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();

    console.log("Tenant connected:", body);

    // Store the tenant connection in your database
    // await db.tenants.create({
    //   tenantId: body.tenantId,
    //   connectedAt: new Date(),
    //   status: "active",
    // });

    return NextResponse.json({ success: true }, { status: 200 });
  } catch (error) {
    console.error("Connect hook failed:", error);
    return NextResponse.json(
      { error: "Failed to process connection" },
      { status: 500 }
    );
  }
}
What this does: Receives the connection notification from JTL, logs the tenant details, and returns a 200 response. In production, you would store the tenant connection in your database so your app knows which merchants have it installed.

Disconnect Hook

The disconnectUrl receives a POST request when a merchant uninstalls your app. Use it to clean up any data or sessions associated with that tenant.
// app/api/lifecycle/disconnect/route.ts

import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();

    console.log("Tenant disconnected:", body);

    // Clean up tenant data
    // await db.tenants.update({
    //   where: { tenantId: body.tenantId },
    //   data: { status: "disconnected", disconnectedAt: new Date() },
    // });

    // Revoke any cached tokens or sessions for this tenant
    // await cache.invalidate(`tenant:${body.tenantId}`);

    return NextResponse.json({ success: true }, { status: 200 });
  } catch (error) {
    console.error("Disconnect hook failed:", error);
    return NextResponse.json(
      { error: "Failed to process disconnection" },
      { status: 500 }
    );
  }
}
What this does: Receives the disconnection notification, marks the tenant as disconnected in your database, and clears any cached data or sessions. Return a 200 even if there’s nothing to clean up. Failing to respond can cause the uninstall process to hang.

Lifecycle Hook Best Practices

Respond quickly. Return a 200 as fast as possible. If you need to do heavy processing (e.g., deleting large amounts of data), acknowledge the request immediately and handle cleanup asynchronously. Make handlers idempotent. The platform may retry a webhook if it doesn’t receive a response. Your handler should produce the same result whether it runs once or multiple times for the same event. Log everything. Lifecycle events are critical for debugging tenant onboarding issues. Log the full payload and any errors. Don’t delete data immediately on disconnect. Consider marking the tenant as inactive instead of deleting their data. This allows for a grace period in case the merchant re-installs your app, and helps with debugging.

Combining Events and Hooks

In a typical Cloud App, lifecycle hooks and AppBridge events work together to cover the full tenant lifecycle: Lifecycle hooks handle the start and end of the tenant lifecycle (install and uninstall). AppBridge events will handle everything in between while the merchant is actively using your app, once the publish/subscribe API ships.

What’s Next

Best Practices

Production patterns for error handling, security, and performance.

Webhooks (Essentials)

Conceptual overview of all three JTL event systems including SCX polling.

App Shell & UI Integration

Full AppBridge API reference with all methods and events.

Authentication & Login

Token verification for securing your lifecycle endpoints.