---
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