Skip to main content

About intents

  • Explanation
  • 7-min read

Intents are the platform mechanism for context-aware cross-app navigation. When a user interacts with a resource—such as an entity, a metric, or a DQL query—and wants to do something with it in another app, an intent carries the relevant context from the source app to the target app through the AppShell.

Every Dynatrace app runs inside an iframe hosted by the AppShell. Regular links can navigate between apps, but they can't carry structured context with them. Intents solve this: they let your app pass data—entity IDs, timeframes, queries—to another app in a way the platform can coordinate, so the target app knows what to show.

How to deliver an intent

Intents reach users through different mechanisms, ordered by specificity. Choosing the right one determines whether users land where they need to go or get stuck picking from a list.

Explicit intents

Use an explicit intent when your app knows exactly where to send the user. The user clicks, the target app opens with all context applied. No dialog, no choices.

An IntentButton menu opened on a host entity, with the explicit intent "View host" (Hosts app icon highlighted) as the primary action, "Investigate" as a secondary action, and "Open with..." as the fallback entry at the bottom

This is the most common pattern. It powers primary actions in intent menus and the left side of the IntentButton split button, the primary action such as View in Hosts or Open in Notebooks.

  • When to use—the target app and action are obvious for the resource type. You're connecting a host entity to the Hosts app, a trace ID to Distributed Traces, a DQL query to Notebooks. The 80/20 case.

  • How it works—your app calls sendIntent() with a recommendedAppId and recommendedIntentId, or uses the <IntentButton> with those options set. The AppShell skips the Open with... dialog and opens the target app directly.

<IntentButton
payload={{ 'dt.entity.host': 'HOST-F66B242CD3A5E38E' }}
options={{
recommendedAppId: 'dynatrace.classic.hosts',
recommendedIntentId: 'view-host',
}}
>
View in Hosts
</IntentButton>
Tip

Do not hardcode your own entity-to-app mappings. Use the platform's recommended intents mapping to determine which target app and intent ID to use for a given resource type. This keeps navigation consistent across the platform.

Open with... dialog (fallback)

The Open with... dialog is the fallback that surfaces every app capable of handling the current intent payload. It appears when no explicit intent is configured or when the user deliberately wants to see all options.

  • When it appears:

    • Your app calls sendIntent() without specifying a recommendedAppId.
    • The user clicks the three-dot menu on an IntentButton and selects the Open with... option.
    • The recommendedAppId doesn't exist or can't handle the payload.
  • What to know:

    • Always provide this as a fallback. Do not suppress it.
    • In the IntentButton split button, the Open with... option sits at the bottom of the three-dot menu, after explicit intents and add-ons.
    • The label must be exactly Open with... with no variations.

The IntentButton

The IntentButton is a split button from the Strato Design System that implements all three mechanisms in one component. The left side triggers the primary explicit intent directly, for example, View in Hosts. The right side opens a menu with additional explicit intents, add-ons, and Open with... as the last option.

Three IntentButton configurations: (1) explicit intent with alternative intents and &quot;Open with...&quot; fallback in the menu, (2) explicit intent with only &quot;Open with...&quot; as alternative, (3) explicit intent without any menu or fallback

Use IntentButton for straightforward cases. Use sendIntent() from @dynatrace-sdk/navigation when you need more control over the trigger. For example, responding to a table row click or a custom interaction.

Without central coordination, each app builds its own mapping from resource types to target apps. This creates inconsistency: clicking a host ID in one app opens the Hosts app, while clicking the same ID in another opens something different.

Recommended intents solve this with a centrally managed mapping from resource types to recommended target apps. Instead of hardcoding recommendedAppId: 'dynatrace.classic.hosts' in your source code, you query the recommended intents list to get the right target for dt.entity.host.

This ensures users always land in the same destination for the same resource type, regardless of which app they started from.

How intents work under the hood

Lifecycle

  1. Source app sends an intent—your app calls sendIntent(payload, options) or renders an <IntentButton>. The communication uses the window.postMessage API to reach the AppShell.

  2. AppShell resolves the target—if a recommendedAppId and recommendedIntentId are provided and valid, the AppShell opens that app directly. Otherwise, it finds all installed apps whose intent declarations match the payload properties and shows the "Open with..." dialog.

  3. AppShell launches the target app—the target app opens in an iframe (full page for navigation intents, modal for add-ons). The intent payload is passed as URL query arguments.

  4. Target app handles the intent—the target app calls getIntent() to read the payload and perform the requested action.

Intent matching

The AppShell matches an intent payload against every installed app's intent declarations in app.config.json. A match occurs when an app declares intent properties that the source app's payload satisfies:

  • If the target declares dt.entity.host as required: true, the source must include dt.entity.host in the payload.
  • Extra properties in the payload that the target does not declare are stripped before delivery.
  • Use keyProperties in sendIntent() to narrow matching when your payload contains multiple properties.

Payload structure

An intent payload is a set of key-value pairs carrying the context to share:

import { IntentPayload } from '@dynatrace-sdk/navigation';

const intent: IntentPayload = {
'dt.entity.host': 'HOST-F66B242CD3A5E38E',
'dt.timeframe': { from: 'now-2h', to: 'now' },
};

Common payload properties include entity IDs (dt.entity.*), DQL queries (dt.query), and timeframes (dt.timeframe). The target app defines which properties it accepts and requires in its intent declaration.

Limitations

  • One-way payload delivery—the initial intent payload is passed via URL query arguments when the target app loads. For two-way communication, use intents-with-response, where the target add-on can pass data back asynchronously.
  • Keep payloads concise—while the previous URL length limitation has been fixed, it's still best practice to pass identifiers and queries rather than large data blobs.

Security context

Every Dynatrace app runs inside its own cross-origin iframe in the AppShell. API calls from any app, whether it opened via a regular intent or as a modal add-on, run under that app's security context, not the calling app's. Each app uses its own identity, OAuth scopes, and permissions.

Cross-origin iFrames comply with the AppShell's Content Security Policy rules.

Context sharing

When a target app opens through an intent, it can access:

  • Platform context shared between all apps: timeframe, segment, tenant info
  • Intent payload—the specific data passed by the source app via the intent
  • The target app does not have access to the source app's internal state beyond what is in the intent payload.

This applies equally to full-page navigation intents and modal add-ons.

Intents as public APIs

Intents behave like public APIs. An app's intent types define its public contract. Consuming apps can rely on a specific intent type always accepting the same payload shape.

  • Intent types have owners, just like API owners own API types.
  • The intent type name and its required properties form a stable interface.
  • Breaking changes to an intent's required properties are breaking API changes and require a deprecation process to phase out safely.

Choose the right mechanism

ScenarioUseWhy
User clicks a host entity and should open in the relevant appRecommended intentResolve the target app dynamically, do not hardcode the destination
App needs to support a resource type it cannot predict upfrontOpen with...Let the platform's matching find the right targets

Intent naming guidelines

Every intent has two user-facing identifiers, which affect discoverability and clarity:

  • The intent ID—which is the key in app.config
  • The description—shown on hover in the intent menu

Intent IDs

The intent ID is the key you declare in your app configuration under app.intents. It should be short, lowercase, and describe the action your app performs.

Use kebab-case for new intent IDs:

DoDon't
view-hostViewHost
share-chartshareChart
create-alertalert
view-host-v2view_host_deprecated

Some older intents use snake_case as in view_host or view_service. Both formats work, but kebab-case is the preferred convention for new intents.

Follow these rules:

  • Use lowercase letters, underscores, and hyphens only. No spaces, no camelCase.
  • Start with a verb that describes the action. For example, view, create, edit, share, add, or analyze.
  • Don't include the app name. The intent ID should describe the action, not the destination. The platform maps actions to apps.
  • Don't include deprecated, new, or old in the intent ID. Use a -v2 suffix for versioned replacements.
  • Keep it short. The ID appears in URLs and API calls.

Descriptions

The description field is a short sentence that appears as the label in the intent context menu and in the Open with... dialog. It tells the user what will happen.

Follow this format: [action verb] + [object]. See the following table:

GoodBadWhy
"View the host""Host"Names the destination, not the action
"Add to dashboard""Dashboard"Names the destination, not the action
"Analyze query results""Notebooks"Users choose by action, not app name
"Create alert""Alerting"Gerund or category name, not an action

The description should complete the sentence This will.... If This will Host doesn't make sense, rewrite it.

Next steps

Still have questions?
Find answers in the Dynatrace Community