SDK Documentation

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 two things from the Admin Dashboard:
1. API Key - Found in Settings → API Keys
2. App ID - Create your app in Settings → Apps

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(
    apiKey: "your_api_key_here",
    appId: "your_app_id_here"  // Create in Settings → Apps
)

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)")
            }
        }
    }
}

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(
            apiKey = "your_api_key_here",
            appId = "com.yourcompany.yourapp"
        )
    }
}

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
apiKey String Your API key from the admin dashboard (required)
appId String Your app's unique identifier, e.g., bundle ID (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.88")
],
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+).

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")
                    }
                )
            }
        }
    }
}

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

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

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.