SDK Documentation

Cuti-E SDK

Cuti-E provides native SDKs for iOS and Android that let you collect user feedback directly in your mobile app. The SDKs handle creating conversations, sending messages, and receiving push notifications - all with minimal code.

Getting Started: You need an App ID from the Admin Dashboard.
Go to Settings → Apps to create your app and get your App ID.

Quick Start

Choose your platform to get started in under 5 minutes:

1. Add the Package

In Xcode, go to File → Add Package Dependencies and enter:

https://github.com/cuti-e/ios-sdk

2. Configure the SDK

import CutiE

// In AppDelegate or @main App
CutiE.shared.configure(appId: "app_your_app_id_here")

3. Show Feedback Form

import SwiftUI
import CutiE

struct ContentView: View {
    @State private var showFeedback = false

    var body: some View {
        Button("Send Feedback") {
            showFeedback = true
        }
        .sheet(isPresented: $showFeedback) {
            CutiEFeedbackView { conversationId in
                print("Submitted: \(conversationId)")
            }
        }
    }
}
Feedback Form

The feedback form allows users to submit bug reports, feature requests, and general feedback.

1. Add the Dependency

In your settings.gradle.kts, add JitPack:

dependencyResolutionManagement {
    repositories {
        maven { url = uri("https://jitpack.io") }
    }
}

Then add the dependency in build.gradle.kts:

dependencies {
    implementation("com.github.Stig-Johnny.cutie:cutie:1.0.0")
}

2. Configure the SDK

import com.cutie.sdk.CutiE

// In your Application class
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        CutiE.getInstance(this).configure(
            appId = "app_your_app_id_here"
        )
    }
}

3. Show Feedback Form

import com.cutie.sdk.CutiEFeedbackView

@Composable
fun MyScreen() {
    var showFeedback by remember { mutableStateOf(false) }

    Button(onClick = { showFeedback = true }) {
        Text("Send Feedback")
    }

    if (showFeedback) {
        CutiEFeedbackView(
            onDismiss = { showFeedback = false },
            onSuccess = { conversationId ->
                println("Submitted: $conversationId")
            }
        )
    }
}

Configuration Options

Option Type Description
appId String Your App ID from the admin dashboard (required)
apiURL String API endpoint (default: production server)

Testing: Use the sandbox environment for development:
apiURL: "https://cutie-worker-sandbox.invotekas.workers.dev"

iOS Installation

Swift Package Manager

Add Cuti-E to your project using Xcode:

  1. File → Add Package Dependencies...
  2. Enter: https://github.com/cuti-e/ios-sdk
  3. Choose version or branch
  4. Add to your target

Manual Installation

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/cuti-e/ios-sdk.git", from: "1.0.104")
],
targets: [
    .target(
        name: "YourTarget",
        dependencies: ["CutiE"]
    )
]

Requirements

iOS Basic Usage

Create a Conversation (Callback)

CutiE.shared.createConversation(
    category: .bug,
    message: "The app crashes when I tap save",
    title: "Crash on Save"
) { result in
    switch result {
    case .success(let conversationId):
        print("Created: \(conversationId)")
    case .failure(let error):
        print("Error: \(error.localizedDescription)")
    }
}

List Conversations (iOS 15+)

// Using async/await
let conversations = try await CutiE.shared.getConversations()
for conv in conversations {
    print("\(conv.title ?? "Untitled") - \(conv.status)")
}

Send a Message (iOS 15+)

// Using async/await
let message = try await CutiE.shared.sendMessage(
    conversationId: "conv_abc123",
    message: "Thanks for the help!"
)

iOS Feedback View

The SDK includes a ready-to-use SwiftUI feedback form:

import SwiftUI
import CutiE

struct SettingsView: View {
    @State private var showFeedback = false

    var body: some View {
        List {
            Button("Send Feedback") {
                showFeedback = true
            }
        }
        .sheet(isPresented: $showFeedback) {
            CutiEFeedbackView { conversationId in
                // Called when feedback is submitted
                print("Created conversation: \(conversationId)")
            }
        }
    }
}

iOS In-app Inbox

Let users view their feedback conversations and admin replies directly in your app (iOS 15+).

In-app Inbox

The inbox shows all conversations and allows users to continue chatting with support.

UX Best Practice: Add an inbox icon to your navigation bar so users can easily access their feedback from anywhere in your app. Don't bury it deep in Settings!

Recommended: Navigation Bar Icon with Badge

Add a persistent inbox icon with an unread message badge in your main view's navigation bar:

import SwiftUI
import CutiE

struct MainView: View {
    @State private var showInbox = false
    @State private var showFeedback = false
    @State private var unreadCount = 0

    var body: some View {
        NavigationView {
            YourContentView()
                .navigationTitle("Home")
                .toolbar {
                    ToolbarItem(placement: .navigationBarTrailing) {
                        HStack(spacing: 16) {
                            // Inbox button with badge
                            Button { showInbox = true } label: {
                                ZStack(alignment: .topTrailing) {
                                    Image(systemName: "tray.full")

                                    if unreadCount > 0 {
                                        Text(unreadCount > 99 ? "99+" : "\(unreadCount)")
                                            .font(.system(size: 10, weight: .bold))
                                            .foregroundColor(.white)
                                            .padding(.horizontal, 5)
                                            .padding(.vertical, 2)
                                            .background(Color.red)
                                            .clipShape(Capsule())
                                            .offset(x: 8, y: -8)
                                    }
                                }
                            }

                            // Submit feedback button
                            Button { showFeedback = true } label: {
                                Image(systemName: "exclamationmark.bubble")
                            }
                        }
                    }
                }
                .sheet(isPresented: $showInbox) {
                    CutiEInboxView()
                }
                .sheet(isPresented: $showFeedback) {
                    CutiEFeedbackView { _ in }
                }
                .task {
                    await loadUnreadCount()
                }
        }
    }

    private func loadUnreadCount() async {
        guard CutiE.shared.isConfigured else { return }
        do {
            unreadCount = try await CutiE.shared.getUnreadCount()
        } catch {
            print("Failed to load unread count: \(error)")
        }
    }
}

Alternative: Settings Menu

struct SettingsView: View {
    @State private var showInbox = false

    var body: some View {
        List {
            Button("My Feedback") {
                showInbox = true
            }
        }
        .sheet(isPresented: $showInbox) {
            CutiEInboxView()
        }
    }
}

UIKit Integration

// Present inbox modally from any view controller
CutiE.shared.showInbox()

// Or from a specific view controller
CutiE.shared.showInbox(from: viewController)

Async/Await API

// Get all conversations
let conversations = try await CutiE.shared.getConversations()

// Get single conversation with messages
let conversation = try await CutiE.shared.getConversation(id: "conv_abc123")

// Send a reply
let message = try await CutiE.shared.sendMessage(
    conversationId: "conv_abc123",
    message: "Thanks for the help!"
)

// Get unread message count (for badge display)
let unreadCount = try await CutiE.shared.getUnreadCount()

Android Installation

Gradle (via JitPack)

Add JitPack to your root settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
    }
}

Add the dependency to your app's build.gradle.kts:

dependencies {
    implementation("com.github.Stig-Johnny.cutie:cutie:1.0.0")
}

Requirements

Android Basic Usage

Create a Conversation

CutiE.getInstance(context).createConversation(
    category = ConversationCategory.BUG,
    message = "The app crashes when I tap save",
    title = "Crash on Save"
) { result ->
    result.onSuccess { conversation ->
        println("Created: ${conversation.id}")
    }.onFailure { error ->
        println("Error: ${error.message}")
    }
}

List Conversations

CutiE.getInstance(context).listConversations { result ->
    result.onSuccess { conversations ->
        conversations.forEach { conv ->
            println("${conv.title} - ${conv.status}")
        }
    }
}

Send a Message

CutiE.getInstance(context).sendMessage(
    message = "Thanks for the help!",
    conversationID = "conv_abc123"
) { result ->
    // Handle result
}

Android Feedback View

The SDK includes a Jetpack Compose feedback form:

import com.cutie.sdk.CutiEFeedbackView

@Composable
fun SettingsScreen() {
    var showFeedback by remember { mutableStateOf(false) }

    Button(onClick = { showFeedback = true }) {
        Text("Send Feedback")
    }

    if (showFeedback) {
        Dialog(onDismissRequest = { showFeedback = false }) {
            Surface(shape = MaterialTheme.shapes.large) {
                CutiEFeedbackView(
                    onDismiss = { showFeedback = false },
                    onSuccess = { conversationId ->
                        println("Created: $conversationId")
                    }
                )
            }
        }
    }
}

Android In-app Inbox

Let users view their feedback conversations and admin replies directly in your app.

In-app Inbox

The inbox shows all conversations and allows users to continue chatting with support.

UX Best Practice: Add an inbox icon to your navigation bar so users can easily access their feedback from anywhere in your app. Don't bury it deep in Settings!

Basic Usage

import com.cutie.sdk.views.CutiEInboxView
import com.cutie.sdk.views.CutiEConversationView

@Composable
fun SettingsScreen() {
    var showInbox by remember { mutableStateOf(false) }
    var selectedConversation by remember { mutableStateOf<Conversation?>(null) }

    Button(onClick = { showInbox = true }) {
        Text("My Feedback")
    }

    if (showInbox) {
        Dialog(
            onDismissRequest = { showInbox = false },
            properties = DialogProperties(usePlatformDefaultWidth = false)
        ) {
            Surface(
                modifier = Modifier.fillMaxSize(),
                shape = MaterialTheme.shapes.large
            ) {
                CutiEInboxView(
                    onDismiss = { showInbox = false },
                    onConversationSelected = { conversation ->
                        selectedConversation = conversation
                    }
                )
            }
        }
    }

    // Show conversation detail when selected
    selectedConversation?.let { conversation ->
        Dialog(
            onDismissRequest = { selectedConversation = null },
            properties = DialogProperties(usePlatformDefaultWidth = false)
        ) {
            Surface(
                modifier = Modifier.fillMaxSize(),
                shape = MaterialTheme.shapes.large
            ) {
                CutiEConversationView(
                    conversationId = conversation.conversationId,
                    onDismiss = { selectedConversation = null }
                )
            }
        }
    }
}

Navigation Bar with Badge

Add an inbox icon with unread count badge to your app bar:

@Composable
fun MainScreen() {
    var showInbox by remember { mutableStateOf(false) }
    var unreadCount by remember { mutableStateOf(0) }

    // Load unread count on launch
    LaunchedEffect(Unit) {
        CutiE.instance?.getUnreadCount { result ->
            result.onSuccess { unreadCount = it }
        }
    }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("My App") },
                actions = {
                    // Inbox button with badge
                    IconButton(onClick = { showInbox = true }) {
                        BadgedBox(
                            badge = {
                                if (unreadCount > 0) {
                                    Badge { Text(unreadCount.toString()) }
                                }
                            }
                        ) {
                            Icon(Icons.Default.Inbox, "Feedback Inbox")
                        }
                    }
                }
            )
        }
    ) { padding ->
        // Your content here
    }

    // Show inbox dialog
    if (showInbox) {
        // ... same as above
    }
}

Callback API

// List all conversations
CutiE.instance?.listConversations { result ->
    result.onSuccess { conversations ->
        conversations.forEach { println("${it.title} - ${it.status}") }
    }
    result.onFailure { error ->
        println("Error: ${error.message}")
    }
}

// Get single conversation with messages
CutiE.instance?.getConversation(conversationId) { result ->
    result.onSuccess { conversation ->
        conversation.messages?.forEach { println(it.message) }
    }
}

// Send a reply
CutiE.instance?.sendMessage(conversationId, "Thanks!") { result ->
    result.onSuccess { message -> println("Sent: ${message.messageId}") }
}

// Get unread count for badge
CutiE.instance?.getUnreadCount { result ->
    result.onSuccess { count -> updateBadge(count) }
}

Feedback App SDK (CutiELink)

CutiELink is a lightweight SDK for connecting your app to the standalone Cuti-E Feedback App. Instead of embedding a full feedback form in your app, users can manage all their feedback in the dedicated Feedback App.

Two Integration Options:
1. CutiE SDK (above) - Full feedback form embedded in your app
2. CutiELink SDK (this section) - Links to standalone Feedback App

How It Works

  1. User taps "Open in Feedback App" button in your app
  2. CutiELink generates a secure magic link token via API
  3. The Cuti-E Feedback App opens via deep link
  4. User is automatically linked and can manage feedback

Installation

Add CutiELink via Swift Package Manager:

// Package.swift
dependencies: [
    .package(url: "https://github.com/cuti-e/ios-link-sdk.git", from: "1.0.0")
]

Or in Xcode: File → Add Package Dependencies and paste the URL.

Info.plist Setup

Add the Cuti-E URL scheme to your Info.plist to detect if the Feedback App is installed:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>cutie</string>
</array>

Usage

1. Configure at App Launch

import CutiELink

@main
struct MyApp: App {
    init() {
        CutiELink.configure(appId: "app_your_app_id_here")
    }
    // ...
}

2. Add "Open in Feedback App" Button

import CutiELink

struct SettingsView: View {
    var body: some View {
        Button("Open in Feedback App") {
            Task {
                do {
                    try await CutiELink.openFeedbackApp()
                } catch {
                    print("Error: \(error)")
                }
            }
        }
    }
}

That's it - just two steps!

Optional: Check if Feedback App is Installed

if CutiELink.isFeedbackAppInstalled {
    // Show "Open in Feedback App" button
} else {
    // Show fallback or prompt to install
}

Sandbox Testing: For development, call CutiELink.useSandbox() after configure() to use the sandbox API.

Conversation API

Categories

Category Description
bugBug reports
featureFeature requests
questionQuestions
feedbackGeneral feedback
otherOther

Statuses

Status Description
openNew, unassigned
in_progressBeing worked on
waiting_userWaiting for user response
waiting_adminWaiting for admin response
resolvedIssue resolved
closedConversation closed

Message API

Sender Types

Type Description
userMessage from app user
adminMessage from support admin
systemAutomated system message

Push Notifications

Push Notification

Users receive push notifications when admins reply to their feedback.

Important: Push notifications require setup in three places: Xcode, Apple Developer Portal, and the Admin Dashboard. See the complete setup guide for step-by-step instructions.

iOS (APNs)

import UserNotifications
import CutiE

// 1. Request permission (in app startup)
CutiE.shared.pushNotifications.requestPermission { granted in
    print("Push notifications \(granted ? "enabled" : "denied")")
}

// 2. In AppDelegate, forward the device token to CutiE
func application(_ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    CutiE.shared.pushNotifications.didRegisterForRemoteNotifications(withDeviceToken: deviceToken)
}

func application(_ application: UIApplication,
    didFailToRegisterForRemoteNotificationsWithError error: Error) {
    CutiE.shared.pushNotifications.didFailToRegisterForRemoteNotifications(withError: error)
}

// 3. Clear badge when app becomes active (SwiftUI)
@Environment(\.scenePhase) private var scenePhase

.onChange(of: scenePhase) { newPhase in
    if newPhase == .active {
        CutiE.shared.pushNotifications.clearBadgeCount()
    }
}

Push Notification Setup Screenshots

For detailed setup instructions with screenshots, see the iOS SDK Push Notifications Guide.

Xcode Push Capability

Enable Push Notifications capability in Xcode

Apple Developer Keys

Create APNs key in Apple Developer Portal

Admin APNs Settings

Configure APNs in Cuti-E Admin Dashboard

Android (FCM)

import com.google.firebase.messaging.FirebaseMessaging
import com.cutie.sdk.CutiE

FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        CutiE.getInstance(context).registerPushToken(task.result) { result ->
            result.onSuccess {
                println("Push token registered")
            }
        }
    }
}

Need help? Email us at [email protected] or check out the full SDK code on GitHub.