--- title: Content Providers - Beeper Developer Docs description: Query chats, messages, and contacts from Beeper using Android Content Provider APIs lastUpdated: 2026-02-23T00:29:17.000Z --- Build Android apps and connected devices that integrate with Beeper using standard Android [content provider](https://developer.android.com/guide/topics/providers/content-providers) APIs. Content providers encapsulate data and provide mechanisms for defining data security, serving as the standard interface between processes. Experimental Content providers are experimental and APIs are subject to change. ### What you can build - **Universal Search** - Search across all chats and messages - **Widgets & Dashboards** - Display chat summaries and unread counts - **Automation** - Send messages and react to changes - **Wearables** - Integrate with watches and IoT devices ### Key features - **Authority**: `com.beeper.api` - **Permissions**: Runtime (request at first use) - **Data Access**: Chats, messages, contacts - **Operations**: Query, insert, observe changes - **Protocol Support**: WhatsApp, Telegram, Signal, and more ## Quick start 1. **Add permissions to your manifest** AndroidManifest.xml ``` ``` These are custom Beeper permissions. Declare them in the manifest and request them at runtime with a permission prompt. ``` // Request at runtime (e.g., in an Activity or Fragment) val requestPermissions = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { results -> val hasRead = results["com.beeper.android.permission.READ_PERMISSION"] == true val hasSend = results["com.beeper.android.permission.SEND_PERMISSION"] == true // Handle granted/denied states } requestPermissions.launch(arrayOf( "com.beeper.android.permission.READ_PERMISSION", "com.beeper.android.permission.SEND_PERMISSION" )) ``` 2. **Query recent chats** ``` import androidx.core.net.toUri val uri = "content://com.beeper.api/chats?limit=50".toUri() contentResolver.query(uri, null, null, null, null)?.use { cursor -> val titleIdx = cursor.getColumnIndexOrThrow("title") val previewIdx = cursor.getColumnIndexOrThrow("messagePreview") val unreadIdx = cursor.getColumnIndexOrThrow("unreadCount") while (cursor.moveToNext()) { val title = cursor.getString(titleIdx) val preview = cursor.getString(previewIdx) val unread = cursor.getInt(unreadIdx) // Display chat info in your UI } } ``` 3. **Send a message** ``` import android.net.Uri import androidx.core.net.toUri val message = "Hello from my app!" val roomId = "!room:server.com" val result = contentResolver.insert( ("content://com.beeper.api/messages?" + "roomId=$roomId&text=${Uri.encode(message)}").toUri(), null ) result?.let { val messageId = it.getQueryParameter("messageId") // Message sent successfully } ``` 4. **Observe changes** ``` import android.os.Handler import android.os.Looper import android.database.ContentObserver import androidx.core.net.toUri contentResolver.registerContentObserver( "content://com.beeper.api/chats".toUri(), true, object : ContentObserver(Handler(Looper.getMainLooper())) { override fun onChange(selfChange: Boolean) { // Re-query and refresh your UI } } ) ``` Only `content://com.beeper.api/chats` currently emits change notifications. Observers for `messages` or `contacts` will not fire. ## Common use cases - [Unread Counter](#tab-panel-34) - [Search Messages](#tab-panel-35) - [Protocol Filter](#tab-panel-36) ``` // Get total unread count across all chats val uri = "content://com.beeper.api/chats/count?isUnread=1".toUri() val cursor = contentResolver.query(uri, null, null, null, null) val unreadTotal = cursor?.use { if (it.moveToFirst()) { it.getInt(it.getColumnIndexOrThrow("count")) } else 0 } ?: 0 ``` ``` // Search with surrounding context val searchTerm = "meeting" val uri = ("content://com.beeper.api/messages?" + "query=${Uri.encode(searchTerm)}" + "&contextBefore=2&contextAfter=2").toUri() contentResolver.query(uri, null, null, null, null)?.use { cursor -> val textIdx = cursor.getColumnIndexOrThrow("text_content") val matchIdx = cursor.getColumnIndexOrThrow("is_search_match") while (cursor.moveToNext()) { val text = cursor.getString(textIdx) val isMatch = cursor.getInt(matchIdx) == 1 // Highlight matches in UI } } ``` ``` // Get WhatsApp chats only val uri = "content://com.beeper.api/chats?protocol=whatsapp".toUri() contentResolver.query(uri, null, null, null, null)?.use { cursor -> // Process WhatsApp-specific chats } ``` ## Performance tips Best practices 1. **Always Set Limits** - Default limit is 100, but always specify `limit` to avoid large queries 2. **Use Filters** - Filter by `roomIds`, `protocol`, or other parameters to reduce data 3. **Batch Operations** - Combine multiple filters in one query instead of multiple queries 4. **Lifecycle Awareness** - Register/unregister observers with your component lifecycle ## Supported protocols | Protocol | Identifier | Features | | --------------- | ------------------ | -------- | | WhatsApp | `whatsapp` | Full | | Telegram | `telegram` | Full | | Signal | `signal` | Full | | Matrix/Beeper | `beeper`, `matrix` | Full | | Discord | `discord` | Full | | Slack | `slack` | Full | | Google Messages | `gmessages` | Full | | Others | Various | Query | ## Learn more - **[API Reference →](/android/content-providers/api-reference/index.md)** - Complete API documentation for all endpoints - **[Integration Guide →](/android/content-providers/integration-guide/index.md)** - Permissions, notifications, and troubleshooting - **[Android Docs →](https://developer.android.com/guide/topics/providers/content-provider-basics)** - Official content provider documentation