# Introduction Source: https://docs.dialect.to/alerts/alerts-and-monitoring/index Before you can send programmatic alerts to your users, you need to detect when events worth notifying about actually happen. This section covers different strategies for monitoring and detecting events in your application. ## Getting Started 1. **[Register Your App](/alerts/setup/register-app)** - Set up your application in Dialect 2. **[Choose your monitoring approach](#choose-your-path)** - Pick the path that fits your current setup 3. **[Send Alerts](/alerts/send)** - Learn how to send notifications using our API, SDK, or Dashboard ## Choose Your Path ### I Need Event Detection You don't have event monitoring in place yet and need a complete solution. Dialect provides open-source monitoring tooling (Dialect Monitor) that can detect on-chain and off-chain events automatically. This is ideal when you want a ready-made solution for common blockchain events. **Best for:** On-chain events, DeFi protocols, NFT marketplaces, standard blockchain monitoring needs, teams without existing monitoring infrastructure. [Learn how to set up Dialect Monitor →](/alerts/alerts-and-monitoring/setup-event-detection) ### I Have Event Detection You already have event detection systems in your backend (webhooks, database triggers, custom monitoring, existing alerting systems) and want to integrate Dialect's alert sending directly into your existing workflows using our SDK or API. **Best for:** Custom business logic, existing backend systems, complex event patterns, off-chain events, teams with established monitoring infrastructure. [Learn how to integrate with existing systems →](/alerts/alerts-and-monitoring/integrate-existing-system) ## What You'll Find Here * **[Setup Event Detection](/alerts/alerts-and-monitoring/setup-event-detection)** - Complete guide to our monitoring tooling * **[Integrate Existing System](/alerts/alerts-and-monitoring/integrate-existing-system)** - Integrate with your backend * Implementation guides and examples # Integrate with Existing Systems Source: https://docs.dialect.to/alerts/alerts-and-monitoring/integrate-existing-system Integrate Dialect's alert sending directly into your existing workflows and event detection systems If you already have event detection in your backend (webhooks, database triggers, custom monitoring, existing alerting systems), you can integrate Dialect's alert sending directly into your existing workflows. This approach leverages your current infrastructure while adding Dialect's multi-channel notification capabilities. **When to Use This Approach** * ✅ You have existing event detection systems * ✅ You have custom business logic for notifications * ✅ You prefer to control when and how events are detected * ✅ Your events are primarily off-chain or custom patterns * ✅ You want to integrate with existing backend services **Alternative:** If you need event detection, check out [Dialect Monitor](/alerts/alerts-and-monitoring/setup-event-detection) for automated monitoring solutions. ## Integration Overview The integration process involves two main steps: 1. **Configure Dialect SDK/API** in your backend services 2. **Send notifications** at the point of events in your application ```mermaid theme={null} flowchart LR A["Your Backend
Event"] --> B["Event
Handler"] --> C["Dialect
SDK/API"] --> D["Multi-Channel
Delivery"] --> E["User
Devices"] style A fill:#e3f2fd,stroke:#1976d2,stroke-width:3px style B fill:#e3f2fd,stroke:#1976d2,stroke-width:3px style C fill:#e3f2fd,stroke:#1976d2,stroke-width:3px style D fill:#e3f2fd,stroke:#1976d2,stroke-width:3px style E fill:#e3f2fd,stroke:#1976d2,stroke-width:3px ``` ## Choose Your Integration Method ### Option 1: TypeScript SDK (Recommended) Best for Node.js/TypeScript backends. Provides type safety and simplified API. **Setup & Configuration:** Follow our complete [SDK Setup & Configuration guide](/alerts/send/sdk/setup-configuration) for installation, app registration, and initialization. **Send Messages:** Use our [SDK Send Messages guide](/alerts/send/sdk/send-messages) for comprehensive examples including: * Single user notifications * Batch notifications to multiple users * Channel-specific delivery (email, Telegram, push) * Interactive notifications with action buttons * Rich content and formatting ### Option 2: REST API Best for non-JavaScript backends or custom integrations. **Setup & Configuration:** Follow our complete [API Authentication guide](/alerts/send/api/authentication) for API key setup and request authentication. **Send Messages:** Use our [API Send Messages guide](/alerts/send/api/send-messages) for comprehensive examples including: * Individual and batch messaging * Channel targeting * Rich HTML content for emails * Interactive notifications with actions ## Real-World Integration Examples **[Realms Governance API](https://github.com/dialectlabs/governance-api)** * **Use case**: Powers notifications for post and comment upvotes and replies on [Realms Discover](https://app.realms.today/discover) * **Implementation**: Direct backend integration using Dialect SDK within NestJS architecture * **Features**: Event-driven notifications from existing services, custom business logic integration **[Realms Monitoring Service](https://github.com/dialectlabs/realms-monitoring-service)** * **Use case**: DAO proposal and governance event monitoring * **Implementation**: Dialect Monitor integration for automated event detection * **Features**: Proposal tracking, voting reminders, and governance event notifications **[Jet Protocol](https://github.com/dialectlabs/jet-monitoring-service)** * **Use case**: Liquidation warnings for DeFi lending positions * **Implementation**: Monitoring service with backend integration patterns * **Features**: Threshold-based notifications and risk management alerts **[Marinade Finance](https://github.com/dialectlabs/marinade-monitoring-service)** * **Use case**: Staking protocol notifications and updates * **Implementation**: Backend integration with broadcast messaging features * **Features**: Multi-user notifications and staking event detection **[Saber](https://github.com/dialectlabs/saber-monitoring-service)** * **Use case**: Trading and AMM protocol notifications * **Implementation**: Push-type data sources with multi-channel delivery * **Features**: Real-time trading alerts and cross-platform integration **[Investin](https://github.com/dialectlabs/investin-monitoring-service)** * **Use case**: Investment platform with multiple notification types * **Implementation**: Complex backend with various notification triggers * **Features**: Portfolio alerts, investment opportunities, and user engagement # Setup Monitoring Source: https://docs.dialect.to/alerts/alerts-and-monitoring/setup-event-detection Use Dialect's open-source monitor library to detect events and turn them into targeted notifications with sophisticated data-stream processing The monitor library is an open-source TypeScript package that makes it easy to extract and transform data streams into targeted, timely smart messages with a rich, high-level API for unbounded data-stream processing. ## Key Features Data-stream processing features include: * **Windowing** - fixed size, fixed time, fixed size sliding * **Aggregation** - average, min, max * **Thresholding** - rising edge, falling edge * **Rate limiting** ## Getting Started ### Prerequisites Before building your monitor, you'll need: 1. A Dialect SDK instance configured with your dApp's wallet 2. A defined data type that represents what you want to monitor ### Basic Setup ```typescript theme={null} import { Monitor, Monitors, Pipelines, ResourceId, SourceData } from "@dialectlabs/monitor"; import { Duration } from "luxon"; import { Dialect, DialectCloudEnvironment, DialectSdk } from "@dialectlabs/sdk"; import { Solana, SolanaSdkFactory, NodeDialectSolanaWalletAdapter, } from "@dialectlabs/blockchain-sdk-solana"; // 1. Create Dialect SDK const environment: DialectCloudEnvironment = "development"; const dialectSolanaSdk: DialectSdk = Dialect.sdk( { environment, }, SolanaSdkFactory.create({ // IMPORTANT: Set DIALECT_SDK_CREDENTIALS environment variable // to your dapp's Solana messaging wallet keypair e.g. [170,23, . . . ,300] wallet: NodeDialectSolanaWalletAdapter.create(), }) ); // 2. Define your data type type YourDataType = { cratio: number; healthRatio: number; resourceId: ResourceId; }; ``` ## Building Your Monitor ### 1. Initialize the Monitor Builder ```typescript theme={null} const monitor: Monitor = Monitors.builder({ sdk: dialectSolanaSdk, subscribersCacheTTL: Duration.fromObject({ seconds: 5 }), }) .defineDataSource() ``` ### 2. Supply Data (Poll or Push) Use this when you want to regularly check for data changes: ```typescript theme={null} .poll((subscribers: ResourceId[]) => { const sourceData: SourceData[] = subscribers.map( (resourceId) => ({ data: { cratio: Math.random(), // Replace with your actual data collection healthRatio: Math.random(), resourceId, }, groupingKey: resourceId.toString(), }) ); return Promise.resolve(sourceData); }, Duration.fromObject({ seconds: 3 })) // Poll every 3 seconds ``` Use this when you have event-driven data (webhooks, database triggers, etc.). See the [push example](https://github.com/dialectlabs/monitor/blob/main/examples/007-pushy-data-source-monitor.ts) for implementation details. ### 3. Transform Data to Detect Events During the transform step, streaming data is processed to identify events that should trigger notifications: ```typescript theme={null} .transform({ keys: ['cratio'], // Which data fields to monitor pipelines: [ Pipelines.threshold({ type: 'falling-edge', // Trigger when value drops below threshold threshold: 0.5, }), ], }) ``` #### Available Transformations * **Thresholding**: `rising-edge`, `falling-edge` * **Windowing**: Fixed size, fixed time, sliding windows * **Aggregation**: Average, min, max operations * **Array Diff**: Track additions/removals from arrays For more examples, see: * [Custom Pipeline Operators](https://github.com/dialectlabs/monitor/blob/main/examples/005-custom-pipelines-using-operators.ts) * [Array Diff Pipeline](https://github.com/dialectlabs/monitor/blob/main/examples/008-diff-pipeline.ts) ### 4. Add Notification Logic The notify step defines how to send alerts when events are detected: ```typescript theme={null} .notify() .dialectSdk( ({ value }) => { return { title: "dApp cratio warning", message: `Your cratio = ${value} below warning threshold`, }; }, { dispatch: "unicast", // unicast, multicast, or broadcast to: ({ origin: { resourceId } }) => resourceId, } ) ``` #### Dispatch Types * **unicast**: Send to one specific subscriber * **multicast**: Send to multiple specific subscribers * **broadcast**: Send to all subscribers You can also create [custom notification sinks](https://github.com/dialectlabs/monitor/blob/main/examples/004-custom-notification-sink.ts) for specialized delivery methods. ### 5. Build and Start the Monitor ```typescript theme={null} .and() .build(); // Start monitoring monitor.start(); ``` ## Complete Example Here's a full working example that monitors collateralization ratios and sends warnings: ```typescript theme={null} const dataSourceMonitor: Monitor = Monitors.builder({ sdk: dialectSolanaSdk, subscribersCacheTTL: Duration.fromObject({ seconds: 5 }), }) .defineDataSource() .poll((subscribers: ResourceId[]) => { const sourceData: SourceData[] = subscribers.map( (resourceId) => ({ data: { cratio: Math.random(), healthRatio: Math.random(), resourceId, }, groupingKey: resourceId.toString(), }) ); return Promise.resolve(sourceData); }, Duration.fromObject({ seconds: 3 })) // Warning when cratio drops below 0.5 .transform({ keys: ["cratio"], pipelines: [ Pipelines.threshold({ type: "falling-edge", threshold: 0.5, }), ], }) .notify() .dialectSdk( ({ value }) => { return { title: "dApp cratio warning", message: `Your cratio = ${value} below warning threshold`, }; }, { dispatch: "unicast", to: ({ origin: { resourceId } }) => resourceId, } ) // Recovery notification when cratio rises above 0.5 .also() .transform({ keys: ["cratio"], pipelines: [ Pipelines.threshold({ type: "rising-edge", threshold: 0.5, }), ], }) .notify() .dialectSdk( ({ value }) => { return { title: "dApp cratio recovered", message: `Your cratio = ${value} is now above warning threshold`, }; }, { dispatch: "unicast", to: ({ origin: { resourceId } }) => resourceId, } ) .and() .build(); dataSourceMonitor.start(); ``` ## Testing Your Monitor ### Step 1: Generate Test Keypair ```bash theme={null} export your_path=~/projects/dialect/keypairs/ solana-keygen new --outfile ${your_path}/monitor-localnet-keypair.private solana-keygen pubkey ${your_path}/monitor-localnet-keypair.private > ${your_path}/monitor-localnet-keypair.public ``` ### Step 2: Start Server ```bash theme={null} cd examples export your_path=~/projects/dialect/keypairs DIALECT_SDK_CREDENTIALS=$(cat ${your_path}/monitor-localnet-keypair.private) ts-node ./your-monitor.ts ``` ### Step 3: Test with Client ```bash theme={null} cd examples export your_path=~/projects/dialect/keypairs DAPP_PUBLIC_KEY=$(cat ${your_path}/monitor-localnet-keypair.public) \ ts-node ./your-client.ts ``` ## Hosting Your Monitor If you already have a preferred hosting framework, you can simply deploy your monitor there and skip to setting up your [notification UI/UX](/alerts/integrate-inbox/). Dialect offers an opinionized, standardized way of hosting monitors within a Nest.js server implementation. **Reference Implementation:** * [Monitoring Service Template](https://github.com/dialectlabs/monitoring-service-template) ### Real-World Examples Explore these open-source implementations for inspiration: #### Governance / DAO * **[Realms](https://github.com/dialectlabs/realms-monitoring-service.git)** - Complex DAO and proposal monitoring #### DeFi Protocols * **[Jet](https://github.com/dialectlabs/jet-monitoring-service.git)** - Liquidation warnings with thresholding * **[Marinade](https://github.com/dialectlabs/marinade-monitoring-service)** - Notification types and broadcast features * **[Saber](https://github.com/dialectlabs/saber-monitoring-service.git)** - Push-type data sources and Twitter integration * **[Investin](https://github.com/dialectlabs/investin-monitoring-service)** - Multiple monitor builders for different use cases These examples may not use the latest Dialect packages. Use them for learning, but implement your monitor with the latest dependencies for new features and bug fixes. ## Advanced Features ### Multiple Transforms You can chain multiple transformations using `.also()`: ```typescript theme={null} .transform(/* first transformation */) .notify(/* first notification */) .also() .transform(/* second transformation */) .notify(/* second notification */) ``` ### Custom Pipelines Create custom pipeline operators for specialized data processing needs. See the [custom pipelines example](https://github.com/dialectlabs/monitor/blob/main/examples/005-custom-pipelines-using-operators.ts). ### Rate Limiting Built-in rate limiting prevents notification spam while ensuring important alerts get through. ## Next Steps Once your monitor is built and hosted: 1. **[Set up notification UI/UX](/alerts/integrate-inbox)** - Create user interfaces for subscription management 2. **[Configure topics and channels](/alerts/setup/topics-channels-subscribers)** - Organize your notifications 3. **[Send additional alerts](/alerts/send)** - Combine monitoring with manual alert sending ## Contributing We welcome contributions to the Monitor library! Check out the [GitHub repository](https://github.com/dialectlabs/monitor) to add custom features for your specific use cases. # Request API Keys Source: https://docs.dialect.to/alerts/api-keys Get your API keys for Dialect Alerts and start sending notifications to your users in minutes. Request API Keys for Alerts ## How it works API keys for Alerts are created automatically when you register your app in the Dialect Dashboard. To do so please follow the steps below: Head to the [Dialect Dashboard](https://dashboard.dialect.to/) and connect your wallet. NOTE: If you're having trouble connecting your wallet, try using a VPN and refreshing the page. This is especially common in the UAE. Click on "Register your Project" and fill out the required fields. [Learn more](/alerts/setup/register-app). Once registered, they're available in the [API Keys](https://dashboard.dialect.to/api-keys) section of the dashboard. You can't find the keys before registering your project. For a full walkthrough, see the [Alerts Quick Start](/alerts/quick-start). ## Need Help? If you have questions about API keys or need assistance, reach out to us at [hello@dialect.to](mailto:hello@dialect.to). # Blinks in Alerts Source: https://docs.dialect.to/alerts/blinks-in-alerts Send actionable alerts with embedded Blinks and let your users complete onchain transactions directly in your app. No more redirects or app switching. Blinks in Alerts When Dialect's mapping service detects supported protocol URLs in your alerts, it automatically enriches them with contextual data and blink URLs, allowing your users to take immediate action. Users never leave your application Rich information to build informed UIs Blinks handle protocol integrations Complete transactions in seconds Currently supports lending protocols (Jupiter Borrow), with more protocols and use cases coming soon. ## Example: Liquidation Warning Flow For this example, we'll use Jupiter Borrow as the protocol and a scenario where a user's lending position reaches a critical LTV threshold. ### Scenario * User has a SOL/USDC lending position on Jupiter Borrow * Position reaches 78% LTV (liquidation threshold is 80%) * User receives a liquidation warning alert ### Step 1: Send the Alert Your backend sends an alert with the protocol website URL: ```json {13} theme={null} { "recipient": { "type": "subscriber", "walletAddress": "6JpNV6DK88auwzKVizdeT4Bw3D44sam5GqjcPCJ7y176" }, "channels": ["IN_APP", "PUSH"], "message": { "title": "Liquidation Warning", "body": "Your SOL/USDC position is at 78% LTV, approaching liquidation threshold of 80%.", "actions": [{ "type": "link", "label": "Add Collateral", "url": "https://jup.ag/lend/borrow/1/nfts/2433" }] } } ``` ### Step 2: Client Receives Enriched Alert Dialect detects the Jupiter Borrow URL and enriches the response: ```json {10-14} theme={null} { "id": "alert-uuid", "title": "Liquidation Warning", "body": "Your SOL/USDC position is at 78% LTV, approaching liquidation threshold of 80%.", "timestamp": "2024-01-15T10:30:00Z", "actions": [{ "type": "link", "label": "Add Collateral", "url": "https://jup.ag/lend/borrow/1/nfts/2433", "context": { "type": "position", "apiUrl": "https://markets.dial.to/api/v0/positions/owners?walletAddresses=6JpNV6DK88auwzKVizdeT4Bw3D44sam5GqjcPCJ7y176&bundleIds=jupiter.borrow.1.2433", "action": "deposit" } }] } ``` ### Step 3: Fetch Position Data When the user clicks a button in the alert, fetch the position data from the [Markets and Positions API](/api-reference/positions/list-all-market-positions-by-wallet-address): ```bash theme={null} curl 'https://markets.dial.to/api/v0/positions/owners?walletAddresses=6JpNV6DK88auwzKVizdeT4Bw3D44sam5GqjcPCJ7y176&bundleIds=jupiter.borrow.1.2433' \ --header 'x-dialect-api-key: pk_demo' ``` The response includes detailed position and market data: ```json theme={null} { "positions": [ { "id": "jupiter.borrow.1.2433.deposit", "type": "lending", "marketId": "jupiter.borrow.1", "ownerAddress": "6JpNV6DK88auwzKVizdeT4Bw3D44sam5GqjcPCJ7y176", "bundleId": "jupiter.borrow.1.2433", "side": "deposit", "additionalData": { "vaultId": 1, "nftId": 2433 }, "actions": { "deposit": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/deposit?positionId=2433" }, "withdraw": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/2433/withdraw" } }, "amount": 0.015060007, "amountUsd": 3.34, "ltv": 0.3033, "liquidationPrice": 84.00917409932147, "market": { ... } }, // ... borrow position ] } ``` ```json theme={null} { "positions": [ { "id": "jupiter.borrow.1.2433.deposit", "type": "lending", "marketId": "jupiter.borrow.1", "ownerAddress": "6JpNV6DK88auwzKVizdeT4Bw3D44sam5GqjcPCJ7y176", "bundleId": "jupiter.borrow.1.2433", "side": "deposit", "additionalData": { "vaultId": 1, "nftId": 2433 }, "actions": { "deposit": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/deposit?positionId=2433" }, "withdraw": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/2433/withdraw" } }, "amount": 0.015060007, "amountUsd": 3.34, "ltv": 0.3033, "liquidationPrice": 84.00917409932147, "market": { "id": "jupiter.borrow.1", "type": "lending", "provider": { "id": "jupiter", "name": "Jupiter", "icon": "https://imagedelivery.net/C7jfNnfrjpAYWW6YevrFDg/5c23c065-89ef-413b-8deb-6d8c6a849300/public" }, "token": { "address": "So11111111111111111111111111111111111111112", "symbol": "WSOL", "decimals": 9, "icon": "https://coin-images.coingecko.com/coins/images/21629/large/solana.jpg?1696520989" }, "borrowToken": { "address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "symbol": "USDC", "decimals": 6, "icon": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png?1696506694" }, "websiteUrl": "https://jup.ag/lend/borrow/1", "depositApy": 0.0784, "baseDepositApy": 0.0484, "borrowApy": 0.0597, "baseBorrowApy": 0.0597, "totalDeposit": 665220.958436127, "totalDepositUsd": 147340132.39, "totalBorrow": 80014008.8212, "totalBorrowUsd": 79990184.41, "maxBorrow": 120021000.673021, "maxLtv": 0.75, "liquidationLtv": 0.8, "liquidationPenalty": 0.01, "additionalData": {}, "actions": { "deposit": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/deposit" } } } }, { "id": "jupiter.borrow.1.2433.borrow", "type": "lending", "marketId": "jupiter.borrow.1", "ownerAddress": "6JpNV6DK88auwzKVizdeT4Bw3D44sam5GqjcPCJ7y176", "bundleId": "jupiter.borrow.1.2433", "side": "borrow", "additionalData": { "vaultId": 1, "nftId": 2433 }, "actions": { "borrow": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/2433/borrow" }, "repay": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/2433/repay" } }, "amount": 1.012143, "amountUsd": 1.01, "ltv": 0.3033, "liquidationPrice": 84.00917409932147, "market": { "id": "jupiter.borrow.1", "type": "lending", "provider": { "id": "jupiter", "name": "Jupiter", "icon": "https://imagedelivery.net/C7jfNnfrjpAYWW6YevrFDg/5c23c065-89ef-413b-8deb-6d8c6a849300/public" }, "token": { "address": "So11111111111111111111111111111111111111112", "symbol": "WSOL", "decimals": 9, "icon": "https://coin-images.coingecko.com/coins/images/21629/large/solana.jpg?1696520989" }, "borrowToken": { "address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "symbol": "USDC", "decimals": 6, "icon": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png?1696506694" }, "websiteUrl": "https://jup.ag/lend/borrow/1", "depositApy": 0.0784, "baseDepositApy": 0.0484, "borrowApy": 0.0597, "baseBorrowApy": 0.0597, "totalDeposit": 665220.958436127, "totalDepositUsd": 147340132.39, "totalBorrow": 80014008.8212, "totalBorrowUsd": 79990184.41, "maxBorrow": 120021000.673021, "maxLtv": 0.75, "liquidationLtv": 0.8, "liquidationPenalty": 0.01, "additionalData": {}, "actions": { "deposit": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/deposit" } } } } ] } ``` ### Step 4: Build the UI With the response data, you can build rich UIs ranging from simple action buttons in your notification feed to full detail screens with position cards, input interfaces, and transaction controls. The response includes all the data you need: protocol information, token details, position health metrics, blink URLs for actions, and more. Full deposit screen example showing position details and deposit interface For more details on available data fields, and ideas on how to use the data, see [using the data](/alerts/integrate-inbox/api/notifications#using-the-data). ### Step 5: Execute Transaction When the user submits: 1. Call the blink URL with user's wallet address and amount 2. Receive ready-to-sign transaction 3. Prompt user to sign with their wallet 4. Submit transaction to blockchain 5. Show success state and updated position metrics ## Key Concepts ### Mapping Service Dialect's mapping service automatically detects supported protocol URLs in your alerts and enriches them with actionable context. Here's the flow: Your app sends an alert with a protocol URL (e.g., `https://jup.ag/lend/borrow/1/nfts/2433`). The mapping service recognizes the URL pattern from supported protocols. The service extracts position identifiers from the URL (vault ID, position ID, wallet address) and adds a `context` object to the notification action containing: * `type`: The kind of data available (e.g., `"position"`) * `apiUrl`: A pre-filled API endpoint to fetch detailed data * `action`: The suggested action for the user (e.g., `"deposit"`) Your client receives the original alert plus the enriched context. The context object is **only present** when the URL is recognized. The original protocol URL remains available as a fallback. This approach allows you to send simple alerts with protocol URLs, and Dialect automatically adds the necessary context for building rich, actionable UIs. ### Context Object The `context` object provides everything your client needs to fetch additional data and build interactive experiences: ```typescript theme={null} { type: string, // Type of context data available apiUrl: string, // Pre-filled API endpoint action: string // Suggested action type } ``` **Fields:** * **`type`**: Determines what kind of data the `apiUrl` will return. Currently `"position"` for lending/borrowing positions. Additional types will be added in the future. * **`apiUrl`**: Complete API endpoint with all required parameters already filled in. Call this URL to get detailed position, market, and blink data. * **`action`**: The recommended action for the user based on the alert context (e.g., `"deposit"`) The `type` field is important because it tells your client how to interpret and display the data from the API response. ## Implementation Learn how to send alerts with blinks. Handle blinks on the client side. # Introduction to Events Source: https://docs.dialect.to/alerts/events/index Integrate onchain and other market events into your products, as webhooks or bundled with Dialect's Alerts Stack. **Beta Access Required** Events is currently in beta. To request early access, please contact our team at [hello@dialect.to](mailto:hello@dialect.to). Dialect Events System # Overview Dialect's Events system provides real-time detection and delivery of blockchain-specific events. These events can be used standalone via webhook delivery, or bundled with Dialect's Alerts Stack as off-the-shelf topics. Developers of all types can use Events for different purposes: to deliver timely alerts to their users, power other custom wallet app event-based capabilities, or even to power trading tools. No complex monitoring infrastructure required. ## What are Events? Events are triggered when specific onchain or market conditions are met, such as an interesting price change for a token held in a wallet, or a new token is trending. When an event is detected, the Events system executes the configured behavior: either a webhook delivery or an Alerts Stack notification. ## Use Cases Notify users about significant price movements in their portfolio Alert traders to breakthrough tokens and market opportunities Provide timely updates on token performance Trigger actions based on market conditions ## Supported Event Types Dialect supports two event types: 1. Price Change Events (Market-Based and Trade-Based) 2. Trending Token Events ### Price Change Events Price change events detect significant price movements in tokens through two approaches: * **Market-Based**: Track price movements over sliding time windows (e.g., "SOL is up 10% in 24h") * **Trade-Based**: Personalized alerts relative to users' actual trade prices (e.g., "SOL is up 50% since you bought") To read more about both types of price change events, see [Price Change Events](/alerts/events/price-change). ### Trending Token Events Trending token events trigger when a newly-launched token graduates from a launchpad and meets strict performance criteria. To read more about this event type, see [Trending Token Events](/alerts/events/trending-token). ## Detecting Interesting Events Most interesting events are difficult to detect. Some events like "SOL is over \$150" may be easy to detect and can sometimes be useful. But it is much more interesting, and much harder, to detect that a token is undergoing unusual price movement, or that one trending token meets strict performance criteria among the many thousands of others that were launched on the same day. It is also difficult to build an events system that is real time, capable of detecting optimally interesting events, and is rate limited. Dialect's Events uses advanced techniques to satisfy all three of these across all of of our Event types. To read more about it, see [Technical Overview](/alerts/events/technical-overview). ## New event types We are building more event types every month. Have one in mind? Get in touch with us. # Price Change Events Source: https://docs.dialect.to/alerts/events/price-change Track token price movements with market-wide sliding windows or personalized alerts based on individual trading history. Get instant notifications when tokens hit thresholds, with smart filtering that cuts through market noise. **Beta Access Required** Price Change Events are currently in beta. To request early access, please contact our team at [hello@dialect.to](mailto:hello@dialect.to). ## Overview Dialect offers two types of price change detection: 1. **Market-Based Price Changes**: Track price movements over sliding time windows (e.g., "SOL is up 10% in 24h") 2. **Trade-Based Price Changes**: Personalized alerts relative to users' actual trade prices (e.g., "SOL is up 50% since you bought") Both systems use complex threshold detection and advanced filtering to deliver only the most relevant alerts. ## Market-Based Price Changes Market-Based Price Changes are triggered by significant price movements of a token over sliding time windows using percentage change thresholds. ### How It Works Dialect continuously monitors token prices and analyzes percentage changes over sliding time windows. When a token's price change exceeds the configured threshold within a time window, a webhook is triggered with detailed information about the price movement. ### Sliding Window Analysis Price changes are calculated using a sliding window approach: * **Window Duration**: Configurable time periods (1h, 3h, 6h, 12h, 24h, 7d) * **Threshold**: Percentage change required to trigger an event * **Direction**: Both upward and downward movements are detected ### Supported Time Windows | Window | Beta Status | Use Case | | ------ | ------------- | ------------------------------------------- | | 1h | Future | Momentum detection, immediate market shifts | | 3h | **Available** | Short-term trend confirmation | | 6h | **Available** | Intraday trend identification | | 12h | **Available** | Half-day trend analysis | | 24h | **Available** | Daily price movement tracking | | 7d | Planned | Weekly trend detection | ### Market-Based Webhook Payload ```json theme={null} { "events": [ { "event": "token_price_change", "timestamp": "2025-07-04T14:30:00Z", "token": { "symbol": "SOL", "address": "So11111111111111111111111111111111111111112" }, "trigger": { "type": "sliding_window_percentage_change", "window": { "duration": "24h" }, "threshold": 10.0 }, "change": { "direction": "up", "from": { "timestamp": "2025-07-03T14:30:00Z", "value": 154.25 }, "to": { "timestamp": "2025-07-04T14:30:00Z", "value": 171.85 }, "absolute": 17.6, "percentage": 11.42 } } ] } ``` #### Market-Based Field Details ##### Event Metadata * **event**: `token_price_change` * **timestamp**: ISO 8601 UTC timestamp when event was generated ##### Token Information * **symbol**: Token ticker symbol * **address**: Token mint address on Solana ##### Trigger Details * **type**: `sliding_window_percentage_change` * **window**: Sliding window configuration * **duration**: Time window used for the calculation (e.g., `1h`, `3h`, `6h`, `12h`, `24h`, `7d`) * **threshold**: The percentage change that must be crossed within the window to trigger the event ##### Change Information * **direction**: `up` for price increases, `down` for decreases * **from**: Price and timestamp at the start of the window * **to**: Price and timestamp at the end of the window * **absolute**: Raw price difference (`to.value - from.value`) * **percentage**: Percentage change over the window (`(to.value - from.value) / from.value * 100`) ## Trade-Based Price Changes (Last Trade) Trade-Based Price Changes are triggered by significant price movements of a token relative to a user's actual trading history. They offer a more personalized approach to [price change alerts](/alerts/events/price-change#market-based-price-changes), providing alerts based on a user's buy/sell behavior rather than market-wide price movements. ### How It Works Please note that during the beta, we only support `buy` transactions that happened **after** the wallet was registered. 1. **Registration**: Wallets must be explicitly registered to receive last trade events 2. **Trade Detection**: System monitors all swap transactions for the wallet 3. **Anchor Points**: Last buy price becomes the baseline for percentage calculations 4. **Threshold Monitoring**: Price changes are evaluated against market cap-specific thresholds 5. **Event Triggering**: Alerts fire when thresholds are crossed in either direction ### Registration Process Wallets must be explicitly registered to receive last trade events. During beta: * Contact us at [hello@dialect.to](mailto:hello@dialect.to) to register your wallets * Projects provide wallet lists to Dialect for monitoring * No backfill of historical trades (monitoring starts from registration) ### Trade-Based Threshold System Thresholds are determined based on token market capitalization at the time of initial tracking: #### Standard Position Thresholds | Market Cap Range | Uptrend Triggers | Downtrend Triggers | | ------------------------ | ---------------------------- | -------------------------------------- | | **Large Cap** (>\$1B) | +5%, +10%, then every +10% | -5%, -10%, then every -10% | | **Mid Cap** ($50M-$500M) | +20%, +50%, then 2x, 3x, 4x… | -20%, -30%, then every -10% until -90% | | **Small Cap** (\<\$50M) | +50%, then 2x, 3x, 4x… | -20%, -30%, then every -10% until -90% | | **Micro Cap** (\<\$1M) | +50%, 2x, then every 1x | -30%, -50%, -70%, -90% | #### Post-Exit Thresholds After a full sell (100% position exit), different thresholds apply: | Market Cap Range | Uptrend Trigger | Downtrend Trigger | | ------------------------ | --------------- | ----------------- | | **Large Cap** (>\$1B) | +30% | -30% | | **Mid Cap** ($50M-$500M) | 2x | -50% | | **Small Cap** (\<\$50M) | 3x | -50% | | **Micro Cap** (\<\$1M) | 5x | -70% | ### Event Triggering Rules #### Threshold Crossing * Events trigger when price **fully crosses** a threshold * Same threshold cannot trigger twice consecutively * Price must cross another threshold before original can trigger again #### Round Trip Detection * System tracks when price returns to previous thresholds * Generates contextual alerts like "TOKEN back to +50% from 2x" * Helps users understand retracement patterns #### Multiple Threshold Handling * If multiple thresholds crossed in 5-minute window, only most significant triggers * Prioritizes largest movement (highest for uptrend, lowest for downtrend) ### Trade-Based Webhook Payload ```json theme={null} { "events": [ { "event": "token_last_trade_price_change", "timestamp": "2025-09-17T16:03:10.259Z", "token": { "symbol": "TOKEN", "address": "TokenMintAddress..." }, "walletAddress": "UserWalletAddress...", "trigger": { "type": "trade_percentage_change", "trade": { "type": "buy", "timestamp": "2024-01-15T10:00:00.000Z", "transactionId": "TxSignature...", "balance": { "before": 0, "after": 1000, "change": 1000 } }, "threshold": 50 }, "changes": { "triggerTrade": { "type": "trade_change", "trade": { "type": "buy", "timestamp": "2024-01-15T10:00:00.000Z", "transactionId": "TxSignature...", "balance": { "before": 0, "after": 1000, "change": 1000 } }, "change": { "direction": "up", "from": { "timestamp": "2024-01-15T10:00:00.000Z", "value": 1.00 }, "to": { "timestamp": "2025-09-17T16:03:10.259Z", "value": 1.50 }, "absolute": 0.50, "percentage": 50.0 } }, "lastTrade": { "type": "trade_change", "trade": { "type": "buy", "timestamp": "2024-01-15T10:00:00.000Z", "transactionId": "TxSignature...", "balance": { "before": 0, "after": 1000, "change": 1000 } }, "change": { "direction": "up", "from": { "timestamp": "2024-01-15T10:00:00.000Z", "value": 1.00 }, "to": { "timestamp": "2025-09-17T16:03:10.259Z", "value": 1.50 }, "absolute": 0.50, "percentage": 50.0 } }, "lastPeak": { "type": "peak_change", "change": { "direction": "down", "from": { "timestamp": "2025-09-17T12:00:00.000Z", "value": 2.00 }, "to": { "timestamp": "2025-09-17T16:03:10.259Z", "value": 1.50 }, "absolute": -0.50, "percentage": -25.0 } } } } ] } ``` #### Trade-Based Field Descriptions ##### Event Metadata * **event**: `token_last_trade_price_change` * **timestamp**: ISO 8601 UTC timestamp when event was generated * **walletAddress**: The wallet being monitored for this event ##### Token Information * **symbol**: Token ticker symbol * **address**: Token mint address on Solana ##### Trigger Details * **type**: `trade_percentage_change` * **trade**: The anchor trade (last buy or sell) used for calculations * **type**: `buy` or `sell` * **timestamp**: When the trade occurred * **transactionId**: Blockchain transaction signature * **balance**: Token balance changes * **before**: Balance before trade * **after**: Balance after trade * **change**: Net change (positive for buys, negative for sells) * **threshold**: The percentage threshold that was crossed ##### Change Information * **triggerTrade**: Price change from the anchor trade * **lastTrade**: Price change from most recent trade * **lastPeak**: Price change from previous alert threshold (optional) * Each contains: * **direction**: `up` or `down` * **from/to**: Start and end prices with timestamps * **absolute**: USD price difference * **percentage**: Percentage change ### Edge Cases #### Partial Sells * Last buy price remains the anchor * Partial sells don't reset calculation baseline * Allows tracking performance of remaining position * If a subsequent **buy** occurs after a partial sell, that new buy **immediately replaces** the anchor; thresholds reset from the new buy price * When multiple trades happen within the same 5‑minute time window, `lastTrade` always refers to the **chronologically latest** trade #### SOL Handling * Trade‑based SOL price changes are **muted in MVP** * SOL is treated as an investment vehicle rather than a tracked asset for trade‑based alerts * **Market‑based SOL** price change alerts remain available and are unaffected #### Multiple Buys (DCA) * Only most recent buy price is used as anchor * No averaging across multiple purchases * We do **not** average across buys. Any new buy supersedes the prior anchor and restarts threshold tracking ## Implementation Examples ### Market-Based Price Change Handler ```javascript theme={null} app.post('/webhook/price-events', (req, res) => { const event = req.body; // Send push notification to users holding this token sendPushNotification({ title: `${event.token.symbol} ${event.change.direction === 'up' ? '📈' : '📉'} ${event.change.percentage.toFixed(1)}%`, body: `${event.token.symbol} moved ${event.change.percentage.toFixed(1)}% in the last ${event.trigger.window.duration}`, data: { tokenAddress: event.token.address, priceChange: event.change.percentage } }); res.status(200).send('OK'); }); ``` ### Trade-Based Price Change Handler ```javascript theme={null} app.post('/webhook/last-trade-events', async (req, res) => { const { events } = req.body; for (const event of events) { const { walletAddress, token, trigger, changes } = event; // Determine alert message based on context let message; if (trigger.trade.type === 'buy' && !changes.lastPeak) { // Simple price change from buy message = `${token.symbol} is ${changes.triggerTrade.change.direction} ` + `${Math.abs(changes.triggerTrade.change.percentage).toFixed(1)}% ` + `since you bought`; } else if (changes.lastPeak) { // Round trip from previous threshold message = `${token.symbol} is back to ` + `${changes.triggerTrade.change.percentage > 0 ? '+' : ''}` + `${Math.abs(changes.triggerTrade.change.percentage).toFixed(1)}% ` + `from ${changes.lastPeak.change.percentage > 0 ? '+' : ''}` + `${Math.abs(changes.lastPeak.change.percentage).toFixed(1)}%`; } // Send personalized notification await sendUserNotification(walletAddress, { title: `${token.symbol} Price Alert`, body: message, data: { tokenAddress: token.address, currentPrice: changes.triggerTrade.change.to.value } }); } res.status(200).send('OK'); }); ``` # Technical Overview Source: https://docs.dialect.to/alerts/events/technical-overview Integrate price alerts and automated trading triggers using webhook events. Build powerful applications by subscribing to real-time market events. Configure price thresholds and receive structured webhook payloads to trigger your application logic. ## Architecture ### Event Detection Pipeline 1. **Data Ingestion**: Real-time price feeds and on-chain data monitoring 2. **Event Processing**: Sliding window analysis and threshold detection 3. **Webhook Delivery**: Reliable delivery to your infrastructure ### Webhook Delivery When an event is detected, Dialect sends an HTTP POST request to your configured webhook endpoint with a structured JSON payload. ## Webhook Payload Schema All webhooks deliver events as arrays, allowing for batch delivery of multiple events in a single request. You can receive multiple events of the same type or mixed event types in one webhook call: ```json theme={null} { "events": [ { "event": "token_price_change", "timestamp": "2025-07-04T14:30:00Z", "token": { "symbol": "SOL", "address": "So11111111111111111111111111111111111111112" }, "trigger": { "type": "sliding_window_percentage_change", "window": { "duration": "24h" }, "threshold": 10.0 }, "change": { "direction": "up", "from": { "timestamp": "2025-07-03T14:30:00Z", "value": 154.25 }, "to": { "timestamp": "2025-07-04T14:30:00Z", "value": 171.85 }, "absolute": 17.6, "percentage": 11.42 } }, { "event": "trending_token", "timestamp": "2025-08-05T12:45:30Z", "token": { "symbol": "BOSS", "address": "GUy9Tu8YtvvHoL3DcXLJxXvEN8PqEus6mWQUEchcbonk" }, "trigger": { "type": "launchpad_graduated" }, "metrics": { "graduatedAt": "2025-08-05T12:31:03Z", "marketCap": 9866748.92, "liquidity": 850000, "holders": 20324, "price": 0.0106, "launchpad": "raydium", "mintAuthorityDisabled": true, "freezeAuthorityDisabled": true, "topHoldersPercentage": 15.2, "socialCount": 1, "priceChange1h": -2.1, "priceChange6h": 8.5, "priceChange24h": 12.3 } } // ... more events ] } ``` ### Common Fields All events share these common fields: * **event**: Event type identifier (`token_price_change`, `trending_token`, or `token_last_trade_price_change`) * **timestamp**: UTC timestamp when the event was generated (ISO 8601) * **token**: Token information including symbol and mint address * **trigger**: Configuration that caused the event to fire The rest of the data structure is event-specific. For detailed field descriptions and specifications, see the individual event type pages for: * [Price Change](/alerts/events/price-change), * [Trending Token](/alerts/events/trending-token), and * [Last Trade Price Change](/alerts/events/price-change#trade-based-price-changes-last-trade) # Trending Token Events Source: https://docs.dialect.to/alerts/events/trending-token Trending Token events trigger when a newly-launched token graduates from a launchpad and meets strict performance criteria. Get notified of high-potential tokens with built-in audit filters and advanced analytics. Trending Token Events are currently in beta. Contact us at [hello@dialect.to](mailto:hello@dialect.to) for early access or to discuss specific requirements. ## Webhooks Webhooks for trending tokens can be delivered to power custom behavior in your infrastructure and apps. Reach out to the Dialect team for early access using the form above. ### Webhook Payload When trending token events are detected, you'll receive a webhook with an array structure. Each individual event within the array has this structure: ```json theme={null} { "events": [ { "event": "trending_token", "timestamp": "2025-08-05T12:45:30Z", "token": { "symbol": "BOSS", "address": "GUy9Tu8YtvvHoL3DcXLJxXvEN8PqEus6mWQUEchcbonk" }, "trigger": { "type": "launchpad_graduated" }, "metrics": { "graduatedAt": "2025-08-05T12:31:03Z", "marketCap": 9866748.92, "liquidity": 850000, "holders": 20324, "price": 0.0106, "launchpad": "raydium", "mintAuthorityDisabled": true, "freezeAuthorityDisabled": true, "topHoldersPercentage": 15.2, "socialCount": 1, "priceChange1h": -2.1, "priceChange6h": 8.5, "priceChange24h": 12.3 } } ] } ``` #### Field Details **Event Information** * **event**: Always `"trending_token"` for trending token events * **timestamp**: ISO 8601 timestamp when the event was detected **Token Details** * **symbol**: Token symbol (e.g., "BOSS") * **address**: Solana token mint address **Trigger Information** * **type**: Always `"launchpad_graduated"` indicating token graduated from launchpad **Token Metrics** * **graduatedAt**: ISO 8601 timestamp when token graduated from launchpad * **marketCap**: Current market capitalization in USD * **liquidity**: Current liquidity pool value in USD * **holders**: Number of unique token holders * **price**: Current token price in USD * **launchpad**: Name of the launchpad platform (e.g., "raydium, pump\_fun, etc.") * **mintAuthorityDisabled**: `true` if mint authority revoked, `false` otherwise * **freezeAuthorityDisabled**: `true` if freeze authority revoked, `false` otherwise * **topHoldersPercentage**: Percentage of total supply held by top 10 holders * **socialCount**: Number of verified social media accounts * **priceChange1h**: Price change percentage over 1 hour window * **priceChange6h**: Price change percentage over 6 hour window * **priceChange24h**: Price change percentage over 24 hour window ## Data Sources ### Supported launchpads New token events support the 40+ launchpads available on [Jupiter Pro](https://jup.ag/pro?tab=launchpads), including: * pump.fun * Letsbonk.fun * BAGS * Jupiter Studio * & 40 more ## Detection Criteria A variety of criteria must be met for an event to trigger, including familiar community "audit" filters, as well as additional analyses we have developed in-house to set a higher bar. Hundreds of tokens satisfy the audit criteria every day. Our added analysis sets a higher bar to just two to five daily. ### Community Audit Filters All events must meet the familiar community "audit" filters: * **✅ Mint Authority**: Revoked to prevent unlimited token creation. * **✅ Freeze Authority**: Revoked to prevent account freezing. * **✅ LP Burned**: Liquidity provider tokens burned for permanent liquidity. * **✅ Holder Distribution**: Top 10 holders hold less than 20% of the total supply. * **✅ Social Presence**: At least one verified social media account. *Note: "Audit" refers to community trading terminology, not formal security audits.* ### Advanced Criteria We employ an additional set of more advanced, in-house criteria to further filter the number of new token events per day. These include: * **✅ Market cap** * **✅ Liquidity** * **✅ Price change** * **✅ Trading volume change** If you'd like to better understand what criteria we have in place, please reach out. ### Top-Down Tuning Using the criteria above, we then apply a "top-down" tuning approach to achieve an ideal number of events per day, in this case two to five. # Using Events Source: https://docs.dialect.to/alerts/events/using-events Learn how to use events to build powerful applications with real-time price alerts and trending token detection. Integrate webhooks, handle events, and deliver timely notifications to your users. ## Quick Start ### 1. Configure Webhook Endpoint Set up an endpoint to receive event webhooks: ```javascript theme={null} const express = require('express'); const app = express(); app.use(express.json()); app.post('/webhook/events', (req, res) => { const event = req.body; console.log('Received event:', event.event); console.log('Token:', event.token.symbol); // Process the event handleEvent(event); // Always respond with 200 OK res.status(200).send('OK'); }); app.listen(3000, () => { console.log('Webhook server running on port 3000'); }); ``` ### 2. Registration #### 2.1 Register Your Webhook (required) Contact us at [hello@dialect.to](mailto:hello@dialect.to) to register your webhook endpoint. ### 2.2 Register Users Wallets (only for last trade price change events) If you want to offer last trade price change events to your users, you need to register their wallets. This can be done by submitting a list of wallet addresses to us. Please contact us at [hello@dialect.to](mailto:hello@dialect.to) before sending any materials. ### 3. Handle Events Implement event handlers based on the event type: ```javascript theme={null} function handleEvent(event) { switch (event.event) { case 'token_price_change': handlePriceChangeEvent(event); break; case 'token_last_trade_price_change': handleLastTradePriceChangeEvent(event); break; default: console.log('Unknown event type:', event.event); } } ``` ## Price Change Events There are two types of price change events. Please remember to [send a list](/alerts/events/using-events#2-2-register-users-wallets-only-for-last-trade-price-change-events) with users wallets to us if you want to offer last trade price change events to your users. ### Market-Based Price Change Events Price change events are triggered by significant price movements over sliding time windows using percentage change thresholds. ```javascript theme={null} async function handlePriceChangeEvent(event) { const { token, change, trigger } = event; // Example: Notify users who hold this token const message = `${token.symbol} moved ${change.percentage.toFixed(1)}% over the last ${trigger.window.duration}`; // Send notifications based on your application logic await notifyUsers(token.address, message); } ``` #### Available Windows * 1h, 3h, 6h, 12h, 24h, 7d #### Spam Prevention Dialect's event detection includes built-in capabilities to ensure users don't receive too many alerts: * **Debouncing & rolling averages** - Prevents rapid-fire alerts * **Rate limits** - Controls notification frequency * **Priority conflicts** - Manages competing events * **Alert escalation** - Prioritizes important events ### Last Trade Price Change Events Last trade price change events provide personalized alerts based on users' actual trading history. To use this feature, you need to [register](/alerts/events/using-events#2-2-register-users-wallets-only-for-last-trade-price-change-events) the wallets of your users. ```javascript theme={null} async function handleLastTradePriceChangeEvent(event) { const { walletAddress, token, trigger, changes } = event; // Build contextual message based on trade history let message; const percentChange = Math.abs(changes.triggerTrade.change.percentage); const direction = changes.triggerTrade.change.direction; if (trigger.trade.type === 'buy') { // Alert relative to buy price message = `${token.symbol} is ${direction} ${percentChange.toFixed(1)}% since you bought`; } else if (trigger.trade.type === 'sell') { // Handle logic for sell events, // e.g. send alert after full exit, // partial sells, etc. } // Send personalized notification to specific wallet await sendWalletNotification(walletAddress, message); } ``` ### Key Features * **Personalized Thresholds**: Based on market cap and position status * **Round Trip Detection**: Alerts when returning to previous levels * **Post-Exit Monitoring**: Continues tracking after full sells ## Trending Token Events (Coming Soon) We're developing advanced event detection for newly launched tokens that meet specific safety and quality criteria. This system will focus on tokens with "staying power" rather than short-lived movements. Learn more about our approach: See the [Trending Tokens Events](/alerts/events/trending-token) section for detailed information about detection criteria, safety filters, and the quality-first methodology. **Early Access Available** Trending token events are in development. The system is being tested internally with smart defaults before expanding to user customization. Contact us at [hello@dialect.to](mailto:hello@dialect.to) for early access or to discuss specific requirements. # Frequently Asked Questions Source: https://docs.dialect.to/alerts/faq Common questions and answers about Dialect's Alert Stack. ## Getting Started ### What is the Dialect Alert Stack? Dialect's Alert Stack is a notification infrastructure platform for Web3 applications. It enables developers to send notifications to users via multiple channels (email, Telegram, in-app, push notifications) while giving users full control over their notification preferences. ### How is Dialect different from traditional notification services? **Key differences:** * **Web3-native**: Built for blockchain applications with wallet-based authentication * **User-controlled**: Users own their notification preferences, not the app * **Multi-channel**: One API, multiple delivery methods * **Decentralized identity**: No need for email/phone collection upfront ### Do I need to handle user signup/authentication? No! Dialect uses **wallet-based authentication**. Users connect their Solana wallet to subscribe to notifications. No email collection or password management required. ### What chains does Dialect support? Currently **Solana**. The system is designed to be chain-agnostic with plans to support additional chains in the future. ## Technical Integration ### How do I get started? 1. **[Register your app](/alerts/setup/register-app)** - Get API credentials 2. **[Create topics](/alerts/setup/topics-channels-subscribers)** - Define notification types 3. **[Send notifications](/alerts/send)** - Integrate sending in your app 4. **[Add subscription UI](/alerts/integrate-inbox/)** - Let users manage preferences The [Quick Start guide](/alerts/quick-start) walks through this in \~15 minutes. ### What programming languages do you support? * **JavaScript/TypeScript**: Full SDK and React components * **REST API**: Any language that can make HTTP requests * **Python**: SDK coming soon * **Rust**: SDK planned ### Can I use Dialect without React? **Yes!** You can: * Use the **REST API** directly from any framework * Use the **TypeScript SDK** in vanilla JavaScript * Integrate with Vue, Angular, Svelte, or any frontend framework The React components are optional convenience tools. ### How do I test notifications during development? 1. **Use test API keys** from your dashboard 2. **Send to your own wallet** for testing 3. **Check dashboard analytics** to verify delivery 4. **Use the dashboard test interface** for quick validation Test notifications are clearly marked and don't count against quotas. ## Notification Channels ### What notification channels are available? | Channel | Description | Setup Required | | ------------ | ------------------------------------- | -------------------------------------- | | **EMAIL** | Traditional email notifications | None (uses Dialect's service) | | **TELEGRAM** | Telegram bot messages | Bot sends you a code | | **IN\_APP** | Notifications within your application | SDK integration | | **PUSH** | Mobile push notifications | Firebase/APNs config (app owners only) | ### Who can send push notifications? **Only app owners** can send push notifications to their own users. Third-party apps must use IN\_APP channel, and the app owner decides whether to show these as push notifications. Example: A DeFi protocol (app owner) can send push notifications directly. A third-party analytics app would send IN\_APP notifications, which the protocol's mobile app could display as push notifications. ## User Experience ### How do users subscribe to notifications? Users can subscribe through: 1. **Your app's interface** - Custom subscription UI you build 2. **Dialect's widget** - Drop-in React component 3. **Direct dashboard link** - Hosted subscription page All methods use wallet authentication - no email/password required. ### Can users unsubscribe? **Absolutely!** Users have full control: * **Unsubscribe from specific topics** - Stop liquidation alerts but keep price alerts * **Change channels** - Switch from email to Telegram * **Unsubscribe completely** - Stop all notifications from your app ### Do users need to pay for notifications? **No.** As of today, this service is free to use. ## Use Cases ### What types of apps use Dialect? Common use cases: * **DeFi Protocols**: Liquidation warnings, yield updates * **NFT Marketplaces**: Auction alerts, collection updates * **Trading Platforms**: Order fills, price alerts * **Gaming**: Quest completions, leaderboard updates * **DAOs**: Governance proposals, voting reminders ## Support For **product support**, **partnership requests**, and **other questions** we have not covered here, email [hello@dialect.to](mailto:hello@dialect.to). # Alerts Source: https://docs.dialect.to/alerts/index Get your users the notifications they need, when and where they need them. **New: Blinks in Alerts**
Send actionable alerts with embedded Blinks and let your users complete onchain transactions directly in your app. No more redirects or app switching. [Learn more](/alerts/blinks-in-alerts)
Dialect Alerts is a notification infrastructure that powers messaging for over 30 of the most-loved apps and wallets on Solana. ![Image with alerts delivered via multiple channels](https://res.cloudinary.com/dnxjhra4h/image/upload/f_auto,q_auto/dialect-docs/alerts-meet-anywhere.png) ## Why Dialect Alerts? **Fast Integration** - Get notifications working in under 15 minutes with our Dashboard, REST API, TypeScript SDK, or pre-built React components. No infrastructure needed. **Built for Web3** - Wallet-based authentication, multi-chain support, and cross-app notifications that let users see alerts from all their apps in one place. **Complete Customization** - Full CSS control, rich content support, topics, channels, and advanced targeting to make notifications feel native to your brand exactly the way you want them. **Enterprise Scale** - Proven performance handling 1M+ daily active users and hundreds of millions of API requests with zero downtime. ## What You Can Build Establish a direct line of communication to your users: * **Critical Alerts** - Liquidation warnings, security notifications, system updates * **Smart Updates** - Price targets, portfolio changes, governance proposals * **Engagement** - NFT activity, trading opportunities, community announcements * **Multi-Channel Delivery** - Reach users through email, Telegram, in-app notifications, and mobile push alerts * **Universal Inbox** - Thanks to our inbox, users can even receive notifications in the apps they already use and love ## Performance Handle north of 1 million DAU across our APIs for both sending alerts server-side and managing inbox feeds & user subscription preferences from the client Successfully processed across our Alerts Stack in recent weeks, all without a single performance issue Maintained consistent uptime even during peak usage periods Powering alerts for Jupiter Mobile Radar, one of the fastest apps in crypto ### Time-Sensitive Alerts Many time-sensitive alerts need to reach users quickly. We have a "burst mode" that can support orders of magnitude higher usage for up to dozens of minutes at a time: * **Orders of Magnitude Higher Usage**: Scale up dramatically for urgent notifications * **Extended Duration**: Maintain peak performance for up to dozens of minutes at a time * **Real-time Processing**: Ensure critical alerts reach users without delay ### Priority Queue A priority queue handles different kinds of delivery pipelines: * **High priority alerts** for things like limit orders or liquidation warnings that need fast delivery * **Lower priority** for broadcast alerts like company announcements, that can be delivered to users over longer timelines This tiered approach ensures that urgent, actionable alerts are delivered instantly while less time-sensitive communications are processed efficiently over longer timelines. ## Getting Started Paths ### New to Alerts? **[Quick Start Guide](/alerts/quick-start)** - With our Quick Start Guide, you can send your first notification in under 15 minutes. Learn how you can register your app, subscribe to notifications and send a test notification. **[Try the Dashboard](https://dashboard.dialect.to)** - Send notifications immediately with no code required. Perfect for testing or one-off messages. ### Want to Send Alerts? Sending alerts can be achieved through multiple approaches, from simple no-code solutions to powerful programmatic integrations: **[Introduction](/alerts/send/index)** - Choose the best approach for your app: * **[REST API](/alerts/send/api/index)**: HTTP endpoints for basic notification sending from any language or platfor incl. mobile * **[React SDK](/alerts/send/sdk/index)**: Rich developer experience with type safety, helper functions, and UI components * **[Dashboard](/alerts/send/dashboard)**: No-code solution for marketing and announcements * **[Monitoring](/alerts/alerts-and-monitoring/index)**: Automated alerts based on blockchain events ### Want to Receive Alerts? Send your users the information they need when they need it. We recommend integrating our alerts using our SDKs for the best experience. Have a look at our inbox that lets your users also receive notifications from other apps, so they don't have to leave your app to stay informed. **[Introduction](/alerts/integrate-inbox/index)** - Build notification experiences for your users: * **[User Management](/alerts/integrate-inbox/user-management)**: Subscription flows and preferences * **[API Integration](/alerts/integrate-inbox/api/index)**: Custom notification interfaces * **[SDK Components](/alerts/integrate-inbox/sdk/setup-configuration)**: Pre-built React components * **[Universal Inbox](/alerts/integrate-inbox/universal-inbox)**: Cross-app notification history ## Questions? Check our [FAQ](/alerts/faq) or explore specific topics in the navigation menu. # Authentication Source: https://docs.dialect.to/alerts/integrate-inbox/api/authentication Set up wallet-based user authentication for notification inbox integration using JWT tokens and client keys ## Prerequisites Before implementing authentication, ensure you have: * **App Registration**: Your app must be registered with Dialect ([registration guide](/alerts/setup/register-app)) * **Client Key**: Your Dialect client key (format: `dk_...`) ## Authentication Overview Dialect uses a two-part authentication system for inbox integration: 1. **Client Key** (`X-Dialect-Client-Key`): Identifies your application 2. **JWT Token** (`Authorization: Bearer`): Authenticates the user via wallet signature The authentication flow involves: 1. **Prepare**: Generate a message/transaction for the user to sign 2. **Sign**: User signs with their wallet 3. **Verify**: Exchange the signature for a JWT token 4. **Use**: Include the JWT token in subsequent API calls ### Flow diagram ```mermaid theme={null} sequenceDiagram participant App as Your App participant API as Dialect API App->>API: 1. POST /v2/auth/solana/prepare API-->>App: Message to sign App->>App: 2. Sign with wallet App->>API: 3. POST /v2/auth/solana/verify API-->>App: JWT Token opt Verify App->>API: GET /v2/auth API-->>App: Wallet address end ``` ## Code examples ### Step 1: Prepare Authentication Request a message for the user to sign: ```typescript TypeScript theme={null} const prepareResponse = await fetch('https://alerts-api.dial.to/v2/auth/solana/prepare', { method: 'POST', headers: { 'X-Dialect-Client-Key': 'YOUR_CLIENT_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ walletAddress: 'USER_WALLET_ADDRESS' }) }); const { message } = await prepareResponse.json(); // Returns: { message: "Sign this message to authenticate. Nonce: 1638471298347" } ``` ```bash cURL theme={null} curl -X POST https://alerts-api.dial.to/v2/auth/solana/prepare \ -H "X-Dialect-Client-Key: YOUR_CLIENT_KEY" \ -H "Content-Type: application/json" \ -d '{"walletAddress": "USER_WALLET_ADDRESS"}' ``` ### Step 2: Sign Message Have the user sign the message with their wallet: ```typescript theme={null} // Using @solana/wallet-adapter import { useWallet } from '@solana/wallet-adapter-react'; const { signMessage, publicKey } = useWallet(); const encodedMessage = new TextEncoder().encode(message); const signature = await signMessage(encodedMessage); const signatureBase58 = bs58.encode(signature); ``` ### Step 3: Verify & Get JWT Exchange the signature for a JWT token: ```typescript TypeScript theme={null} const verifyResponse = await fetch('https://alerts-api.dial.to/v2/auth/solana/verify', { method: 'POST', headers: { 'X-Dialect-Client-Key': 'YOUR_CLIENT_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ message: message, signature: signatureBase58 }) }); const { token } = await verifyResponse.json(); // Store this JWT token for subsequent API calls // Note: JWT tokens have a 1 year lifetime ``` ```bash cURL theme={null} curl -X POST https://alerts-api.dial.to/v2/auth/solana/verify \ -H "X-Dialect-Client-Key: YOUR_CLIENT_KEY" \ -H "Content-Type: application/json" \ -d '{"message": "SIGNED_MESSAGE", "signature": "SIGNATURE_BASE58"}' ``` ## Making Authenticated Requests You've setup the authentication flow. From now on, you can send requests to the API. Include both headers in all subsequent API calls: ```typescript theme={null} const response = await fetch('https://alerts-api.dial.to/v2/history', { headers: { 'Authorization': `Bearer ${jwtToken}`, 'X-Dialect-Client-Key': 'YOUR_CLIENT_KEY' } }); ``` ## Token Lifetime JWT tokens issued through the authentication flow have a **1 year lifetime**. After this period, users will need to re-authenticate by signing a new message to obtain a fresh token. # Integrate alerts inbox via API Source: https://docs.dialect.to/alerts/integrate-inbox/api/index Build notification inbox functionality using Dialect's REST API. This approach provides flexibility for applications that need server-side user management, custom authentication flows, or integration with non-JavaScript environments. # Integrate alerts inbox via API Build notification inbox functionality using Dialect's REST API. This approach provides flexibility for applications that need server-side user management, custom authentication flows, or integration with non-JavaScript environments. ## Why Use the REST API for Integration? ### Use-Cases * Server-side user subscription management * Custom authentication and user flows * Non-React or non-TypeScript applications * Microservices handling user notification preferences * Backend services managing notification history and read states ### Key Capabilities * **User Authentication**: Wallet-based authentication with JWT tokens * **Subscription Management**: Subscribe/unsubscribe users to apps and channels * **Topic Management**: Manage notification categories and user preferences * **Notification History**: Retrieve, mark as read, clear, and manage user notifications * **Push Notifications**: Complete mobile push notification setup ## Prerequisites Before integrating the notification inbox API, ensure you have completed: * **App Registration**: Your app must be registered with Dialect ([registration guide](/alerts/setup/register-app)) * **Client Key**: Obtain your client key for API authentication ## Integration Guide Follow this recommended implementation flow: 1. **[Authentication](/alerts/integrate-inbox/api/authentication)** - Set up wallet-based user authentication using JWT tokens and client keys 2. **[User Management](/alerts/integrate-inbox/user-management)** - Understand subscription, channel, and topic management (see API tabs in each section) 3. **[Notifications](/alerts/integrate-inbox/api/notifications)** - Retrieve notification history and manage read states 4. **[Push Notifications](/alerts/integrate-inbox/api/push-notifications)** - Complete mobile push notification integration ## API vs SDK vs Components **Choose the API approach when you need:** * ✅ Server-side user management * ✅ Custom authentication flows * ✅ Non-JavaScript/React environments * ✅ Microservices architecture * ✅ Full control over user data and flows **Consider the alternatives:** * **[React Components](/alerts/integrate-inbox/sdk/setup-configuration)** - For frontend React applications with built-in UI * **[TypeScript SDK](/alerts/integrate-inbox/sdk/setup-configuration)** - For frontend applications needing programmatic control ## What's Next? Ready to start building? Begin with [Authentication](/alerts/integrate-inbox/api/authentication) to set up wallet-based user authentication, then explore the comprehensive user management capabilities in our [User Management guide](/alerts/integrate-inbox/user-management). # Notifications Source: https://docs.dialect.to/alerts/integrate-inbox/api/notifications Retrieve and manage user notification history, read states, and notification data via API for building custom inbox experiences ## Prerequisites Before managing notifications, ensure you have: * **App Registration**: Your app must be registered with Dialect ([registration guide](/alerts/setup/register-app)) * **User Authentication**: Users must be authenticated with JWT tokens ([authentication guide](/alerts/integrate-inbox/api/authentication)) ## Best Practices Use pagination (25-50 per page), cache data, and implement virtual scrolling for large lists. Show unread counts, "mark all read" buttons, and relative timestamps with graceful empty states. Poll summary endpoint periodically and update local state optimistically when marking as read. ## Notification History ### Get Full Notification History Retrieve paginated notification history for the authenticated user: ```typescript TypeScript theme={null} const response = await fetch('https://alerts-api.dial.to/v2/history?limit=25', { headers: { 'Authorization': `Bearer ${jwtToken}`, 'X-Dialect-Client-Key': 'YOUR_CLIENT_KEY' } }); const data = await response.json(); ``` ```bash cURL theme={null} curl -X GET "https://alerts-api.dial.to/v2/history?limit=25" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "X-Dialect-Client-Key: YOUR_CLIENT_KEY" ``` **Query Parameters:** * `appId` (optional): Filter notifications for a specific app * `limit` (optional): Number of notifications to return (default: 25, max: 50) * `cursor` (optional): Base64-encoded cursor for pagination **Response:** ```json theme={null} { "notifications": [ { "id": "notification-uuid", "title": "Swap Completed! 📈", "body": "Successfully swapped 100 USDC to SOL", "image": "https://example.com/notification-image.png", "timestamp": "2024-01-15T10:30:00Z", "readAt": null, "app": { "id": "app-uuid", "name": "SolTrader", "image": "https://example.com/app-logo.png" }, "actions": [ { "type": "link", "label": "View on Solscan", "url": "https://solscan.io/tx/3x7Kj9mNp..." } ], "data": { "type": "transaction", "transactionId": "3x7Kj9mNp...", "amount": "100", "fromToken": "USDC", "toToken": "SOL" } } ], "hasMore": true, "nextCursor": "eyJpZCI6ImFsZXJ0XzEyMyIsInRpbWVzdGFtcCI6MTcwNTMwNzQwMH0=" } ``` ### Working with Custom Data The `data` field in notifications contains custom key-value pairs that were included when the [alert was sent](/alerts/send/api/send-messages#step-4%3A-custom-data). This data is perfect for implementing deep linking, passing transaction context, or routing users to specific screens in your application. **Example from the response above:** ```json theme={null} "data": { "type": "transaction", "transactionId": "3x7Kj9mNp...", "amount": "100", "fromToken": "USDC", "toToken": "SOL" } ``` **Common use cases:** ```typescript theme={null} // Example 1: Navigate to transaction screen in your app if (notification.data?.type === 'transaction') { navigateToTransaction({ transactionId: notification.data.transactionId, amount: notification.data.amount, fromToken: notification.data.fromToken, toToken: notification.data.toToken }); } // Example 2: Open external block explorer if (notification.data?.transactionId) { openExternalUrl(`https://solscan.io/tx/${notification.data.transactionId}`); } ``` The `data` object is optional. Not all notifications will include custom data. Always check if it exists before accessing its properties. For more information see also the [OpenAPI specification](/api-reference/integrate-an-inbox/get-notification-history) ### Pagination Example ```typescript theme={null} async function getAllNotifications() { const notifications = []; let cursor = null; let hasMore = true; while (hasMore) { const url = cursor ? `https://alerts-api.dial.to/v2/history?cursor=${cursor}&limit=50` : 'https://alerts-api.dial.to/v2/history?limit=50'; const response = await fetch(url, { headers: { 'Authorization': `Bearer ${jwtToken}`, 'X-Dialect-Client-Key': 'YOUR_CLIENT_KEY' } }); const data = await response.json(); notifications.push(...data.notifications); hasMore = data.hasMore; cursor = data.nextCursor; } return notifications; } ``` ### Get Notification Summary Get unread count and summary information without full notification data: ```typescript TypeScript theme={null} const response = await fetch('https://alerts-api.dial.to/v2/history/summary', { headers: { 'Authorization': `Bearer ${jwtToken}`, 'X-Dialect-Client-Key': 'YOUR_CLIENT_KEY' } }); const summary = await response.json(); ``` ```bash cURL theme={null} curl -X GET "https://alerts-api.dial.to/v2/history/summary" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "X-Dialect-Client-Key: YOUR_CLIENT_KEY" ``` **Query Parameters:** * `appId` (optional): Filter summary for a specific app **Response:** ```json theme={null} { "unreadCount": 5, "lastUnreadNotification": { "id": "notification-uuid", "title": "Price Alert! 🚨", "body": "SOL reached your target price of $150", "timestamp": "2024-01-15T14:20:00Z", "app": { "id": "app-uuid", "name": "PriceTracker" } } } ``` ### Mark Notifications as Read Mark all notifications as read for the authenticated user: ```typescript TypeScript theme={null} const response = await fetch('https://alerts-api.dial.to/v2/history/read', { method: 'POST', headers: { 'Authorization': `Bearer ${jwtToken}`, 'X-Dialect-Client-Key': 'YOUR_CLIENT_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ appId: 'YOUR_APP_ID' // Optional: mark as read for specific app only }) }); ``` ```bash cURL theme={null} curl -X POST "https://alerts-api.dial.to/v2/history/read" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "X-Dialect-Client-Key: YOUR_CLIENT_KEY" \ -H "Content-Type: application/json" \ -d '{"appId": "YOUR_APP_ID"}' ``` **Request Body:** * `appId` (optional): If provided, only marks notifications from this app as read **Response:** ```json theme={null} {} ``` ### Clear Notification History Clear all notifications for the authenticated user. This is a global action that applies across all clients: ```typescript TypeScript theme={null} const response = await fetch('https://alerts-api.dial.to/v2/history/clear', { method: 'POST', headers: { 'Authorization': `Bearer ${jwtToken}`, 'X-Dialect-Client-Key': 'YOUR_CLIENT_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ appId: 'YOUR_APP_ID' // Optional: clear notifications for specific app only }) }); ``` ```bash cURL theme={null} curl -X POST "https://alerts-api.dial.to/v2/history/clear" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "X-Dialect-Client-Key: YOUR_CLIENT_KEY" \ -H "Content-Type: application/json" \ -d '{"appId": "YOUR_APP_ID"}' ``` **Request Body:** * `appId` (optional): Single app ID, array of app IDs, or omitted to clear notifications from all apps **Response:** ```json theme={null} {} ``` ## Working with Blink Actions Alerts can now include blinks, enabling users to complete onchain transactions directly from your notification feed. This works as a two-step process: When [Dialect's mapping service](/alerts/blinks-in-alerts#mapping-service) detects supported protocol URLs in notification actions, it automatically enriches them with a `context` object containing: * `type`: The kind of data available (e.g., `"position"` for lending/borrowing positions) * `apiUrl`: A pre-filled API endpoint to fetch detailed data * `action`: The suggested action for the user Your client uses the `context.apiUrl` to fetch detailed data. The response structure depends on the `context.type`. For `type: "position"`, you'll receive position and market data from our Markets API, including blink URLs for onchain actions. Always check the `context.type` field to determine how to handle the data. Additional context types will be supported in the future beyond the current `"position"` type. In the following examples, we'll use Jupiter Borrow (a `type: "position"` context) to demonstrate this flow. ### Detecting Blink-Enabled Notifications Check if a notification has blink context by looking for the `context` object in actions. When present, check the `type` field to determine how to handle the data: ```typescript {11-15} theme={null} // Notification with blink context const notification = { "id": "notification-uuid", "title": "Liquidation Warning", "body": "Your SOL/USDC position is at 78% LTV, approaching liquidation threshold of 80%.", "timestamp": "2024-01-15T10:30:00Z", "actions": [{ "type": "link", "label": "Add Collateral", "url": "https://jup.ag/lend/borrow/1/nfts/2433", "context": { "type": "position", "apiUrl": "https://markets.dial.to/api/v0/positions/owners?walletAddresses=6JpNV6DK88auwzKVizdeT4Bw3D44sam5GqjcPCJ7y176&bundleIds=jupiter.borrow.1.2433", "action": "deposit" } }] }; // Check if action has blink context and get the type const action = notification.actions?.[0]; if (action?.context) { const contextType = action.context.type; // e.g., "position" // Handle based on context type if (contextType === 'position') { // Fetch position data from Markets API fetchPositionData(action.context.apiUrl); } // Future context types can be added here } ``` The `context` object is only present when Dialect's mapping service detects a supported protocol URL. Always check `context.type` to handle different data structures appropriately. ### Fetching Position Data For `type: "position"` contexts, use the `apiUrl` to fetch position and market data from the [Markets and Positions API](/api-reference/positions/list-all-market-positions-by-wallet-address): ```typescript theme={null} async function fetchPositionData(apiUrl: string) { const response = await fetch(apiUrl, { headers: { 'x-dialect-api-key': 'pk_demo' } }); const data = await response.json(); return data.positions; // Array of positions (deposit/borrow sides) } // Usage const action = notification.actions[0]; const positions = await fetchPositionData(action.context.apiUrl); // Find the deposit position const depositPosition = positions.find(p => p.side === 'deposit'); // Access position data (more examples below) console.log('LTV:', depositPosition.ltv); console.log('Amount:', depositPosition.amount, depositPosition.market.token.symbol); console.log('Liquidation Price:', depositPosition.liquidationPrice); // Get blink URL for deposit action const blinkUrl = depositPosition.actions.deposit.blinkUrl; // blink:https://jupiter.dial.to/api/v0/lend/borrow/1/deposit?positionId=2433 ``` ```bash theme={null} curl 'https://markets.dial.to/api/v0/positions/owners?walletAddresses=6JpNV6DK88auwzKVizdeT4Bw3D44sam5GqjcPCJ7y176&bundleIds=jupiter.borrow.1.2433' \ --header 'x-dialect-api-key: pk_demo' ``` ### Response Structure The [Markets and Positions API](/api-reference/positions/list-all-market-positions-by-wallet-address) returns an array of positions. For a lending position like Jupiter Borrow, you'll receive both deposit and borrow sides as well as all relevant market data: ```json theme={null} { "positions": [ { "id": "jupiter.borrow.1.2433.deposit", "type": "lending", "marketId": "jupiter.borrow.1", "ownerAddress": "6JpNV6DK88auwzKVizdeT4Bw3D44sam5GqjcPCJ7y176", "bundleId": "jupiter.borrow.1.2433", "side": "deposit", "additionalData": { "vaultId": 1, "nftId": 2433 }, "actions": { "deposit": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/deposit?positionId=2433" }, "withdraw": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/2433/withdraw" } }, "amount": 0.015060007, "amountUsd": 3.34, "ltv": 0.3033, "liquidationPrice": 84.00917409932147, "market": { "id": "jupiter.borrow.1", "type": "lending", "provider": { "id": "jupiter", "name": "Jupiter", "icon": "https://imagedelivery.net/C7jfNnfrjpAYWW6YevrFDg/5c23c065-89ef-413b-8deb-6d8c6a849300/public" }, "token": { "address": "So11111111111111111111111111111111111111112", "symbol": "WSOL", "decimals": 9, "icon": "https://coin-images.coingecko.com/coins/images/21629/large/solana.jpg?1696520989" }, "borrowToken": { "address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "symbol": "USDC", "decimals": 6, "icon": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png?1696506694" }, "websiteUrl": "https://jup.ag/lend/borrow/1", "depositApy": 0.0784, "baseDepositApy": 0.0484, "borrowApy": 0.0597, "baseBorrowApy": 0.0597, "totalDeposit": 665220.958436127, "totalDepositUsd": 147340132.39, "totalBorrow": 80014008.8212, "totalBorrowUsd": 79990184.41, "maxBorrow": 120021000.673021, "maxLtv": 0.75, "liquidationLtv": 0.8, "liquidationPenalty": 0.01, "additionalData": {}, "actions": { "deposit": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/deposit" } } } }, { "id": "jupiter.borrow.1.2433.borrow", "type": "lending", "marketId": "jupiter.borrow.1", "ownerAddress": "6JpNV6DK88auwzKVizdeT4Bw3D44sam5GqjcPCJ7y176", "bundleId": "jupiter.borrow.1.2433", "side": "borrow", "additionalData": { "vaultId": 1, "nftId": 2433 }, "actions": { "borrow": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/2433/borrow" }, "repay": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/2433/repay" } }, "amount": 1.012143, "amountUsd": 1.01, "ltv": 0.3033, "liquidationPrice": 84.00917409932147, "market": { "id": "jupiter.borrow.1", "type": "lending", "provider": { "id": "jupiter", "name": "Jupiter", "icon": "https://imagedelivery.net/C7jfNnfrjpAYWW6YevrFDg/5c23c065-89ef-413b-8deb-6d8c6a849300/public" }, "token": { "address": "So11111111111111111111111111111111111111112", "symbol": "WSOL", "decimals": 9, "icon": "https://coin-images.coingecko.com/coins/images/21629/large/solana.jpg?1696520989" }, "borrowToken": { "address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "symbol": "USDC", "decimals": 6, "icon": "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png?1696506694" }, "websiteUrl": "https://jup.ag/lend/borrow/1", "depositApy": 0.0784, "baseDepositApy": 0.0484, "borrowApy": 0.0597, "baseBorrowApy": 0.0597, "totalDeposit": 665220.958436127, "totalDepositUsd": 147340132.39, "totalBorrow": 80014008.8212, "totalBorrowUsd": 79990184.41, "maxBorrow": 120021000.673021, "maxLtv": 0.75, "liquidationLtv": 0.8, "liquidationPenalty": 0.01, "additionalData": {}, "actions": { "deposit": { "blinkUrl": "blink:https://jupiter.dial.to/api/v0/lend/borrow/1/deposit" } } } } ] } ``` ### Using the Data The response includes everything needed to build feature rich UIs ranging from simple notification buttons to detailed position management screens. Use the data to create cards, input interfaces, action buttons, and more. #### UI Component Examples Here are examples of different UI patterns you can build using the data from the example above. Each component uses specific fields from the API response to create rich, informative interfaces: **Position Widget** A compact position summary card showing key metrics at a glance. Position widget showing compact position metrics This widget uses: * `market.provider.name` and `market.provider.icon` for protocol branding (e.g., "Jupiter") * `market.token.symbol` and `market.token.icon` for collateral token display (e.g., "SOL") * `market.borrowToken.symbol` and `market.borrowToken.icon` for borrowed token display (e.g., "USDC") * `amount` and `amountUsd` from both deposit and borrow positions to show collateral and debt sizes * `ltv` for position health indicator (e.g., "Very Risky 78%") * `market.websiteUrl` for the external link to the protocol **Market Cards** Display multiple sides of a market in an easy to understand and actionable card layout. Cards showing multiple options for a market These cards use: * `market.token.symbol` and `market.token.icon` for collateral display (e.g., "SOL") * `market.borrowToken.symbol` and `market.borrowToken.icon` for debt display (e.g., "USDC") * `amount` and `amountUsd` from deposit and borrow positions for user's position sizes * `market.depositApy` for collateral yield (e.g., "7.2% APY") * `market.borrowApy` for borrow rate (e.g., "4.4% APY") * `actions.deposit.blinkUrl`, `actions.withdraw.blinkUrl`, `actions.borrow.blinkUrl`, `actions.repay.blinkUrl` for action buttons #### Available Data Fields by Group The API response contains extensive data organized into three main categories: * **Protocol**: `market.provider.name`, `market.provider.icon`, `market.provider.id` * **Tokens**: `market.token.symbol`, `market.token.address`, `market.token.icon`, `market.borrowToken.symbol`, `market.borrowToken.address`, `market.borrowToken.icon` * **Health**: `ltv`, `liquidationPrice` * **Amounts**: `amount`, `amountUsd` * **Identifiers**: `id`, `bundleId`, `marketId`, `ownerAddress` * **Blink URLs**: `actions.deposit.blinkUrl`, `actions.withdraw.blinkUrl`, `actions.borrow.blinkUrl`, `actions.repay.blinkUrl` * **Protocol website**: `market.websiteUrl` * **APY rates**: `market.depositApy`, `market.baseDepositApy`, `market.borrowApy`, `market.baseBorrowApy` * **Liquidation**: `market.liquidationLtv`, `market.liquidationPenalty` * **Limits**: `market.maxLtv`, `market.maxBorrow` * **Totals**: `market.totalDeposit`, `market.totalDepositUsd`, `market.totalBorrow`, `market.totalBorrowUsd` * **Decimals**: `market.token.decimals`, `market.borrowToken.decimals` # Push Notifications Source: https://docs.dialect.to/alerts/integrate-inbox/api/push-notifications Enable mobile push notifications to reach users instantly on their devices, even when your app was closed ## How Push Notifications Work with Dialect Please note that it is requried to subscribe your user to the [basic alerts endpoint](/api-reference/integrate-an-inbox/subscribe-to-alerts) as well to ensure push notifications are delivered. Users subscribe to push notifications via your app or notification components via the [push notification endpoint](/api-reference/integrate-an-inbox/subscribe-to-push-notifications). If event occurs, Dialect sends notifications to subscribed users and triggers the send process Firebase handles device token management and delivers push notifications to the user's device ## Prerequisites Before implementing push notifications with Dialect, please ensure you have completed the following steps: 1. **App Registration**: Your app must be registered with Dialect ([registration guide](/alerts/setup/register-app)) 2. **User Authentication**: Users must be authenticated with JWT tokens ([authentication guide](/alerts/integrate-inbox/api/authentication)) 3. **Set up Firebase Cloud Messaging** in your application * Follow [Firebase's documentation](https://firebase.google.com/docs/cloud-messaging) to add the Firebase SDK to your app * Implement the necessary code to request and receive an FCM token 4. **Implement wallet integration** for Solana signature verification * Your mobile app must be able to request message signing from the user's wallet 5. **Provide Service Account Credentials** to Dialect for authentication * Dialect uses your service account credentials to authenticate and send push notifications to your users * To get started, create a service account with the `cloudmessaging.messages.create` rights * Once created, contact us at [hello@dialect.to](mailto:hello@dialect.to) to provide your service account credentials * Dialect will then connect your app with our Alerts infrastructure Once these prerequisites are in place, you can proceed with the subscription and notification flows. ## Push Subscription Flow Once a user is authenticated, you can register their device to receive push notifications. This involves obtaining an FCM token and associating it with the user's wallet address. ```mermaid theme={null} sequenceDiagram participant App as Your App participant Firebase as Firebase SDK participant API as Dialect API App->>Firebase: 1. Request FCM Token Firebase-->>App: FCM Token App->>API: 2. POST /v2/push/subscribe Note right of App: Send JWT + FCM Token API-->>App: Subscription confirmed ``` ## Get FCM Token Before you can subscribe to push notifications, you need to obtain an FCM token from Firebase: ```typescript theme={null} import messaging from '@react-native-firebase/messaging'; async function getFCMToken() { try { // Request permission (iOS) const authStatus = await messaging().requestPermission(); const enabled = authStatus === messaging.AuthorizationStatus.AUTHORIZED || authStatus === messaging.AuthorizationStatus.PROVISIONAL; if (enabled) { // Get FCM token const fcmToken = await messaging().getToken(); console.log('FCM Token:', fcmToken); return fcmToken; } } catch (error) { console.error('Failed to get FCM token:', error); } } ``` ```swift theme={null} import Firebase func getFCMToken() { Messaging.messaging().token { token, error in if let error = error { print("Error fetching FCM registration token: \(error)") } else if let token = token { print("FCM registration token: \(token)") // Use this token to subscribe to Dialect push notifications } } } ``` ```kotlin theme={null} import com.google.firebase.messaging.FirebaseMessaging fun getFCMToken() { FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> if (!task.isSuccessful) { Log.w(TAG, "Fetching FCM registration token failed", task.exception) return@addOnCompleteListener } // Get new FCM registration token val token = task.result Log.d(TAG, "FCM Token: $token") // Use this token to subscribe to Dialect push notifications } } ``` ```javascript theme={null} import { getMessaging, getToken } from 'firebase/messaging'; async function getFCMToken() { const messaging = getMessaging(); try { const currentToken = await getToken(messaging, { vapidKey: 'YOUR_VAPID_KEY' }); if (currentToken) { console.log('FCM Token:', currentToken); return currentToken; } else { console.log('No registration token available.'); } } catch (err) { console.log('An error occurred while retrieving token. ', err); } } ``` For more detailed implementation instructions, visit the Firebase Cloud Messaging documentation for [iOS](https://firebase.google.com/docs/cloud-messaging/ios/client), [Android](https://firebase.google.com/docs/cloud-messaging/android/client), and [Web](https://firebase.google.com/docs/cloud-messaging/js/client). ## Push Subscription Management ### Subscribe to Push Notifications Once you have an FCM token, subscribe the user to push notifications: ```typescript theme={null} async function subscribeToDialectPush(fcmToken: string, jwtToken: string, clientKey: string) { try { const response = await fetch('https://alerts-api.dial.to/v2/push/subscribe', { method: 'POST', headers: { 'Authorization': `Bearer ${jwtToken}`, 'X-Dialect-Client-Key': clientKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ fcmToken: fcmToken, deviceId: 'unique-device-identifier', // Optional but recommended appId: 'YOUR_APP_ID' // Optional - for app-specific subscriptions }) }); if (response.ok) { console.log('Successfully subscribed to push notifications'); } else { console.error('Failed to subscribe to push notifications'); } } catch (error) { console.error('Push subscription error:', error); } } ``` **Parameters:** * `fcmToken`: Firebase Cloud Messaging token from your mobile app * `deviceId`: (Optional) Unique device identifier for managing devices * `appId`: (Optional) Subscribe to pushes for specific app only ### Unsubscribe from Push Notifications ```typescript theme={null} async function unsubscribeFromDialectPush(fcmToken: string, jwtToken: string, clientKey: string) { try { const response = await fetch('https://alerts-api.dial.to/v2/push/unsubscribe', { method: 'POST', headers: { 'Authorization': `Bearer ${jwtToken}`, 'X-Dialect-Client-Key': clientKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ fcmToken: fcmToken, deviceId: 'unique-device-identifier', // Should match subscription appId: 'YOUR_APP_ID' // Optional }) }); if (response.ok) { console.log('Successfully unsubscribed from push notifications'); } } catch (error) { console.error('Push unsubscription error:', error); } } ``` ## Handling Push Notifications ### Processing Received Notifications ```typescript theme={null} import messaging from '@react-native-firebase/messaging'; import { useEffect } from 'react'; export function usePushNotifications() { useEffect(() => { // Handle notifications when app is in foreground const unsubscribeForeground = messaging().onMessage(async remoteMessage => { console.log('Foreground notification:', remoteMessage); // Show in-app notification or update notification list handleDialectNotification(remoteMessage); }); // Handle notifications when app is opened from background const unsubscribeBackground = messaging().onNotificationOpenedApp(remoteMessage => { console.log('Background notification opened:', remoteMessage); // Navigate to specific screen or perform action handleNotificationAction(remoteMessage); }); // Handle notifications when app is opened from quit state messaging() .getInitialNotification() .then(remoteMessage => { if (remoteMessage) { console.log('App opened from quit state:', remoteMessage); handleNotificationAction(remoteMessage); } }); return () => { unsubscribeForeground(); unsubscribeBackground(); }; }, []); } function handleDialectNotification(remoteMessage: any) { const { notification, data } = remoteMessage; // Dialect notifications include: // - notification.title // - notification.body // - notification.image (optional) // - data.* (custom data from sender) console.log('Notification title:', notification?.title); console.log('Notification body:', notification?.body); console.log('Custom data:', data); // Update your notification state/context // addNotificationToInbox(remoteMessage); } function handleNotificationAction(remoteMessage: any) { const { data } = remoteMessage; // Handle deep linking based on notification data if (data?.action === 'open_trade') { // Navigate to trade screen navigateToTrade(data.tradeId); } else if (data?.action === 'open_profile') { // Navigate to profile screen navigateToProfile(data.userId); } } ``` ```swift theme={null} import UserNotifications class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { // Handle notifications when app is in foreground func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { let userInfo = notification.request.content.userInfo handleDialectNotification(userInfo: userInfo) // Show notification banner completionHandler([.banner, .sound, .badge]) } // Handle notification taps func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo handleNotificationAction(userInfo: userInfo) completionHandler() } func handleDialectNotification(userInfo: [AnyHashable: Any]) { // Extract notification data let title = userInfo["gcm.notification.title"] as? String let body = userInfo["gcm.notification.body"] as? String print("Notification: \(title ?? "") - \(body ?? "")") // Update your app state // NotificationManager.shared.addNotification(userInfo) } func handleNotificationAction(userInfo: [AnyHashable: Any]) { // Handle deep linking if let action = userInfo["action"] as? String { switch action { case "open_trade": if let tradeId = userInfo["tradeId"] as? String { navigateToTrade(tradeId: tradeId) } case "open_profile": if let userId = userInfo["userId"] as? String { navigateToProfile(userId: userId) } default: break } } } } ``` ```kotlin theme={null} import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) // Handle Dialect notification handleDialectNotification(remoteMessage) } private fun handleDialectNotification(remoteMessage: RemoteMessage) { val notification = remoteMessage.notification val data = remoteMessage.data Log.d(TAG, "Notification title: ${notification?.title}") Log.d(TAG, "Notification body: ${notification?.body}") Log.d(TAG, "Notification data: $data") // Create and show notification createNotification( title = notification?.title ?: "New Notification", body = notification?.body ?: "", data = data ) } private fun createNotification(title: String, body: String, data: Map) { val intent = Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK // Add notification data for deep linking data.forEach { (key, value) -> putExtra(key, value) } } val pendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_IMMUTABLE ) val notification = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(title) .setContentText(body) .setAutoCancel(true) .setContentIntent(pendingIntent) .build() val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.notify(System.currentTimeMillis().toInt(), notification) } } ``` ## Push Notification Data Structure Dialect push notifications include standard notification content plus custom data: ```json theme={null} { "notification": { "title": "Trade Executed! 📈", "body": "Your buy order for 10 SOL has been executed at $142.50", "image": "https://example.com/notification-image.png" }, "data": { "action": "open_trade", "tradeId": "123", "amount": "10", "price": "142.50", "appId": "your-app-id", "notificationId": "notification-uuid" } } ``` ## Advanced Configuration ### App-Specific Push Subscriptions Subscribe to push notifications for specific apps only: ```typescript theme={null} // Subscribe to pushes for all apps (default) await subscribeToDialectPush(fcmToken, jwtToken, clientKey); // Subscribe to pushes for specific app only await subscribeToDialectPush(fcmToken, jwtToken, clientKey, deviceId, 'specific-app-id'); ``` ### Notification Categories and Topics Push notifications respect user subscription preferences: * **Channel subscriptions**: Users must be subscribed to apps via channels (IN\_APP, EMAIL, etc.) * **Topic subscriptions**: If your app uses topics, push notifications will only be sent for subscribed topics * **Push preference**: Users must specifically enable push notifications ### Custom Push Behavior Configure push notification behavior: ```typescript theme={null} // Send push notification with custom configuration const notificationData = { recipient: { type: 'subscriber', walletAddress: 'USER_WALLET_ADDRESS' }, channels: ['PUSH', 'IN_APP'], message: { title: 'Custom Push Notification', body: 'This includes custom data and actions' }, push: { playNotificationSound: true, // Play sound (default: true) showNotification: true // Show UI notification (default: true) }, data: { action: 'custom_action', deepLink: '/custom-path', customField: 'custom-value' } }; ``` ## Troubleshooting ### Common Issues **Push notifications not received:** * Verify FCM token is valid and current * Check if user is subscribed to push notifications via Dialect * Ensure app has notification permissions * Verify Firebase project configuration **Authentication errors:** * Ensure JWT token is valid and not expired * Verify client key is correct * Check if user has proper permissions **iOS-specific issues:** * Verify APNs certificate is properly configured in Firebase * Check iOS notification permissions * Ensure app is properly signed for push notifications ## Best Practices **User Experience**: Request push permission at the right moment (not immediately on app launch), clearly explain the value of push notifications, provide easy opt-out options, and handle permission denials gracefully. **Technical Implementation**: Keep FCM tokens updated when they change, handle token refresh properly, implement proper error handling for network failures, and use unique device IDs for better device management. ## Next Steps With push notifications integrated, you have a complete notification system: 1. **[User Management](/alerts/integrate-inbox/user-management)** - Manage user subscriptions and preferences 2. **[Notification History](/alerts/integrate-inbox/api/notifications)** - Retrieve and display notification history 3. **[Authentication](/alerts/integrate-inbox/api/authentication)** - Handle user authentication flows Your users can now receive notifications via in-app messages, email, and push notifications across all their devices! # Introduction Source: https://docs.dialect.to/alerts/integrate-inbox/index Integrate notification inboxes into your applications to let users view, manage, and interact with their notifications across multiple apps and channels. Dialect provides flexible integration options to fit different technical requirements and use cases. ![Image with alerts delivered via multiple channels](https://res.cloudinary.com/dnxjhra4h/image/upload/f_auto,q_auto/dialect-docs/alerts-meet-anywhere.png) ## Integration Options Choose the approach that best fits your application architecture: ### 🎨 React Components (Recommended) Pre-built React components for quick integration with minimal setup. Perfect for React applications that want a complete notification experience out of the box. * **NotificationsButton**: Complete done-for-you solution with built-in UI * **Notifications**: Standalone component with full styling control * Built-in user management and authentication flows **[Get started with React Components →](/alerts/integrate-inbox/sdk/setup-configuration)** ### 🔧 REST API Direct API integration for maximum flexibility and control. Ideal for server-side implementations, custom authentication flows, or non-JavaScript environments. * Wallet-based authentication with JWT tokens * Complete subscription and topic management * Notification history and read state management * Mobile push notification setup **[Get started with REST API →](/alerts/integrate-inbox/api/index)** ### 🌐 Universal Inbox Enable cross-app notifications where users can subscribe to multiple applications and receive all their notifications in one unified feed. * Subscribe to notifications from multiple apps (Drift, Bonk, Sanctum, etc.) * Single notification feed across the entire ecosystem * User-controlled cross-app messaging experience **[Learn about Universal Inbox →](/alerts/integrate-inbox/universal-inbox)** ## Key Features **📱 Multi-Channel Delivery** * In-app notifications with real-time updates * Mobile push notifications via Firebase * Email notifications for important updates **🎯 User Control** * Granular subscription management per app and channel * Topic-based notification categories * Read/unread state management **🔐 Secure Authentication** * Solana wallet-based authentication * JWT token management * Client key authorization **📊 Complete History** * Paginated notification history * Cross-app message aggregation * Filtering and search capabilities ## Prerequisites Before integrating notification inboxes, ensure you have: 1. **App Registration**: Your app must be registered with Dialect ([registration guide](/alerts/setup/register-app)) 2. **Client Key**: Obtain your client key for API authentication 3. **User Wallet Integration**: Ability to request wallet signatures for authentication ## User Management Foundation All integration approaches build on the same user management system. Understanding these concepts is crucial regardless of your chosen integration method: **[User Management Guide →](/alerts/integrate-inbox/user-management)** ## Quick Start Guide 1. **Choose Your Integration**: React Components for frontend apps, REST API for backend/custom implementations 2. **Review User Management**: Understand how subscriptions and channels work 3. **Set Up Authentication**: Implement wallet-based user authentication 4. **Build Notification Features**: Display notifications, manage subscriptions, handle push notifications 5. **Consider Universal Inbox**: Enable cross-app notifications for enhanced user experience ## What's Next? * **New to Dialect?** Start with [User Management](/alerts/integrate-inbox/user-management) to understand the foundation * **Building a React app?** Jump to [React Components](/alerts/integrate-inbox/sdk/setup-configuration) * **Need API control?** Begin with [REST API](/alerts/integrate-inbox/api/index) * **Want cross-app notifications?** Explore [Universal Inbox](/alerts/integrate-inbox/universal-inbox) # Notifications Source: https://docs.dialect.to/alerts/integrate-inbox/sdk/notifications Standalone notification feed component with complete styling control via CSS variables, perfect for embedding in custom layouts, modals, or sidebars ## Overview `Notifications` gives you maximum flexibility: * **Headless component** - no built-in button or modal * **Full CSS control** via CSS custom properties * **Embed anywhere** - sidebars, modals, dedicated pages * **Complete brand integration** with custom themes * **Advanced styling** for individual notification types **Choose this component if you want:** * ✅ Complete design control and custom styling * ✅ Embed in your own modal, sidebar, or layout * ✅ Full brand integration with CSS variables * ✅ Custom notification type styling * ❌ Need to build your own button/modal logic ## Prerequisites Before implementing the Notifications component, ensure you have completed: * **App Registration**: Your app must be registered with Dialect ([registration guide](/alerts/setup/register-app)) * **SDK Setup**: Configure the Dialect React SDK ([Setup & Configuration guide](/alerts/integrate-inbox/sdk/setup-configuration)) * **User Management Understanding**: Familiarize yourself with subscriptions and channels ([User Management guide](/alerts/integrate-inbox/user-management)) ## Basic Usage ### Simple Integration ```tsx theme={null} "use client"; import "@dialectlabs/react-ui/index.css"; import { DialectSolanaSdk } from "@dialectlabs/react-sdk-blockchain-solana"; import { Notifications } from "@dialectlabs/react-ui"; const DAPP_ADDRESS = process.env.NEXT_PUBLIC_DIALECT_DAPP_ADDRESS!; export function MyNotificationFeed() { return (
); } ``` ### With Close Button If you want users to be able to close the notification panel: ```tsx theme={null} "use client"; import "@dialectlabs/react-ui/index.css"; import { DialectSolanaSdk } from "@dialectlabs/react-sdk-blockchain-solana"; import { Notifications } from "@dialectlabs/react-ui"; import { useState } from "react"; const DAPP_ADDRESS = process.env.NEXT_PUBLIC_DIALECT_DAPP_ADDRESS!; export function NotificationPanel() { const [isOpen, setIsOpen] = useState(true); if (!isOpen) return null; return ( {isOpen ? (
) : ( )}
); } ``` ## Configuration Options ### Channel Configuration Control which additional notification channels users can configure: ```tsx theme={null} // Show all additional channels (default) // Limit to specific additional channels ``` Available additional channels: * `"email"` - Email notifications * `"telegram"` - Telegram bot notifications * **In-app** - Always enabled (not configurable) ## Custom Styling ### CSS Variables Overview The `Notifications` component uses CSS custom properties for complete theme control: ```css theme={null} /* Import Dialect styles first */ @import url('@dialectlabs/react-ui/index.css'); .dialect { /* Brand colors */ --dt-accent-brand: #09cbbf; --dt-accent-error: #f62d2d; /* Background colors */ --dt-bg-primary: #ffffff; --dt-bg-secondary: #f9f9f9; --dt-bg-tertiary: #f2f3f5; /* Text colors */ --dt-text-primary: #232324; --dt-text-secondary: #434445; --dt-text-tertiary: #737373; /* Border radius */ --dt-border-radius-s: 0.5rem; --dt-border-radius-m: 0.75rem; } ``` ### Basic Brand Customization Override CSS variables to match your brand: ```css theme={null} .dialect { --dt-accent-brand: #your-brand-color; --dt-bg-primary: #your-background; --dt-text-primary: #your-text-color; } ``` **Important:** Import order matters: ```tsx theme={null} // ✅ Correct order import "@dialectlabs/react-ui/index.css"; // Import first import "./custom-dialect-styles.css"; // Then your overrides ``` ### Complete CSS Variable Reference ```css theme={null} .dialect { /* Brand & Accent */ --dt-accent-brand: #09cbbf; --dt-accent-error: #f62d2d; --dt-brand-transparent: #b3b3b31a; /* Backgrounds */ --dt-bg-brand: #ebebeb; --dt-bg-primary: #ffffff; --dt-bg-secondary: #f9f9f9; --dt-bg-tertiary: #f2f3f5; /* Buttons */ --dt-button-primary: #2a2a2b; --dt-button-primary-disabled: #656564; --dt-button-primary-hover: #434445; --dt-button-secondary: #ebebeb; --dt-button-secondary-disabled: #f2f3f5; --dt-button-secondary-hover: #f2f3f5; /* Icons */ --dt-icon-primary: #2a2a2b; --dt-icon-secondary: #888989; --dt-icon-tertiary: #b3b3b3; /* Text */ --dt-text-accent: #08c0b4; --dt-text-inverse: #ffffff; --dt-text-primary: #232324; --dt-text-quaternary: #888989; --dt-text-secondary: #434445; --dt-text-tertiary: #737373; } ``` ```css theme={null} .dialect { /* Border radius */ --dt-border-radius-xs: 0.375rem; --dt-border-radius-s: 0.5rem; --dt-border-radius-m: 0.75rem; --dt-border-radius-l: 1rem; /* Borders & Strokes */ --dt-stroke-primary: #dee1e7; /* Inputs */ --dt-input-checked: #09cbbf; --dt-input-primary: #dee1e7; --dt-input-secondary: #ffffff; --dt-input-unchecked: #d7d7d7; } ``` ### Dark Theme The component automatically applies dark theme when using the `theme` prop: ```tsx theme={null} ``` You can also customize dark theme colors: ```css theme={null} .dialect[data-theme="dark"] { --dt-accent-brand: #09cbbf; --dt-bg-primary: #1b1b1c; --dt-bg-secondary: #232324; --dt-bg-tertiary: #2a2a2b; --dt-text-primary: #ffffff; --dt-text-secondary: #c4c6c8; /* ... customize other dark theme variables */ } ``` ### Auto Theme Detection Automatically switch themes based on user preferences: ```tsx theme={null} import { useState, useEffect } from 'react'; export function ThemeAwareNotifications() { const [theme, setTheme] = useState<'light' | 'dark'>('light'); useEffect(() => { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); setTheme(mediaQuery.matches ? 'dark' : 'light'); const handler = (e: MediaQueryListEvent) => { setTheme(e.matches ? 'dark' : 'light'); }; mediaQuery.addEventListener('change', handler); return () => mediaQuery.removeEventListener('change', handler); }, []); return (
); } ``` ## Layout Integration Examples ### Sidebar Integration ```tsx theme={null} export function AppWithNotificationSidebar() { const [showNotifications, setShowNotifications] = useState(false); return (
{showNotifications && ( )}
); } ``` ### Modal Integration ```tsx theme={null} import { Dialog } from '@headlessui/react'; export function NotificationModal() { const [isOpen, setIsOpen] = useState(false); return ( <>
); } ``` ### Full Page Integration ```tsx theme={null} export function NotificationsPage() { return (

Your Notifications

); } ``` ## Troubleshooting **Styling not applying:** * Ensure CSS import order is correct * Check CSS specificity - you may need `!important` * Verify CSS variables are defined on `.dialect` class **Component not displaying:** * Verify `dappAddress` is correct and app is registered * Check wallet connection status * Ensure parent container has appropriate dimensions **Performance issues:** * Consider lazy loading for modals/conditional displays * Use CSS containment for better rendering performance * Implement virtualization for large notification lists ## Next Steps * **Need a simpler solution?** Use the [NotificationsButton component](/alerts/integrate-inbox/sdk/notifications-button) * **Want to send notifications?** Check the [Send SDK documentation](/alerts/send/) The `Notifications` component provides complete control over notification display and styling, perfect for creating fully branded notification experiences. # NotificationsButton Source: https://docs.dialect.to/alerts/integrate-inbox/sdk/notifications-button Complete, done-for-you notification solution with built-in notification bell button, modal, user settings panel, and notification history ## Overview `NotificationsButton` includes everything you need for user notifications: * **Notification bell icon** with unread count badge * **Responsive modal** that opens when clicked * **User settings panel** for channel and topic management * **Notification history** with read/unread states * **Built-in authentication** and wallet integration **Choose this component if you want:** * ✅ Quick setup with minimal configuration * ✅ Professional notification UI out of the box * ✅ Built-in responsive design (mobile + desktop) * ✅ Light/dark theme options * ❌ Limited visual customization (theme prop only) ## Prerequisites Before implementing the NotificationsButton component, ensure you have completed: * **App Registration**: Your app must be registered with Dialect ([registration guide](/alerts/setup/register-app)) * **SDK Setup**: Configure the Dialect React SDK ([Setup & Configuration guide](/alerts/integrate-inbox/sdk/setup-configuration)) * **User Management Understanding**: Familiarize yourself with subscriptions and channels ([User Management guide](/alerts/integrate-inbox/user-management)) ## Basic Usage It is important that the `NotificationsButton` is always wrapped around the client SDK. We recommend writing a small component to ensure it is always the case. ### Simple Integration ```tsx theme={null} "use client"; import "@dialectlabs/react-ui/index.css"; import { DialectSolanaSdk } from "@dialectlabs/react-sdk-blockchain-solana"; import { NotificationsButton } from "@dialectlabs/react-ui"; const DAPP_ADDRESS = process.env.NEXT_PUBLIC_DIALECT_DAPP_ADDRESS!; export function MyNotificationButton() { return ( ); } ``` ### Theme Options Control the visual theme of the notification button and modal: ```tsx theme={null} ``` ```tsx theme={null} ``` ### Channel Configuration Control which additional notification channels users can configure. In-app notifications are always enabled by default. ```tsx theme={null} // Show all channels (default - includes email and telegram options) // Limit to specific channels ``` Available channels: * `"email"` - Email notifications * `"telegram"` - Telegram bot notifications * **In-app** - Always enabled (not configurable) ### Custom Button Replace the default notification bell with your own button component: ```tsx theme={null} import { NotificationsButton } from "@dialectlabs/react-ui"; export function CustomButtonNotifications() { return ( {({ open, setOpen, unreadCount, ref }) => ( )} ); } ``` **Render prop parameters:** * `open` - Boolean indicating if modal is open * `setOpen` - Function to control modal state * `unreadCount` - Number of unread notifications * `ref` - Required ref for the button element ### Custom Modal Replace the default modal container with your own: ```tsx theme={null} export function CustomModalNotifications() { return ( { if (!open) return null; return (
{children}
); }} />
); } ``` **Modal render prop parameters:** * `open` - Boolean indicating if modal should be shown * `setOpen` - Function to control modal state * `ref` - Required ref for the modal container * `children` - The notification content (must be rendered) ## Advanced Configuration ### Styling (Limited) #### Available Styling Options The `NotificationsButton` component has limited styling capabilities: ```tsx theme={null} // ✅ Theme options // ✅ CSS class for wrapper // ❌ CSS variables don't affect internal styling // ❌ Individual component styling not available ``` #### Wrapper Styling You can style the button wrapper, but internal components use fixed styling: ```css theme={null} .my-wrapper-class { /* This affects the button wrapper */ position: relative; z-index: 10; } /* These CSS variables WON'T affect NotificationsButton */ .dialect { --dt-accent-brand: #custom-color; /* No effect */ --dt-bg-primary: #custom-bg; /* No effect */ } ``` #### For Full Styling Control If you need complete visual customization, use the [standalone Notifications component](/alerts/integrate-inbox/sdk/notifications) instead: ```tsx theme={null} // Full styling control with CSS variables import { Notifications } from "@dialectlabs/react-ui";
``` ### Component Behavior #### Responsive Design The component automatically adapts to different screen sizes: * **Mobile**: Full-screen modal overlay * **Tablet/Desktop**: Positioned modal (420px × 600px) * **Button**: Scales appropriately for touch interfaces #### User Workflow 1. **Button Click**: User clicks notification bell 2. **Authentication**: User connects wallet (if not connected) 3. **Subscription**: User signs a message, thereby subscribes to your app 4. **Settings**: User can configure channels and topics 5. **Notifications**: User views notification history ## Troubleshooting ### Common Issues **Button not appearing:** * Verify `dappAddress` is correct and app is registered * Check that styles are imported before the component * Ensure wallet adapter is properly configured **Modal not opening:** * Check browser console for JavaScript errors * Verify wallet connection is working * Ensure `dappAddress` matches your registered app **Styling not working:** * CSS variables don't affect `NotificationsButton` styling * Use the `theme` prop for basic theming * Consider the standalone `Notifications` component for full control **Wallet connection issues:** * Verify wallet adapter compatibility * Check wallet permissions and connection state * Test with different wallet providers ### Development Tips * Use `environment="development"` during development * Test with different wallet providers * Verify notification flow end-to-end * Check network requests in browser dev tools ## Next Steps * **Need full styling control?** Use the [standalone Notifications component](/alerts/integrate-inbox/sdk/notifications) * **Want to send notifications?** Check the [Send SDK documentation](/alerts/send/) The `NotificationsButton` provides everything most applications need for user notifications with minimal setup. For advanced customization needs, consider the standalone [`Notifications`](/alerts/integrate-inbox/sdk/notifications) component. # Setup & Configuration Source: https://docs.dialect.to/alerts/integrate-inbox/sdk/setup-configuration Get the Dialect React SDK installed and configured for notification inbox integration with React hooks support and Solana wallet integration Get the Dialect React SDK installed and configured for notification inbox integration. This guide covers installation, client setup, authentication, and environment configuration for frontend applications. ## Prerequisites Before using the inbox components, ensure you have completed: * **App Registration**: Your app must be registered with Dialect ([registration guide](/alerts/setup/register-app)) * **React Environment**: React 16.8+ with hooks support * **Wallet Integration**: Solana wallet adapter or custom wallet implementation ## Installation Install the React UI package and blockchain-specific SDK for your needs: ```bash theme={null} # React UI components (required) npm install @dialectlabs/react-ui # Blockchain SDK for Solana npm install @dialectlabs/react-sdk-blockchain-solana ``` ```bash theme={null} # React UI components (required) yarn add @dialectlabs/react-ui # Blockchain SDK for Solana yarn add @dialectlabs/react-sdk-blockchain-solana ``` ```bash theme={null} # React UI components (required) pnpm add @dialectlabs/react-ui # Blockchain SDK for Solana pnpm add @dialectlabs/react-sdk-blockchain-solana ``` ## Basic SDK Setup ### Simple Integration For most applications, this basic setup is sufficient: ```tsx theme={null} "use client"; import "@dialectlabs/react-ui/index.css"; import { DialectSolanaSdk } from "@dialectlabs/react-sdk-blockchain-solana"; import { NotificationsButton } from "@dialectlabs/react-ui"; const DAPP_ADDRESS = "your-dapp-address"; export function App() { return (
{/* Your app content */}
); } ``` ### Custom Wallet Adapter If you need to use a custom wallet implementation: ```tsx theme={null} import { useMemo } from "react"; import { DialectSolanaSdk } from "@dialectlabs/react-sdk-blockchain-solana"; import { NotificationsButton } from "@dialectlabs/react-ui"; import { useMyCustomWallet } from "./my-wallet"; export function CustomWalletNotifications() { const wallet = useMyCustomWallet(); // Your wallet implementation const walletAdapter = useMemo( () => ({ publicKey: wallet.publicKey, // PublicKey | null signMessage: wallet.signMessage, // (msg: Uint8Array) => Promise signTransaction: wallet.signTransaction, // (tx: T) => Promise }), [wallet] ); return ( ); } ``` ### Environment Configuration Configure your Dialect SDK wrapper to switch between production and development environments: ```tsx theme={null} import { DialectSolanaSdk } from "@dialectlabs/react-sdk-blockchain-solana"; export function MyApp() { return ( {/* Your notification components */} ); } ``` ## Troubleshooting **Styles not loading correctly:** ```tsx theme={null} // ❌ Wrong - styles imported after component import { NotificationsButton } from "@dialectlabs/react-ui"; import "@dialectlabs/react-ui/index.css"; // ✅ Correct - styles imported first import "@dialectlabs/react-ui/index.css"; import { NotificationsButton } from "@dialectlabs/react-ui"; ``` **dappAddress not found:** * Ensure your app is registered with Dialect * Verify the dappAddress matches your registration * Check environment configuration (development vs production) **Wallet connection issues:** * Ensure wallet adapter is properly configured * Check wallet adapter version compatibility * Verify wallet permissions and connection state # Universal Inbox 📥 Source: https://docs.dialect.to/alerts/integrate-inbox/universal-inbox A shared, user-controlled, actionable communication layer across the entire Web3 ecosystem. Fully interoperable inboxes across Web3 applications The Universal Inbox represents the next evolution of messaging, transforming isolated app notifications into a shared, user-controlled communication layer across the entire ecosystem. ## What is a Universal Inbox? Traditionally, each application maintains its own dedicated inbox for users. When you use Drift, you receive Drift notifications. When you use Bonk, you get Bonk alerts. Each app operates in isolation, creating fragmented communication experiences. The Universal Inbox breaks down these silos by creating a **shared notification feed** where users can opt-in to receive messages from multiple applications they care about. Instead of checking separate inboxes across different apps, users have one central place to see all their notifications. ## How It Works The Universal Inbox builds directly on Dialect's existing notification infrastructure: ```mermaid theme={null} graph TB subgraph "Subscribed Apps" App1[🚀 Jito
MEV Rewards] App2[🐕 Bonk
Community Updates] App3[⚡ Sanctum
Staking Rewards] App4[📊 Your App
Custom Notifications] end App1 --> Inbox[📥 Universal Inbox] App2 --> Inbox App3 --> Inbox App4 --> Inbox Inbox --> User[👤 User] ``` ### Core Concepts **Cross-App Subscriptions**: Users can subscribe to notifications from any registered Dialect app, not just the one they're currently using. A user in your DeFi app could also receive liquidation alerts from Drift and governance updates from their favorite DAO. **User-Controlled**: Users have complete control over which apps they want to hear from and through which channels (push, email, in-app). **Shared Infrastructure**: All apps use the same Dialect messaging infrastructure, making cross-app communication seamless. The same APIs that power your app's notifications can deliver messages from any other registered app. ## Building on Existing Infrastructure The Universal Inbox doesn't require new APIs or endpoints—it leverages the notification system you're already familiar with: ### App Discovery & Subscription Users discover and subscribe to apps using the existing app management endpoints: * **[GET /v2/apps](https://alerts-api.dial.to/docs#tag/subscriber/GET/v2/apps)**: Lists all available apps users can subscribe to * **[POST /v2/subscribe](https://alerts-api.dial.to/docs#tag/subscriber/POST/v2/subscribe)**: Subscribe to notifications from specific apps * **[POST /v2/unsubscribe](https://alerts-api.dial.to/docs#tag/subscriber/POST/v2/unsubscribe)**: Unsubscribe from app notifications For implementation details, see the [User Management](/alerts/integrate-inbox/user-management) documentation. ### Unified Message History The notification history endpoints automatically aggregate messages from all subscribed apps: * **[GET /v2/history](https://alerts-api.dial.to/docs#tag/subscriber/GET/v2/history)**: Retrieves unified message history across all subscribed apps * **[GET /v2/history/summary](https://alerts-api.dial.to/docs#tag/subscriber/GET/v2/history/summary)**: Gets unread counts and latest messages * **[POST /v2/history/read](https://alerts-api.dial.to/docs#tag/subscriber/POST/v2/history/read)**: Marks messages as read For implementation details, see the [Notifications](/alerts/integrate-inbox/api/notifications) documentation. ### Cross-Channel Delivery Messages from all subscribed apps are delivered through the user's preferred channels: * **Push Notifications**: Mobile alerts from any subscribed app * **Email**: Emails from any subscribed app * **In-App**: Unified notification feed within your application For implementation details, see the [Push Notifications](/alerts/integrate-inbox/api/push-notifications) documentation. ## User Experience Benefits ### For Users * **Single Source of Truth**: One inbox for all notifications instead of scattered app-specific feeds * **Reduced Context Switching**: No need to check multiple apps for important updates * **Granular Control**: Choose exactly which apps to hear from and how * **Consistent Experience**: Familiar notification patterns across all apps ### For Developers * **Increased Engagement**: Users can discover and engage with your app through the unified feed * **Network Effects**: Benefit from the broader Dialect ecosystem of apps and users * **Reduced Development**: Leverage existing notification infrastructure instead of building from scratch * **Cross-App Integration**: Enable users to stay connected with your app even when using others ## Implementation Strategy To support the Universal Inbox in your application: ### 1. App Registration Ensure your app is properly registered with Dialect so users can discover and subscribe to it. See the [App Registration guide](/alerts/setup/register-app). ### 2. Request Universal Inbox Access Universal Inbox requires manual activation for your project. **Contact us at [hello@dialect.to](mailto:hello@dialect.to)** to request access. Our team will review your request and activate the feature for your app. ### 3. User Authentication Implement the authentication flow so users can manage their cross-app subscriptions. See the [Authentication documentation](/alerts/integrate-inbox/api/authentication). ### 4. Subscription Management Build UI that allows users to: * Browse available apps using the `/v2/apps` endpoint * Subscribe/unsubscribe from apps they're interested in * Manage notification preferences per app ### 5. Unified Inbox Display Create a notification inbox that shows messages from all subscribed apps: * Use `/v2/history` to fetch a unified message stream * Display app branding/icons to show message sources * Implement filtering options (by app, by type, etc.) ### 6. Cross-App Actions Consider implementing actions that work across apps: * "Subscribe to similar apps" recommendations * Cross-app notification batching * Unified notification settings ## Getting Started Ready to support the Universal Inbox? Start with these resources: 1. **[Authentication](/alerts/integrate-inbox/api/authentication)** - Set up user authentication for subscription management 2. **[User Management](/alerts/integrate-inbox/user-management)** - Implement app discovery and subscription flows 3. **[Notifications](/alerts/integrate-inbox/api/notifications)** - Build the unified message feed 4. **[Push Notifications](/alerts/integrate-inbox/api/push-notifications)** - Enable cross-app mobile notifications The Universal Inbox transforms Web3 from a collection of isolated apps into a connected ecosystem where users stay informed and engaged across their entire on-chain journey. # User Management Source: https://docs.dialect.to/alerts/integrate-inbox/user-management Manage user subscriptions, notification preferences, channel configurations, and topic organization for your Dialect integration. Manage user subscriptions, notification preferences, channel configurations, and topic organization. This section covers the complete user workflow from initial subscription to advanced notification management. ## Prerequisites Before managing users, ensure you have completed these steps: 1. **App Registration**: Register your app with Dialect (see the [registration guide](/alerts/setup/register-app) if you need to set this up) ## User Management Workflow Overview Dialect's user management follows a three-step process: 1. **User Subscription**: User subscribes to your app (via notification widget or SDK) and automatically gets IN\_APP notifications 2. **Channel Management**: User can add email and Telegram channels for additional notification delivery methods 3. **Topic Organization**: Configure notification categories and topics that users can selectively subscribe to ## Step 1: Subscription Management Before a user can receive messages, they have to be subscribed to your service. ### Subscribe user Create a subscription for a user's wallet address to your app. This automatically enables them to receive IN\_APP notifications. ```typescript theme={null} // Step 1: User subscribes to your app (gets IN_APP notifications automatically) const subscription = await dialectSolanaSdk.wallet.dappAddresses.create({ addressId: address.id, enabled: true, dappPublicKey, }); ``` Subscribe to notifications using react notifications component Users subscribe by connecting their wallet and signing a message in the notification widget. This automatically enables IN\_APP notifications. Subscribe a user to channels via API. You can specify which channels to subscribe to, or omit `channel` to subscribe to all available channels: ```bash theme={null} curl https://alerts-api.dial.to/v2/subscribe \ --request POST \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' \ --header 'Content-Type: application/json' \ --data '{ "appId": "YOUR_APP_ID", "channel": ["IN_APP", "EMAIL"] }' ``` * `appId`: Your application's unique identifier (optional if using client key) * `channel`: Channel or array of channels to subscribe to (`IN_APP`, `EMAIL`, `TELEGRAM`). If omitted, subscribes to all available channels * `Authorization`: Bearer token (see authentication section) * `X-Dialect-Client-Key`: Your client key The response will be an empty JSON object indicating success: ```json theme={null} {} ``` ### Enabling & disabling a subscription Enable/disable a subscription programmatically: ```typescript theme={null} const updatedSubscription = await dialectSolanaSdk.wallet.dappAddresses.update({ dappAddressId: subscription.id, enabled: false, }); ``` Users can enable or disable notifications for your app in the notification widget's settings panel. *No extra code needed—handled by the component UI.* Not supported via public API.\ Please use the Notification Components or SDK methods above. ### Getting subscriptions by id Retrieve a specific subscription using its unique ID: ```typescript theme={null} const specificSubscription = await dialectSolanaSdk.wallet.dappAddresses.find({ dappAddressId: subscription.id, }); ``` Users can view their current subscription status in the notification widget's settings panel. *No extra code needed—handled by the component UI.* Not supported via public API.\ Please use the Notification Components or SDK methods above. ### Getting subscriptions by wallet Retrieve all subscriptions for a wallet address: ```typescript theme={null} const allSubscriptions = await dialectSolanaSdk.wallet.dappAddresses.findAll({ dappPublicKey, // optional parameter addressIds: [address.id], // optional parameter }); ``` Users can view all their subscriptions in the notification widget's settings panel. *No extra code needed—handled by the component UI.* Get detailed subscription information for the authenticated user: ```bash theme={null} curl https://alerts-api.dial.to/v2/apps \ --request GET \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' ``` This returns comprehensive subscription details: ```json theme={null} { "apps": [ { "id": "app-uuid", "name": "Your App Name", "subscribed": true, "channels": [ { "channel": "IN_APP", "subscribed": true }, { "channel": "EMAIL", "subscribed": false }, { "channel": "TELEGRAM", "subscribed": true } ], "topics": [ { "id": "topic-uuid", "name": "Price Alerts", "slug": "price-alerts", "subscribed": true } ] } ] } ``` ### Getting subscribers by app Not available via SDK. Use the API method below for server-side subscriber management. App owners can view subscriber counts and details through their admin dashboard, not through the notification widget. *Subscriber management is handled server-side.* Get all subscribers for your app (app owner perspective): ```bash theme={null} curl https://alerts-api.dial.to/v2/{appId}/subscribers \ --request GET \ --header 'x-dialect-api-key: YOUR_API_KEY' ``` * `appId`: Your application's unique identifier * `offset`: (Optional) Pagination offset (default: 0) * `limit`: (Optional) Pagination limit (default: 1000, max: 10000) Response contains all wallet addresses subscribed to your app: ```json theme={null} { "subscribers": [ { "walletAddress": "6CxnSjtasq5Tzwb4b93AhLofXtiDvMpQ2vTkWdSZqTH7" }, { "walletAddress": "AnotherWalletAddress..." } ] } ``` ### Deleting a subscription Remove a subscription completely: ```typescript theme={null} await dialectSolanaSdk.wallet.dappAddresses.delete({ dappAddressId: subscription.id, }); ``` Users can unsubscribe from your app in the notification widget's settings panel. *No extra code needed—handled by the component UI.* Unsubscribe from an app or specific channels: ```bash theme={null} curl https://alerts-api.dial.to/v2/unsubscribe \ --request POST \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' \ --header 'Content-Type: application/json' \ --data '{ "appId": "YOUR_APP_ID", "channel": ["IN_APP", "EMAIL", "TELEGRAM"] }' ``` * `appId`: App ID or array of App IDs. If omitted, targets all apps available for the client * `channel`: Channel or array of channels to unsubscribe from. If omitted, unsubscribes from all channels To completely unsubscribe from an app, omit the `channel` parameter: ```bash theme={null} curl https://alerts-api.dial.to/v2/unsubscribe \ --request POST \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' \ --header 'Content-Type: application/json' \ --data '{ "appId": "YOUR_APP_ID" }' ``` ## Step 2: Channel Management After subscribing to your app, users can add additional notification channels like email and Telegram. Users manage addresses via the Dialect data service, where they may add, verify, update & remove addresses for these various channels. ### Add User Email Address Add an email address for a user to receive notifications via email. The address will need to be verified before notifications can be sent. ```typescript theme={null} // Step 2: User can add additional channels like email or Telegram const address = await dialectSolanaSdk.wallet.addresses.create({ type: AddressType.Email, value: "address@mailservice.com", }); ``` Users add and manage channels (email, Telegram) in the notification widget's settings screen. The component handles verification and management flows. ```tsx theme={null} ``` *In the settings panel, users can add, verify, and remove channels. No extra code needed!* Not supported via public API.\ Please use the Notification Components or SDK methods above. ### Verify an address Dialect uses verification codes to verify ownership of web2 channels such as email and Telegram. These codes are sent to the address in question. ```typescript theme={null} // Verify address (email or telegram). Constraint: there are // 3 attempts to verify address, otherwise use call below to send new // verification code const verifiedAddress = await dialectSolanaSdk.wallet.addresses.verify({ addressId: address.id, code: "1337", }); ``` If you did not receive the verification code, or if you failed to enter the correct value after several attempts, you can send a new code via the following call: ```typescript theme={null} // Resend verification code. Constraint: you must wait 60 sec before // resending the code. await dialectSolanaSdk.wallet.addresses.resendVerificationCode({ addressId: address.id, }); ``` Users verify their email/Telegram addresses in the notification widget's settings screen by entering the verification code sent to their address. *No extra code needed—handled by the component UI.* Not supported via public API.\ Please use the Notification Components or SDK methods above. ### Get addresses owned by a wallet Retrieve address information for a wallet. This shows all the notification channels (email, Telegram) that a user has configured. ```typescript theme={null} // Find all addresses owned by wallet const allAddresses = await dialectSolanaSdk.wallet.addresses.findAll(); // Find specific address owned by wallet const specificAddress = await dialectSolanaSdk.wallet.addresses.find({ addressId: address.id, }); ``` You can also check email subscription status using the hook: ```typescript theme={null} import { AddressType } from '@dialectlabs/sdk'; import { useNotificationChannelDappSubscription } from '@dialectlabs/react-sdk'; // Check email subscription status const { enabled: emailEnabled, isFetchingSubscriptions: emailFetching, } = useNotificationChannelDappSubscription({ addressType: AddressType.Email, dappAddress: DAPP_ADDRESS, }); if (!emailFetching) { console.log('Email subscribed:', emailEnabled); } ``` Users can view all their configured notification channels in the notification widget's settings screen. *No extra code needed—handled by the component UI.* Get channel subscription status via the apps endpoint: ```bash theme={null} curl https://alerts-api.dial.to/v2/apps \ --request GET \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' ``` This shows which channels are subscribed for each app: ```json theme={null} { "apps": [ { "id": "app-uuid", "name": "Your App", "subscribed": true, "channels": [ { "channel": "IN_APP", "subscribed": true }, { "channel": "EMAIL", "subscribed": false }, { "channel": "TELEGRAM", "subscribed": true } ] } ] } ``` Note: This endpoint shows channel subscription status, but email/Telegram addresses must be managed via components or SDK. ### Subscribe to specific channels Use the `useNotificationChannelDappSubscription` hook to manage email subscriptions: ```typescript theme={null} import { AddressType } from '@dialectlabs/sdk'; import { useNotificationChannelDappSubscription } from '@dialectlabs/react-sdk'; // Subscribe to email notifications for your dapp const { enabled: emailEnabled, isToggling: emailToggling, toggleSubscription: toggleEmailSubscription, } = useNotificationChannelDappSubscription({ addressType: AddressType.Email, dappAddress: DAPP_ADDRESS, }); // Enable email notifications await toggleEmailSubscription({ enabled: true }); ``` Users can enable/disable specific channels (email, Telegram) in the notification widget's settings screen. *No extra code needed—handled by the component UI.* Subscribe to specific channels for an app: ```bash theme={null} curl https://alerts-api.dial.to/v2/subscribe \ --request POST \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' \ --header 'Content-Type: application/json' \ --data '{ "appId": "YOUR_APP_ID", "channel": ["EMAIL", "TELEGRAM"] }' ``` Note: Email and Telegram channels require the user to have already added and verified those addresses via components or SDK. ### Unsubscribe from specific channels Use the same `useNotificationChannelDappSubscription` hook to disable email notifications: ```typescript theme={null} import { AddressType } from '@dialectlabs/sdk'; import { useNotificationChannelDappSubscription } from '@dialectlabs/react-sdk'; const { enabled: emailEnabled, isToggling: emailToggling, toggleSubscription: toggleEmailSubscription, } = useNotificationChannelDappSubscription({ addressType: AddressType.Email, dappAddress: DAPP_ADDRESS, }); // Disable email notifications await toggleEmailSubscription({ enabled: false }); // Check current subscription status console.log('Email notifications enabled:', emailEnabled); console.log('Currently toggling:', emailToggling); ``` Users can disable specific channels in the notification widget's settings screen while keeping other channels active. *No extra code needed—handled by the component UI.* Unsubscribe from specific channels while maintaining subscription to others: ```bash theme={null} curl https://alerts-api.dial.to/v2/unsubscribe \ --request POST \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' \ --header 'Content-Type: application/json' \ --data '{ "appId": "YOUR_APP_ID", "channel": ["EMAIL"] }' ``` This will unsubscribe from EMAIL notifications while keeping IN\_APP and TELEGRAM active. ### Update an address You can update an address at any time. All of your subscriptions will remain intact, but won't be sent until you re-verify. ```typescript theme={null} // Update address value const updatedAddress = await dialectSolanaSdk.wallet.addresses.update({ addressId: address.id, value: "updated.address@example.com", }); ``` Users can update their email/Telegram addresses in the notification widget's settings screen. *No extra code needed—handled by the component UI.* Not supported via public API.\ Please use the Notification Components or SDK methods above. ### Remove an address You can delete an address at any time. This will remove all subscriptions associated with that address. ```typescript theme={null} // Delete address await dialectSolanaSdk.wallet.addresses.delete({ addressId: address.id, }); ``` Users can remove email/Telegram addresses in the notification widget's settings screen. *No extra code needed—handled by the component UI.* Not supported via public API.\ Please use the Notification Components or SDK methods above. ### Push Notifications Push Notifications require a more complex setup and you can find a full walkthrough in the [API section](/alerts/integrate-inbox/api/push-notifications). ## Step 3: Topic Management Create and manage notification topics to organize and categorize your app's notifications. Topics help users subscribe to specific types of alerts they care about. ### Create a new notification type Define a new category of notifications for your app. Each notification type represents a specific kind of alert that users can subscribe to independently. ```typescript theme={null} // Create a new notification type const dapp = await dialectSolanaSdk.dapps.find(); if (!dapp) { throw new IllegalStateError( "Dapp doesn't exist, please create dapp before using it" ); } const notificationType = await dapp.notificationTypes.create({ humanReadableId: "announcements", name: "Announcements", orderingPriority: 0, trigger: "Notification description or triggering event/conditions", defaultConfig: { enabled: true, }, }); ``` App owners create notification topics through their backend or admin interface, not through the notification widget. *Topics are created server-side and automatically appear in user widgets.* Not supported via public API.\ Please use the SDK method above. ### Update an existing notification type Modify the properties of an existing notification type, such as changing its name or trigger description. This is useful when refining your notification categories. ```typescript theme={null} const allNotificationTypes = await dapp.notificationTypes.findAll(); if (allNotificationTypes.length < 1) return; const notificationType = allNotificationTypes[0]; const patched = await dapp.notificationTypes.patch(notificationType.id, { name: "New feature announcements", }); ``` App owners update notification topics through their backend or admin interface, not through the notification widget. *Topic updates are managed server-side and automatically reflected in user widgets.* Not supported via public API.\ Please use the SDK method above. ### Get all topics Retrieve all notification types (topics) for your app: ```typescript theme={null} const dapp = await dialectSolanaSdk.dapps.find(); const allNotificationTypes = await dapp.notificationTypes.findAll(); // Or find a specific topic by human-readable ID const specificTopic = await dapp.notificationTypes.find({ humanReadableId: "price-alerts" }); ``` Users can view all available topics for each app in the notification widget's settings screen. *No extra code needed—handled by the component UI.* Get all available topics for a user: ```bash theme={null} curl https://alerts-api.dial.to/v2/topics?appId=YOUR_APP_ID \ --request GET \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' ``` This returns topics organized by app with subscription status: ```json theme={null} { "byApp": { "app-uuid": { "items": [ { "id": "topic-uuid", "name": "Price Alerts", "description": "Receive alerts when prices drop", "slug": "price-alerts", "subscribed": true } ] } } } ``` ### Subscribe to topics Use the `useNotificationSubscriptions` hook to manage topic subscriptions: ```typescript theme={null} import { useNotificationSubscriptions } from '@dialectlabs/react-sdk'; const { subscriptions, update, isFetching, isUpdating, } = useNotificationSubscriptions({ dappAddress: DAPP_ADDRESS, }); // Subscribe to a specific topic const subscribeToTopic = async (topicId: string) => { await update({ notificationTypeId: topicId, config: { enabled: true, }, }); }; // Example: Subscribe to price alerts topic await subscribeToTopic('price-alerts-topic-id'); ``` Users can subscribe to topics directly in the notification widget's settings screen.\ When a user toggles a topic on, the component handles the subscription automatically. ```tsx theme={null} ``` *In the settings panel, users can toggle topics on/off. No extra code needed!* Subscribe to a specific topic: ```bash theme={null} curl https://alerts-api.dial.to/v2/topics/subscribe \ --request POST \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' \ --header 'Content-Type: application/json' \ --data '{ "topicId": "123e4567-e89b-12d3-a456-426614174000" }' ``` ### Unsubscribe from topics Use the same `useNotificationSubscriptions` hook to unsubscribe from topics: ```typescript theme={null} import { useNotificationSubscriptions } from '@dialectlabs/react-sdk'; const { subscriptions, update, isUpdating, } = useNotificationSubscriptions({ dappAddress: DAPP_ADDRESS, }); // Unsubscribe from a specific topic const unsubscribeFromTopic = async (topicId: string) => { await update({ notificationTypeId: topicId, config: { enabled: false, }, }); }; // Example: Unsubscribe from price alerts topic await unsubscribeFromTopic('price-alerts-topic-id'); ``` Users can unsubscribe from topics directly in the notification widget's settings screen by toggling them off. *No extra code needed—handled by the component UI.* Unsubscribe from a specific topic: ```bash theme={null} curl https://alerts-api.dial.to/v2/topics/unsubscribe \ --request POST \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' \ --header 'Content-Type: application/json' \ --data '{ "topicId": "123e4567-e89b-12d3-a456-426614174000" }' ``` ### Get topic subscription status Use the `useNotificationSubscriptions` hook to get topic subscription status: ```typescript theme={null} import { useNotificationSubscriptions } from '@dialectlabs/react-sdk'; const { subscriptions, isFetching, } = useNotificationSubscriptions({ dappAddress: DAPP_ADDRESS, }); // Check if a specific topic is subscribed const isTopicSubscribed = (topicId: string) => { const subscription = subscriptions.find( (sub) => sub.notificationType.id === topicId ); return subscription?.subscription?.config?.enabled || false; }; // Get all subscribed topics const subscribedTopics = subscriptions.filter( (sub) => sub.subscription?.config?.enabled ); if (!isFetching) { console.log('Price alerts subscribed:', isTopicSubscribed('price-alerts-topic-id')); console.log('All subscribed topics:', subscribedTopics); } ``` Users can view their topic subscription status in the notification widget's settings screen. *No extra code needed—handled by the component UI.* Get topic subscription status via the apps endpoint: ```bash theme={null} curl https://alerts-api.dial.to/v2/apps \ --request GET \ --header 'Authorization: Bearer YOUR_AUTH_TOKEN' \ --header 'X-Dialect-Client-Key: YOUR_CLIENT_KEY' ``` This returns apps with their topics and subscription status: ```json theme={null} { "apps": [ { "id": "app-uuid", "name": "Your App", "subscribed": true, "topics": [ { "id": "topic-uuid", "name": "Price Alerts", "slug": "price-alerts", "subscribed": true } ] } ] } ``` ## Best Practices 💡 **User Experience Tips**: * Start users with a simple subscription, then let them add channels as needed * Create meaningful notification types that users actually want to subscribe to * Good topics are specific enough to be useful but broad enough to generate regular content * Always respect user preferences and make unsubscribing easy 💡 **Technical Tips**: * Always subscribe users to your app first using `dappAddresses.create()` before adding additional notification channels * Implement proper error handling for address verification flows * Use batch operations when managing multiple users to avoid rate limits ## Next Steps Now that you understand user management concepts, you're ready to implement notification components: 1. **[NotificationsButton](/alerts/integrate-inbox/sdk/notifications-button)** - Complete done-for-you solution 2. **[Notifications](/alerts/integrate-inbox/sdk/notifications)** - Standalone component for custom integration The components handle all the user management workflows described above, providing your users with intuitive interfaces for managing their notification preferences. # Mobile Alert Stack Source: https://docs.dialect.to/alerts/mobile-alerts/index The complete mobile alerts stack for Web3 applications. Deliver alerts across any platform, in any programming language. Mobile Alert Stack - Cross-platform notifications for Web3 applications ## What is the Mobile Alert Stack? The Mobile Alert Stack is a comprehensive suite of open APIs bundled with Firebase push notifications that extends Dialect's current alerts stack with cross-platform, cross-language capabilities. It's designed as parts of our product offering that get you ready for mobile—enabling Web3 applications to deliver notifications across mobile, web, email, and messaging platforms from a single integration. Built from the ground up to support the same features as our existing Alert Stack, the Mobile Alert Stack now operates as a set of APIs that can be used cross-platform across React, Swift, Kotlin, React Native, and more to deliver the same experience directly inside your mobile app. ## Key Features ### Cross-Platform API Integration Build with every programming language you want. Our open APIs enable notifications across any platform—Swift for iOS, Kotlin for Android, React Native for cross-platform mobile, or traditional web frameworks. **What you get:** APIs that work with all programming languages Unified notification system across all platforms Get from nothing to your first alert integrated in 15 minutes All the tools you need to add mobile notifications to your application ### Firebase Push Notifications (Bundled) Native mobile push notifications delivered through Firebase, bundled directly with the Mobile Alert Stack. We handle all the routing under the hood, including what users have subscribed for and what they haven't. **Features:** * **Native Integration**: Seamless integration with device notification systems * **Automatic Routing**: Smart delivery based on user preferences and subscriptions * **Rich Content**: Support for images, actions, and custom data * **Cross-App Notifications**: Users can receive alerts from multiple apps they've subscribed to ### Multi-Channel Messaging In-App, Email and Telegram are available as separate offerings, providing users choice in how they receive different types of alerts. ## Integration Paths Choose your implementation approach: **Recommended approach:** Start with receiving integration first, then add sending capabilities to enable the full cross-app notification experience. Start with the [User Management](/alerts/integrate-inbox/user-management) guide and follow the [integration guide](/alerts/integrate-inbox/api/push-notifications) to: * Implement the [Universal Inbox](/alerts/integrate-inbox/universal-inbox) for cross-app notifications * Enable users to receive push notifications from multiple subscribed apps * Set up in-app notification feeds Review the [sending guide](/alerts/send/api/push-notifications) to: * Set up Firebase for push notifications * Integrate Dialect's API for multi-channel delivery * Configure notification routing and user preferences ## Why Mobile Push Notifications? Web3 is increasingly mobile-first, and users expect instant notifications for critical events like liquidation warnings, price alerts, governance proposals, and trading opportunities. Push notifications offer unique advantages: * **Instant Delivery**: Push notifications arrive immediately, even when apps aren't open * **Native Experience**: Seamless integration with device notification systems * **Higher Engagement**: Push notifications typically see 3-10x higher open rates than email * **Privacy-First**: No need to share email addresses or join Telegram channels * **Rich Content**: Support for images, actions, and custom data ## Cross-Platform Consistency The same notification system powers all channels, ensuring consistent messaging and user experience whether users receive alerts via push, email, or in-app notifications. Users can choose their preferred channels for different alert types: | Alert Type | 📱 Push | 📧 Email | 💬 Telegram | 📋 In-App | | -------------------------------------------- | ------- | -------- | ----------- | --------- | | **Critical alerts** (liquidations, security) | ✅ | ✅ | ✅ | ✅ | | **Trading signals** (price alerts) | ✅ | ❌ | ❌ | ✅ | | **Daily summaries** (portfolio updates) | ❌ | ✅ | ✅ | ❌ | | **Governance proposals** (voting deadlines) | ❌ | ✅ | ✅ | ✅ | | **Transaction confirmations** (swaps, sends) | ✅ | ❌ | ❌ | ✅ | This approach ensures your users never miss important information, regardless of their preferred communication method or device. ## Real-World Example: Jupiter Mobile Radar [Jupiter Exchange announced](https://x.com/JupiterExchange/status/1935683989894758759) native push notification support powered by Dialect, bringing real-time alerts directly to users' phones. Jupiter Mobile app showcasing real-time push notifications and alerts Push notifications for swaps, sends, and receives High-fidelity channel for product and company announcements Same amazing alert experience directly in the mobile app Direct delivery to users' phones # Quick Start Source: https://docs.dialect.to/alerts/quick-start Get your first notification working in under 15 minutes. Complete flow from dashboard registration to sending your first notification. ## Prerequisites **Before you start, make sure you have:** * **Solana Wallet**: Any Solana wallet (Phantom, Solflare, etc.) * **Development Environment**: Node.js 16+ installed * **Basic Knowledge**: React components and wallet connections ## Video Tutorial Watch the complete walkthrough to see exactly how it works.