- {% if page.title and page.url != '/' %}
-
- ` | `channel.cheer` |
+| `channel-bits-events-v2.` | `channel.cheer` |
+| `channel-points-channel-v1.` | `channel.channel_points_custom_reward_redemption.add` |
+| `channel-subscribe-events-v1.` | `channel.subscribe`, `channel.subscription.gift`, `channel.subscription.message` |
+| `chat_moderator_actions..` | `channel.moderate` |
+| `whispers.` | `user.whisper.message` |
+
+### Key Differences
+
+1. **Authentication** - Requires a Helix client for creating subscriptions
+2. **Context** - All operations use `context.Context`
+3. **Message format** - Events use EventSub format, not old PubSub format
+4. **Connection** - Must call `Connect()` before `Listen()`
+
+### Migrating Directly to EventSub
+
+For new code or major refactors, we recommend using EventSub directly:
+
+```go
+// EventSub WebSocket (recommended for most use cases)
+es := helix.NewEventSubClient(helixClient,
+ helix.WithEventSubMessageHandler(func(event helix.EventSubMessage) {
+ switch e := event.Event.(type) {
+ case *helix.ChannelPointsRedemptionAddEvent:
+ fmt.Printf("%s redeemed %s\n", e.UserName, e.Reward.Title)
+ }
+ }),
+)
+es.Connect(ctx)
+es.SubscribeToChannelPointsRedemption(ctx, broadcasterID)
+```
+
+See [EventSub documentation](eventsub.md) for full details.
+
+---
+
+## Migrating from go-twitch-irc
+
+If you're using [gempir/go-twitch-irc](https://github.com/gempir/go-twitch-irc), here's how to migrate:
+
+### Basic Connection
+
+**go-twitch-irc:**
+```go
+client := twitch.NewClient("username", "oauth:token")
+client.OnPrivateMessage(func(message twitch.PrivateMessage) {
+ fmt.Println(message.Message)
+})
+client.Join("channel")
+client.Connect()
+```
+
+**Kappopher:**
+```go
+irc := helix.NewIRCClient(
+ helix.WithIRCNick("username"),
+ helix.WithIRCToken("oauth:token"),
+ helix.WithIRCMessageHandler(func(msg *helix.IRCMessage) {
+ if msg.Command == "PRIVMSG" {
+ fmt.Println(msg.Text)
+ }
+ }),
+)
+irc.Connect(ctx)
+irc.Join("channel")
+```
+
+### Message Structure
+
+**go-twitch-irc:**
+```go
+message.User.DisplayName
+message.User.ID
+message.Message
+message.Channel
+message.Tags["emotes"]
+```
+
+**Kappopher:**
+```go
+msg.Tags["display-name"]
+msg.Tags["user-id"]
+msg.Text
+msg.Channel
+msg.Tags["emotes"]
+```
+
+### Sending Messages
+
+**go-twitch-irc:**
+```go
+client.Say("channel", "Hello!")
+client.Reply("channel", "parent-msg-id", "Reply text")
+```
+
+**Kappopher:**
+```go
+irc.Say("channel", "Hello!")
+irc.Reply("channel", "parent-msg-id", "Reply text")
+```
+
+### Event Handlers
+
+| go-twitch-irc | Kappopher |
+|---------------|-----------|
+| `OnPrivateMessage` | Check `msg.Command == "PRIVMSG"` |
+| `OnUserNoticeMessage` | Check `msg.Command == "USERNOTICE"` |
+| `OnClearChatMessage` | Check `msg.Command == "CLEARCHAT"` |
+| `OnClearMessage` | Check `msg.Command == "CLEARMSG"` |
+| `OnRoomStateMessage` | Check `msg.Command == "ROOMSTATE"` |
+| `OnConnect` | `WithIRCConnectHandler` |
+| `OnReconnect` | `WithIRCReconnectHandler` |
+
+---
+
+## Migrating from nicklaw5/helix
+
+If you're using [nicklaw5/helix](https://github.com/nicklaw5/helix), here's how to migrate:
+
+### Client Setup
+
+**nicklaw5/helix:**
+```go
+client, _ := helix.NewClient(&helix.Options{
+ ClientID: "client-id",
+ ClientSecret: "client-secret",
+ AppAccessToken: "token",
+})
+```
+
+**Kappopher:**
+```go
+authClient := helix.NewAuthClient(helix.AuthConfig{
+ ClientID: "client-id",
+ ClientSecret: "client-secret",
+})
+token, _ := authClient.GetAppAccessToken(ctx)
+authClient.SetToken(token)
+
+client := helix.NewClient("client-id", authClient)
+```
+
+### API Calls
+
+**nicklaw5/helix:**
+```go
+resp, err := client.GetUsers(&helix.UsersParams{
+ Logins: []string{"shroud"},
+})
+user := resp.Data.Users[0]
+```
+
+**Kappopher:**
+```go
+resp, err := client.GetUsers(ctx, &helix.GetUsersParams{
+ Logins: []string{"shroud"},
+})
+user := resp.Data[0]
+```
+
+### Key Differences
+
+1. **Context required** - All API methods require `context.Context`
+2. **Separate auth client** - Authentication is handled by `AuthClient`
+3. **Response structure** - `resp.Data` is the slice directly, not wrapped
+4. **Generics** - Kappopher uses Go generics for type safety
+
+### Error Handling
+
+**nicklaw5/helix:**
+```go
+if resp.ErrorMessage != "" {
+ // Handle error
+}
+```
+
+**Kappopher:**
+```go
+if err != nil {
+ if apiErr, ok := err.(*helix.APIError); ok {
+ fmt.Printf("Status %d: %s\n", apiErr.StatusCode, apiErr.Message)
+ }
+}
+```
+
+---
+
+## Migrating from Direct API Calls
+
+If you're making direct HTTP calls to the Twitch API:
+
+### Before (raw HTTP)
+
+```go
+req, _ := http.NewRequest("GET", "https://api.twitch.tv/helix/users?login=shroud", nil)
+req.Header.Set("Authorization", "Bearer "+token)
+req.Header.Set("Client-Id", clientID)
+resp, _ := http.DefaultClient.Do(req)
+// Parse JSON manually...
+```
+
+### After (Kappopher)
+
+```go
+resp, err := client.GetUsers(ctx, &helix.GetUsersParams{
+ Logins: []string{"shroud"},
+})
+if err != nil {
+ return err
+}
+user := resp.Data[0]
+```
+
+### Benefits
+
+- **Type safety** - Responses are properly typed structs
+- **Auto-retry** - Handles rate limits and transient failures
+- **Token refresh** - Automatic token management
+- **Pagination** - Built-in cursor handling
+- **Caching** - Optional response caching
+- **Middleware** - Extensible request/response processing
+
+---
+
+## Feature Comparison
+
+| Feature | Kappopher | nicklaw5/helix | go-twitch-irc |
+|---------|-----------|----------------|---------------|
+| Helix API | 147+ endpoints | ~100 endpoints | - |
+| IRC Chat | Yes | No | Yes |
+| EventSub WebSocket | Yes | No | No |
+| EventSub Webhooks | Yes | Partial | No |
+| PubSub Compat | Yes | No | No |
+| Auto Token Refresh | Yes | Manual | - |
+| Caching | Yes | No | No |
+| Batch Operations | Yes | No | No |
+| Middleware | Yes | No | No |
+| Go Generics | Yes | No | No |
+
+---
+
+## Need Help?
+
+- [Full Documentation](/)
+- [API Reference](api-reference.md)
+- [Examples](cookbook.md)
+- [GitHub Issues](https://github.com/Its-donkey/kappopher/issues)
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
new file mode 100644
index 0000000..704ff36
--- /dev/null
+++ b/docs/troubleshooting.md
@@ -0,0 +1,328 @@
+---
+layout: default
+title: Troubleshooting
+description: Solutions to common issues when using Kappopher.
+---
+
+## Authentication Issues
+
+### "Invalid OAuth token" or 401 Unauthorized
+
+**Symptoms:** API calls return 401 errors or "Invalid OAuth token" messages.
+
+**Solutions:**
+
+1. **Validate your token**
+ ```go
+ validation, err := authClient.ValidateToken(ctx, token.AccessToken)
+ if err != nil {
+ // Token is invalid - refresh or re-authenticate
+ }
+ ```
+
+2. **Check token expiry** - Access tokens expire after ~4 hours. Use refresh tokens:
+ ```go
+ newToken, err := authClient.RefreshToken(ctx, token.RefreshToken)
+ ```
+
+3. **Verify token type** - Some endpoints need user tokens, others need app tokens:
+ - User token: `GetUsers` with `IDs` of other users
+ - App token: Most read-only public data endpoints
+
+4. **Enable auto-refresh**
+ ```go
+ cancel := authClient.AutoRefresh(ctx)
+ defer cancel()
+ ```
+
+### "Missing required scope"
+
+**Symptoms:** API returns 403 with scope-related error message.
+
+**Solutions:**
+
+1. Check the [Twitch API docs](https://dev.twitch.tv/docs/api/reference) for required scopes
+2. Re-authenticate with the needed scopes:
+ ```go
+ authClient := helix.NewAuthClient(helix.AuthConfig{
+ Scopes: []string{"channel:read:subscriptions", "channel:manage:broadcast"},
+ // ...
+ })
+ ```
+
+3. Validate your token to see current scopes:
+ ```go
+ validation, _ := authClient.ValidateToken(ctx, token)
+ fmt.Println("Scopes:", validation.Scopes)
+ ```
+
+### "Client ID and token mismatch"
+
+**Symptoms:** 401 error mentioning client ID mismatch.
+
+**Solution:** The token was created with a different Client ID. Either:
+- Use the correct Client ID that matches the token
+- Generate a new token with your Client ID
+
+---
+
+## EventSub Issues
+
+### Subscriptions stuck in "webhook_callback_verification_pending"
+
+**Symptoms:** Webhook subscriptions never become `enabled`.
+
+**Solutions:**
+
+1. **Verify your endpoint is reachable** - Twitch sends a challenge request that must be answered
+2. **Check your callback URL** - Must be HTTPS with valid certificate
+3. **Return the challenge correctly**:
+ ```go
+ handler := helix.NewEventSubWebhookHandler(secret,
+ helix.WithEventSubChallengeHandler(func(challenge string) {
+ // Handler returns challenge automatically
+ }),
+ )
+ ```
+4. **Check firewall/proxy settings** - Ensure Twitch can reach your server
+
+### Not receiving WebSocket events
+
+**Symptoms:** WebSocket connected but no events arrive.
+
+**Solutions:**
+
+1. **Verify subscription is active**:
+ ```go
+ subs, _ := client.GetEventSubSubscriptions(ctx, nil)
+ for _, sub := range subs.Data {
+ fmt.Printf("%s: %s\n", sub.Type, sub.Status)
+ }
+ ```
+
+2. **Check you're subscribed to the right events** - Use `SubscribeTo*` methods after connecting
+
+3. **Trigger test events** - Use the Twitch CLI to send test events:
+ ```bash
+ twitch event trigger channel.follow -F wss://localhost:8080/eventsub
+ ```
+
+4. **Verify your session ID** - Subscriptions must use the session ID from the welcome message
+
+### "subscription limit reached"
+
+**Symptoms:** Cannot create more EventSub subscriptions.
+
+**Solutions:**
+
+1. **WebSocket limit**: 300 subscriptions per connection. Use multiple connections or webhooks.
+2. **Total limit**: 10,000 subscriptions total. Delete unused subscriptions:
+ ```go
+ client.DeleteEventSubSubscription(ctx, subscriptionID)
+ ```
+3. **List and clean up**:
+ ```go
+ subs, _ := client.GetEventSubSubscriptions(ctx, nil)
+ for _, sub := range subs.Data {
+ if sub.Status != "enabled" {
+ client.DeleteEventSubSubscription(ctx, sub.ID)
+ }
+ }
+ ```
+
+---
+
+## IRC/Chat Issues
+
+### "Login authentication failed"
+
+**Symptoms:** IRC connection fails with NOTICE about authentication.
+
+**Solutions:**
+
+1. **Check token format** - Must include `oauth:` prefix:
+ ```go
+ helix.WithIRCToken("oauth:your-token-here")
+ ```
+
+2. **Verify token scopes** - Need `chat:read` to read, `chat:edit` to send
+
+3. **Check username matches token** - The nick must match the token owner:
+ ```go
+ helix.WithIRCNick("your_username")
+ ```
+
+### Messages not sending
+
+**Symptoms:** `Say()` returns no error but messages don't appear.
+
+**Solutions:**
+
+1. **Join the channel first**:
+ ```go
+ irc.Join("channelname")
+ time.Sleep(time.Second) // Wait for join confirmation
+ irc.Say("channelname", "Hello!")
+ ```
+
+2. **Check rate limits** - 20 messages per 30 seconds for regular users
+
+3. **Verify you're not banned/timed out** in that channel
+
+4. **Check for shadowban** - Your messages might be hidden from others
+
+### Not receiving messages
+
+**Symptoms:** Connected and joined but no messages received.
+
+**Solutions:**
+
+1. **Verify message handler is set**:
+ ```go
+ helix.WithIRCMessageHandler(func(msg *helix.IRCMessage) {
+ fmt.Printf("Received: %+v\n", msg)
+ })
+ ```
+
+2. **Request capabilities** - Kappopher requests these automatically, but verify:
+ - `twitch.tv/membership` - JOIN/PART messages
+ - `twitch.tv/tags` - User badges, emotes
+ - `twitch.tv/commands` - USERNOTICE, etc.
+
+3. **Check the channel has activity** - Try a popular channel to verify connection
+
+---
+
+## API Issues
+
+### Empty response data
+
+**Symptoms:** API returns successfully but `Data` is empty.
+
+**Solutions:**
+
+1. **Resource doesn't exist** - User deleted, stream offline, etc.
+
+2. **Check parameters**:
+ ```go
+ // Wrong: searching by ID when you have login
+ client.GetUsers(ctx, &helix.GetUsersParams{IDs: []string{"shroud"}})
+
+ // Correct: use the right parameter
+ client.GetUsers(ctx, &helix.GetUsersParams{Logins: []string{"shroud"}})
+ ```
+
+3. **Paginate if needed** - First page might be empty, check cursor:
+ ```go
+ if resp.Pagination != nil && resp.Pagination.Cursor != "" {
+ // More data available
+ }
+ ```
+
+### "too many requests" / 429 errors
+
+**Symptoms:** API returns 429 Too Many Requests.
+
+**Solutions:**
+
+1. **Check rate limit info**:
+ ```go
+ remaining, reset := client.GetRateLimitInfo()
+ if remaining == 0 {
+ time.Sleep(time.Until(reset))
+ }
+ ```
+
+2. **Use caching** to reduce API calls:
+ ```go
+ client := helix.NewClient(clientID, authClient,
+ helix.WithCache(helix.CacheConfig{
+ DefaultTTL: 5 * time.Minute,
+ }),
+ )
+ ```
+
+3. **Batch requests** where possible:
+ ```go
+ // Instead of 100 individual calls
+ client.GetUsers(ctx, &helix.GetUsersParams{
+ IDs: userIDs[:100], // Up to 100 at once
+ })
+ ```
+
+### Request timeout
+
+**Symptoms:** Context deadline exceeded.
+
+**Solutions:**
+
+1. **Increase timeout**:
+ ```go
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer cancel()
+ ```
+
+2. **Check network connectivity** to Twitch servers
+
+3. **Use a custom HTTP client** with longer timeouts:
+ ```go
+ httpClient := &http.Client{Timeout: 60 * time.Second}
+ client := helix.NewClient(clientID, authClient,
+ helix.WithHTTPClient(httpClient),
+ )
+ ```
+
+---
+
+## Connection Issues
+
+### WebSocket keeps disconnecting
+
+**Symptoms:** Frequent disconnections from EventSub or IRC.
+
+**Solutions:**
+
+1. **Enable auto-reconnect**:
+ ```go
+ helix.WithEventSubAutoReconnect(true)
+ // or for IRC
+ helix.WithIRCAutoReconnect(true)
+ ```
+
+2. **Handle reconnection**:
+ ```go
+ helix.WithEventSubReconnectHandler(func(oldID, newID string) {
+ log.Println("Reconnected")
+ })
+ ```
+
+3. **Check for network issues** - Unstable connections, proxies, firewalls
+
+### "connection refused"
+
+**Symptoms:** Cannot connect to Twitch servers.
+
+**Solutions:**
+
+1. **Check firewall** - Allow outbound connections to:
+ - `api.twitch.tv` (HTTPS/443)
+ - `id.twitch.tv` (HTTPS/443)
+ - `eventsub.wss.twitch.tv` (WSS/443)
+ - `irc-ws.chat.twitch.tv` (WSS/443)
+
+2. **Check proxy settings** - Configure HTTP client if needed
+
+3. **Verify DNS resolution** - Try `ping api.twitch.tv`
+
+---
+
+## Still Having Issues?
+
+1. **Enable debug logging** to see raw requests/responses
+2. **Check [Twitch Status](https://status.twitch.tv/)** for outages
+3. **Search [existing issues](https://github.com/Its-donkey/kappopher/issues)**
+4. **Open a new issue** with:
+ - Go version
+ - Kappopher version
+ - Minimal reproducible example
+ - Error messages/logs
{{ page.title }}
- {% if page.description %} -{{ page.description }}
+
+
+
diff --git a/docs/api-reference.md b/docs/api-reference.md
index 71b2fd4..f51d82f 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -60,3 +60,8 @@ description: Complete documentation for all Twitch Helix API endpoints.
- [Guest Star API](guest-star.md) - Guest Star (beta)
- [Content Classification](ccl.md) - Content labels
- [Advanced Features](advanced.md) - Batch, caching, middleware
+
+## Help
+- [FAQ](faq.md) - Frequently asked questions
+- [Troubleshooting](troubleshooting.md) - Common issues and solutions
+- [Migration Guide](migration.md) - Migrating from other libraries
diff --git a/docs/assets/css/main.css b/docs/assets/css/main.css
index 3bff890..37042bd 100644
--- a/docs/assets/css/main.css
+++ b/docs/assets/css/main.css
@@ -188,9 +188,85 @@ body {
padding: var(--space-2xl) var(--space-lg);
}
+.content-wrapper {
+ max-width: 1400px;
+ margin: 0 auto;
+ display: flex;
+ gap: var(--space-2xl);
+}
+
.content-container {
+ flex: 1;
max-width: 900px;
- margin: 0 auto;
+ min-width: 0;
+}
+
+/* ========================================
+ Sidebar
+ ======================================== */
+
+.sidebar {
+ width: 240px;
+ flex-shrink: 0;
+ position: sticky;
+ top: 88px;
+ height: fit-content;
+ max-height: calc(100vh - 112px);
+ overflow-y: auto;
+}
+
+.sidebar-nav {
+ padding-right: var(--space-md);
+}
+
+.sidebar-section {
+ margin-bottom: var(--space-lg);
+}
+
+.sidebar-section h3 {
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ color: var(--light-text-muted);
+ margin-bottom: var(--space-sm);
+ padding-left: var(--space-sm);
+}
+
+.sidebar-section ul,
+.sidebar .sidebar-section ul,
+.sidebar-nav .sidebar-section ul {
+ list-style: none !important;
+ padding: 0 !important;
+ margin: 0 !important;
+}
+
+.sidebar-section li,
+.sidebar .sidebar-section li,
+.sidebar-nav .sidebar-section li {
+ margin: 0 !important;
+ list-style: none !important;
+}
+
+.sidebar-section a {
+ display: block;
+ padding: var(--space-xs) var(--space-sm);
+ color: var(--light-text);
+ text-decoration: none;
+ font-size: 0.875rem;
+ border-radius: var(--radius-sm);
+ transition: all var(--transition-fast);
+}
+
+.sidebar-section a:hover {
+ background: var(--purple-subtle);
+ color: var(--purple-dark);
+}
+
+.sidebar-section a.active {
+ background: var(--purple-primary);
+ color: white;
+ font-weight: 500;
}
.page-header {
@@ -463,6 +539,16 @@ body {
Responsive Design
======================================== */
+@media (max-width: 1024px) {
+ .sidebar {
+ display: none;
+ }
+
+ .content-wrapper {
+ display: block;
+ }
+}
+
@media (max-width: 768px) {
.nav-links {
position: fixed;
diff --git a/docs/faq.md b/docs/faq.md
new file mode 100644
index 0000000..150258e
--- /dev/null
+++ b/docs/faq.md
@@ -0,0 +1,201 @@
+---
+layout: default
+title: FAQ
+description: Frequently asked questions about Kappopher.
+---
+
+## General
+
+### What is Kappopher?
+
+Kappopher is a comprehensive Go wrapper for the Twitch Helix API. It provides type-safe access to 147+ API endpoints, multiple OAuth flows, real-time event handling via EventSub and IRC, and advanced features like caching, batch operations, and middleware.
+
+### Why "Kappopher"?
+
+A playful combination of "Kappa" (Twitch's iconic emote) and "Gopher" (Go's mascot).
+
+### Is this an official Twitch library?
+
+No, Kappopher is a community-maintained library. It is not affiliated with or endorsed by Twitch.
+
+### Which Go version is required?
+
+Kappopher requires Go 1.21 or later. We use generics and other modern Go features.
+
+---
+
+## Authentication
+
+### Which OAuth flow should I use?
+
+| Use Case | Recommended Flow |
+|----------|------------------|
+| Server-side app | Authorization Code |
+| CLI tool | Device Code |
+| Server-to-server (no user) | Client Credentials |
+| Client-side/SPA | Implicit (least secure) |
+
+### How do I get a Client ID and Secret?
+
+1. Go to the [Twitch Developer Console](https://dev.twitch.tv/console)
+2. Register a new application
+3. Copy your Client ID
+4. Generate a Client Secret
+
+### My token keeps expiring. What should I do?
+
+Use the `AutoRefresh` feature to automatically refresh tokens before they expire:
+
+```go
+cancel := authClient.AutoRefresh(ctx)
+defer cancel()
+```
+
+Or manually refresh using `RefreshToken()` when you get a 401 error.
+
+### What scopes do I need?
+
+It depends on the endpoints you're using. Check the [Twitch API Reference](https://dev.twitch.tv/docs/api/reference) for required scopes per endpoint. Common scope groups are available via `helix.CommonScopes`.
+
+---
+
+## EventSub
+
+### WebSocket or Webhooks?
+
+| Feature | WebSocket | Webhooks |
+|---------|-----------|----------|
+| Public endpoint required | No | Yes |
+| Best for | Bots, local dev | Production servers |
+| Max subscriptions | 300 per connection | 10,000 total |
+| Delivery | Real-time push | HTTP POST |
+
+### Why am I not receiving events?
+
+1. **Check subscription status** - Use `GetEventSubSubscriptions()` to verify your subscriptions are `enabled`
+2. **Verify scopes** - Some events require specific OAuth scopes
+3. **Check user authorization** - User token events require the user to have authorized your app
+4. **WebSocket connected?** - Ensure your WebSocket client is connected and listening
+
+### How do I handle EventSub reconnection?
+
+The WebSocket client handles reconnection automatically. Use the reconnect handler to be notified:
+
+```go
+helix.WithEventSubReconnectHandler(func(oldSessionID, newSessionID string) {
+ log.Printf("Reconnected: %s -> %s", oldSessionID, newSessionID)
+})
+```
+
+---
+
+## IRC / Chat
+
+### Should I use IRC or EventSub for chat?
+
+| Feature | IRC | EventSub |
+|---------|-----|----------|
+| Latency | ~50ms | ~200ms |
+| Send messages | Direct | Via API |
+| Non-chat events | No | Yes |
+| Best for | Chat bots | Dashboards |
+
+Use IRC if you're building a chat bot that needs to send messages quickly. Use EventSub if you need other events alongside chat.
+
+### Why can't I send messages?
+
+1. **Verify your token** - Needs `chat:edit` scope
+2. **Join the channel first** - Call `irc.Join(channel)` before sending
+3. **Check rate limits** - Twitch limits messages to 20 per 30 seconds (100 for verified bots)
+4. **Bot account verified?** - For higher limits, verify your bot at [Twitch Developer Console](https://dev.twitch.tv/console)
+
+### How do I parse emotes from chat messages?
+
+Emote positions are in the `emotes` tag of IRC messages:
+
+```go
+emotes := msg.Tags["emotes"]
+// Format: "emote_id:start-end,start-end/emote_id:start-end"
+```
+
+---
+
+## API Usage
+
+### How do I handle pagination?
+
+Use the cursor from the response:
+
+```go
+cursor := ""
+for {
+ resp, _ := client.GetStreams(ctx, &helix.GetStreamsParams{
+ First: 100,
+ After: cursor,
+ })
+
+ // Process resp.Data...
+
+ if resp.Pagination == nil || resp.Pagination.Cursor == "" {
+ break
+ }
+ cursor = resp.Pagination.Cursor
+}
+```
+
+### How do I handle rate limits?
+
+Kappopher tracks rate limits automatically. Check before making requests:
+
+```go
+remaining, reset := client.GetRateLimitInfo()
+if remaining < 10 {
+ time.Sleep(time.Until(reset))
+}
+```
+
+### Can I make requests concurrently?
+
+Yes! The client is thread-safe. For batch operations, use the batch helper:
+
+```go
+batcher := helix.NewBatcher(client, helix.BatchConfig{
+ MaxConcurrent: 5,
+})
+results := batcher.GetUsers(ctx, userIDs)
+```
+
+---
+
+## Troubleshooting
+
+### I'm getting "invalid token" errors
+
+1. Validate your token: `authClient.ValidateToken(ctx, token)`
+2. Check if it's expired
+3. Ensure you're using the correct token type (app vs user)
+4. Verify the token has required scopes
+
+### API calls return empty data
+
+1. Check if the resource exists (user might have changed name, stream might be offline)
+2. Verify your parameters are correct
+3. Check the response's `Pagination` field - you might need to paginate
+
+### WebSocket disconnects frequently
+
+1. Handle PING/PONG keepalives (Kappopher does this automatically)
+2. Check your network stability
+3. Use the reconnect handler to recover gracefully
+
+---
+
+## Contributing
+
+### How can I contribute?
+
+See [CONTRIBUTING.md](https://github.com/Its-donkey/kappopher/blob/main/CONTRIBUTING.md) for guidelines. We welcome bug reports, feature requests, and pull requests!
+
+### Where do I report bugs?
+
+Open an issue on [GitHub](https://github.com/Its-donkey/kappopher/issues).
diff --git a/docs/index.md b/docs/index.md
index efde3a7..4c4ad82 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -20,6 +20,12 @@ Complete documentation for all Twitch Helix API endpoints.
### [Cookbook](cookbook.md)
Practical code examples and recipes for common use cases.
+## Help
+
+- [FAQ](faq.md) - Frequently asked questions
+- [Troubleshooting](troubleshooting.md) - Common issues and solutions
+- [Migration Guide](migration.md) - Migrating from other libraries or PubSub
+
## Features
- **Full Helix API Coverage** - All endpoints implemented with typed responses
diff --git a/docs/migration.md b/docs/migration.md
new file mode 100644
index 0000000..4fae246
--- /dev/null
+++ b/docs/migration.md
@@ -0,0 +1,279 @@
+---
+layout: default
+title: Migration Guide
+description: How to migrate to Kappopher from other libraries or Twitch PubSub.
+---
+
+## Migrating from Twitch PubSub
+
+> **Note:** Twitch PubSub was fully decommissioned on April 14, 2025. Kappopher provides a compatibility layer that uses EventSub under the hood.
+
+### Using the PubSub Compatibility Layer
+
+If you want to minimize code changes, use our PubSub compatibility layer:
+
+```go
+// Old PubSub code (no longer works)
+pubsub.Listen("channel-points-channel-v1.12345", token)
+
+// New Kappopher code (uses EventSub internally)
+pubsub := helix.NewPubSubClient(helixClient,
+ helix.WithPubSubMessageHandler(func(topic string, message json.RawMessage) {
+ // Handle messages
+ }),
+)
+pubsub.Connect(ctx)
+pubsub.Listen(ctx, "channel-points-channel-v1.12345")
+```
+
+### Supported Topic Mappings
+
+| PubSub Topic | EventSub Type(s) |
+|--------------|------------------|
+| `channel-bits-events-v1.
+ {% if page.title and page.url != '/' %}
+
+
{% endif %}
-
- {% endif %}
-
- {{ content }}
-
+
+ {{ content }}
+
+
{{ page.title }}
+ {% if page.description %} +{{ page.description }}
+ {% endif %} +