Skip to main content

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.

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.
C# samples target .NET 8 with implicit usings enabled. PHP samples target PHP 8.1+ and use Slim 4 for HTTP routing. Required Composer packages: slim/slim, slim/psr7. TypeScript samples use Express.

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": {
		"configurationUrl": "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
configurationUrlMerchant 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 configurationUrl 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 is called when the JTL Platform establishes the connection with your app. Use this hook for non-authoritative side-effect work: logging, sending a welcome email, and queuing background onboarding jobs.
// routes/lifecycle.ts
import { Request, Response } from 'express';

app.post('/api/lifecycle/connect', async (req: Request, res: Response) => {
  try {
    const { tenantId } = req.body;

    console.log('Tenant connected:', tenantId);

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

    return res.status(200).json({ success: true });
  } catch (error) {
    console.error('Connect hook failed:', error);
    return res.status(500).json({ error: 'Failed to process connection' });
  }
});

Disconnect Hook

The disconnectUrl receives a POST request when a merchant uninstalls your app. Use it to mark the tenant inactive, invalidate cached sessions, and schedule any longer-running cleanup. The handler structure is nearly identical to the connect hook, including request parsing and error handling. The main difference is the cleanup logic performed after disconnection.
// routes/lifecycle.ts
app.post('/api/lifecycle/disconnect', async (req: Request, res: Response) => {
  try {
    const { tenantId } = req.body;

    console.log('Tenant disconnected:', tenantId);

    // Mark the tenant as inactive (soft-delete; see best practices below)
    // await db.tenants.update({
    //   where: { tenantId },
    //   data: { status: 'disconnected', disconnectedAt: new Date() },
    // });

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

    return res.status(200).json({ success: true });
  } catch (error) {
    console.error('Disconnect hook failed:', error);
    return res.status(500).json({ error: 'Failed to process disconnection' });
  }
});

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.