Cometchat Android V5 Calls Sdk logo

Cometchat Android V5 Calls Sdk

Organization
cometchat
cometchat-android-v5-calls-sdk

Add voice & video calling to any Android app FROM SCRATCH with the headless CometChat Calls SDK v5 (`com.cometchat:calls-sdk-android:5.0.4`) — no UI Kit. Kotlin: initFromSettings→login→generateToken→joinSession into a RelativeLayout, lifecycle-bound listeners, meet-style session rooms AND 1:1 ringing (Chat SDK v5 signaling + Calls SDK media), call logs, layouts, audio modes, recording, PiP, background service. Triggers: 'add calling from scratch android', 'standalone video call kotlin', 'headless calls sdk android', 'build my own call UI android', 'meeting room join by session id android', 'one-on-one ringing call without uikit android'.

Overview

Publishercometchat
Repositorycometchat-skills
Skill namecometchat-android-v5-calls-sdk
Stars
109
Forks
2
Bundled files
6
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 6 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by cometchat on GitHub. Read the source before you install it.

Installation

Install the Cometchat Android V5 Calls Sdk AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/cometchat/cometchat-skills.git /tmp/cometchat-skills
mkdir -p .claude/skills
cp -r /tmp/cometchat-skills/skills/cometchat-android-v5-calls-sdk .claude/skills/cometchat-android-v5-calls-sdk
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Cometchat Android V5 Calls Sdk in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Cometchat Android V5 Calls Sdk on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Cometchat Android V5 Calls Sdk is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Ground truth: com.cometchat:calls-sdk-android:5.0.4 + catalog android-calls-v5.json (the CLOSED list, javap-verified on the installed AAR). Docs: /calls/android/** = v5 · Docs MCP. Fetch builder options, callback lists and filters from the docs (references/docs-map.md) — never memory. ⚠️ Where docs and AAR disagree, the AAR wins (drift list in references/pitfalls.md). APPEND to the user's app — additive wiring only (RULES.md). HEADLESS path (no UI Kit); for the prebuilt call UI use cometchat-android-v6-calls.

Companion skills (read first)

  • Standalone/headless entry — owns its own artifact (the Calls SDK), no -core sibling.
  • 1:1 RINGING only also drives the Chat SDK (com.cometchat:chat-sdk-android:5.0.5) for signaling — references/ringing.md; deeper Chat-SDK usage: cometchat-android-v5-sdk.
  • Prefer the UI Kit? Chat + a prebuilt call UI ⇒ cometchat-android-v6-core + cometchat-android-v6-calls, not this skill.

Use this skill when

"add calling from scratch / without the UI Kit", "standalone voice/video on Android", "build my own call screen", "headless calls SDK", "meeting room join by session id", "one-on-one ringing call". Precondition: the caller chose the from-scratch / standalone path (prebuilt call UI ⇒ see Companion skills).

Prerequisites & install

kotlin
// settings.gradle(.kts) → dependencyResolutionManagement.repositories
maven("https://dl.cloudsmith.io/public/cometchat/cometchat/maven/")
// app/build.gradle.kts
implementation("com.cometchat:calls-sdk-android:5.0.4")    // meet-style needs ONLY this (+ the two RN lines below)
implementation("com.cometchat:chat-sdk-android:5.0.5")     // + this for 1:1 RINGING (signaling)
implementation("com.facebook.react:react-android:0.77.2")  // the SDK's embedded RN runtime — see "SoLoader" below
implementation("com.facebook.soloader:soloader:0.12.1")    // (versions = what calls-sdk-android 5.0.4 pulls)
properties
# gradle.properties — REQUIRED for the 1:1 RINGING path (chat-sdk-android 5.0.5 still pulls android.arch.lifecycle:extensions:1.1.1 → support-compat 26.1.0)
android.useAndroidX=true
android.enableJetifier=true
org.gradle.jvmargs=-Xmx4096m   # REQUIRED: Jetifier transforms the embedded react-android AAR — the AS-default -Xmx2048m OOMs (AUDIT-225)
xml
<!-- AndroidManifest.xml, inside <application> — REQUIRED for background handling: the AAR declares NO <service> -->
<service android:name="com.cometchat.calls.services.CometChatOngoingCallService"
    android:exported="false"
    android:foregroundServiceType="camera|microphone|mediaPlayback" />

Full error-text evidence for the four build gates below (minSdk, <service>, Jetifier, heap): references/build-truth.md.

  • minSdk 26 — the manifest merge is the floor. calls-sdk-android:5.0.4 pulls react-native-svg at minSdkVersion 26; a 24/25 app fails process*MainManifest. Docs now agree (AUDIT-229) — raise minSdk first.
  • The <service> above is REQUIRED. The 5.0.4 AAR merges permissions only; without YOUR declaration CometChatOngoingCallService.launch(activity) fails silently (call dies on Home). Docs ship the <service> block (corrected upstream, verified 2026-09-09).
  • Jetifier (ringing only). Without android.enableJetifier=true, an AndroidX app + chat-sdk-android fails checkDebugDuplicateClasses. Meet-style needs none.
  • ⚠️ Gradle heap -Xmx4096m (MANDATORY — the FIRST build OOMs, meet-style too, AUDIT-225). The AS-default -Xmx2048m OOMs Jetifier transforming the embedded react-android AAR; the gradle.properties line above fixes it.
  • ⚠️ SoLoader (MANDATORY on 5.0.x; a REQUIRED skill step — the SDK doesn't self-init on the joinSession path). The call surface is an embedded React Native 0.77; without the merged-mapping init joinSession crashes the process (UnsatisfiedLinkError: … "libhermes_executor.so" not found). Add ONCE, first in Application.onCreate() (hence the two RN artifacts on YOUR compile classpath):
    kotlin
    import com.cometchat.calls.core.CometChatCalls
    import com.facebook.react.soloader.OpenSourceMergedSoMapping
    import com.facebook.soloader.SoLoader
    
    SoLoader.init(this, OpenSourceMergedSoMapping)   // Application.onCreate(), BEFORE CometChatCalls.initFromSettings — idempotent
  • Manifest: the AAR merges the call permissions (INTERNET, CAMERA, RECORD_AUDIO, FOREGROUND_SERVICE_*, POST_NOTIFICATIONS, …) — but NOT the <service> above. Java 11 compileOptions.
  • Runtime permissions are YOURS to request (API 23+): CAMERA + RECORD_AUDIO (video) / RECORD_AUDIO (voice) before joinSession. Denied ⇒ tell the user; never join silently muted.
  • Credentials: App ID · Region · Auth Key (dev only) live in the gitignored app/src/main/assets/cometchat-settings.json (same file the chat core uses; shape → references/docs-map.md § DOCS-GAP). Fetch via the CLI — npx @cometchat/skills-cli@3 auth loginprovision use --app-id <id> --json (@3 pins the CLI major that matches the v5 skills) — or paste from Dashboard → Credentials. Mint auth tokens server-side for production; never ship the Auth Key.

Init & login ordering (BAKED — invariant)

CometChatCalls.initFromSettings(context, listener) resolves (onSuccess) → CometChatCalls.login(uid, authKey, …) (dev) or login(authToken, …) (prod) resolves → then generateToken / joinSession / listeners. Nothing joins before init+login resolve; before init every call fails with ERROR_COMETCHAT_CALLS_SDK_INIT.

  • DEFAULT to initFromSettings — the ai-agent/telemetry-attributed path (integrationSource="ai-agent", parallel to CometChat.initFromSettings; RULES §5, AUDIT-175). It reads assets/cometchat-settings.json (appId, region, optional callsSDK hosts). Intentionally undocumented (@nodoc); the documented CometChatCalls.init(context, CallAppSettings.CallAppSettingBuilder(...).build(), listener) is the FALLBACK only.
  • authKey is read back out of the same asset (credentials.authKey) — never a second copy. Skip login only when getLoggedInUser()?.uid == uid — a DIFFERENT uid ⇒ logout → RE-INIT → login (references/user-switch.md); != null alone calls as the PREVIOUS user. Init ONCE, in Application.onCreate().
kotlin
import com.cometchat.calls.core.CometChatCalls
import com.cometchat.calls.exceptions.CometChatException
import com.cometchat.calls.model.CallUser
import org.json.JSONObject

// Call from Application.onCreate(); it re-enters itself after a user-switch logout.
fun initThenLogin(uid: String) {
    CometChatCalls.initFromSettings(this, object : CometChatCalls.CallbackListener<String>() {   // telemetry default; init(context, CallAppSettings, …) is the doc fallback
        override fun onSuccess(result: String) {
            val current = CometChatCalls.getLoggedInUser()
            if (current?.uid == uid) return                                    // SAME user — already logged in
            if (current != null) {                                             // DIFFERENT user — NOT `!= null` → login (that calls as the PREVIOUS user)
                CometChatCalls.logout(object : CometChatCalls.CallbackListener<String>() {
                    // logout leaves isInitialized()==false → RE-INIT before login (ringing: CometChat.logout too — references/user-switch.md)
                    override fun onSuccess(msg: String) = initThenLogin(uid)
                    override fun onError(e: CometChatException) { /* surface e.code — never continue as the old user */ }
                })
                return
            }
            // dev: login(uid, authKey, l) — authKey from the SAME asset; prod: login(authToken, l), token minted server-side.
            val authKey = JSONObject(assets.open("cometchat-settings.json").bufferedReader().readText())
                .getJSONObject("credentials").getString("authKey")
            CometChatCalls.login(uid, authKey, object : CometChatCalls.CallbackListener<CallUser>() {
                override fun onSuccess(callUser: CallUser) { /* unlock calling */ }
                override fun onError(e: CometChatException) { /* surface e.code + e.message */ }
            })
        }
        override fun onError(e: CometChatException) { /* ERROR_SETTINGS_FILE_NOT_FOUND ⇒ the asset is missing */ }
    })
}
initThenLogin(uid)

Two build modes (BAKED — the router picks one)

  • Meet-style (session room) — Calls SDK ONLY: generateToken(sessionId)joinSession(token, sessionSettings, relativeLayout, …) (or the joinSession(sessionId, …) overload); everyone on the same sessionId shares the call. No ringing.
  • 1:1 ringing — Chat SDK signals, Calls SDK carries media (§ 1:1 ringing below — FOREGROUND-only without VoIP push).

SDK method map (BAKED closed list — from the catalog; signatures → FETCH from docs)

  • Lifecycle (CometChatCalls statics): initFromSettings · init · isInitialized · login(uid, authKey, l) / login(authToken, l) · logout · getLoggedInUser · getUserAuthToken
  • Session: generateToken(sessionId, l)GenerateToken.token · joinSession(token | sessionId, SessionSettings, RelativeLayout, l)CallSession (startSession/CallSettingsBuilder = v4 path, do not use) · builder CometChatCalls.SessionSettingsBuilder() (setSessionType/setLayout/setAudioMode/…, full list in the reference).
  • Active call (CallSession.getInstance()): isSessionActive · leaveSession · lifecycle-bound listeners (addSessionStatusListener(owner, …) + siblings) · clearAllListeners.
  • Also (full enumeration → references/method-map.md): In-call actions (CUSTOM controls only) · Call logs (CallLogRequest) · Transcription & closed captions (v5.0.4+, plan-gated) · Background (CometChatOngoingCallService) · Constants/enums (SessionType/LayoutType/AudioMode/CometChatCallsConstants.*).

A curated highlight, NOT the full surface — the AUTHORITATIVE closed list is the android-calls-v5.json catalog (also transcription/streaming, CometChatCallsEventsListener, the v4-era CallSettingsBuilder). A symbol is real iff it's in the catalog; then fetch its page via references/docs-map.md. Full method map: references/method-map.md.

Listener lifecycle (BAKED)

Every CallSession listener takes a LifecycleOwner and is removed with it — register from joinSession's onSuccess with the Activity/Fragment as owner; no manual remove (clearAllListeners() for hard resets). Still yours by hand: CallSession.getInstance().leaveSession() on teardown, and for ringing CometChat.removeCallListener(ID).

Least-code recipe (meet-style, Kotlin Activity)

kotlin
import android.Manifest
import android.os.Bundle
import android.view.ViewGroup
import android.widget.RelativeLayout
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.cometchat.calls.core.CallSession
import com.cometchat.calls.core.CometChatCalls
import com.cometchat.calls.exceptions.CometChatException
import com.cometchat.calls.listeners.SessionStatusListener
import com.cometchat.calls.model.GenerateToken
import com.cometchat.calls.model.LayoutType
import com.cometchat.calls.model.SessionType

class MeetCallActivity : AppCompatActivity() {           // init + login already resolved (Application)
    private lateinit var callContainer: RelativeLayout
    private val permissions = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { granted ->
        if (granted.values.all { it }) joinRoom(intent.getStringExtra("SESSION_ID") ?: "team-room")
        else finish()                                     // tell the user — a call without mic/camera cannot start
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // The SDK renders its ENTIRE call UI into this view — a RelativeLayout with REAL size. No external controls.
        callContainer = RelativeLayout(this).apply {
            layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
        }
        setContentView(callContainer)
        permissions.launch(arrayOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO))
    }

    private fun joinRoom(sessionId: String) {
        val sessionSettings = CometChatCalls.SessionSettingsBuilder()      // fetch more options from /calls/android/session-settings
            .setSessionType(SessionType.VIDEO)                              // ⚠️ setSessionType — the docs' setType() does not exist here
            .setLayout(LayoutType.TILE)
            .setDisplayName("Me")
            .setIdleTimeoutPeriod(120)                                      // alone for 2 min ⇒ onSessionTimedOut
            .build()
        CometChatCalls.generateToken(sessionId, object : CometChatCalls.CallbackListener<GenerateToken>() {
            override fun onSuccess(token: GenerateToken) {
                CometChatCalls.joinSession(token, sessionSettings, callContainer, object : CometChatCalls.CallbackListener<CallSession>() {
                    override fun onSuccess(callSession: CallSession) {
                        // Listeners are LIFECYCLE-BOUND (owner = this Activity) — register here, after join.
                        callSession.addSessionStatusListener(this@MeetCallActivity, object : SessionStatusListener() {
                            override fun onSessionLeft() { finish() }
                            override fun onSessionTimedOut() { finish() }
                            override fun onConnectionClosed() { finish() }
                        })
                    }
                    override fun onError(e: CometChatException) { /* show e.code + e.message, then finish() */ }
                })
            }
            override fun onError(e: CometChatException) { /* ERROR_AUTH_TOKEN ⇒ not logged in; show it */ }
        })
    }

    override fun onDestroy() {
        if (CallSession.getInstance().isSessionActive) CallSession.getInstance().leaveSession()   // never leak a live session
        // If you called CometChatOngoingCallService.launch(...), ALSO CometChatOngoingCallService.abort(this) here —
        // onSessionLeft is NOT delivered on Activity destroy (BACK), so callback-only abort leaks the FGS (AUDIT-227).
        super.onDestroy()
    }
}

Compose host: the same code inside AndroidView(factory = { RelativeLayout(it) }) filling Modifier.fillMaxSize(); declare the Activity with configChanges (Rotation pitfall, references/pitfalls.md).

1:1 ringing (Chat SDK signaling + Calls SDK media)

Register the CallListener once, globally (Application.onCreate, after CometChat.initFromSettings + CometChat.login — the cometchat-android-v5-sdk recipe) so the callee rings from any screen of a FOREGROUND app. Full recipe (both roles, Kotlin) + the ringing prerequisites: references/ringing.mdinitiateCall → peer onIncomingCallReceivedacceptCall/rejectCalljoinSession(call.sessionId) on BOTH sides → hang-up = leaveSession and CometChat.endCall.

  • Foreground-only (verified live). In auto socket mode the Chat SDK drops its WebSocket the instant the app is backgrounded — a backgrounded callee never gets onIncomingCallReceived and a backgrounded caller never gets onOutgoingCallAccepted (not replayed on return). Background / killed-app ringing = the VoIP push path (/calls/android/voip-calling). Never promise it from this recipe. Full evidence + the caller-side reconcile (dismiss on timeout, onResume re-check): references/ringing.md.

Common pitfalls (BAKED)

One-liners here; full evidence + reasoning for every one — see references/pitfalls.md. minSdk-26 floor + missing <service> are in Prerequisites (evidence: references/build-truth.md).

  • Don't duplicate the built-in call controls (the #1 mistake). joinSession renders a COMPLETE call UI (mute, camera, switch, audio mode, layout, participants, raise hand, recording, invite, timer, red leave) into your RelativeLayout — no custom control row unless the ask is "replace the controls" (then hideControlPanel(true) FIRST).
  • Docs ≠ AAR (verified vs 5.0.4): setTypesetSessionType · unMuteAudiounmuteAudio · unPinParticipantunpinParticipant · pinParticipant(uid)(uid, pid) · MediaEventsListener.onScreenShareStarted/Stopped don't exist (they're on ParticipantEventListener) · raiseHand/setLayout/startRecording are no-ops. Per-page notes: references/docs-map.md.
  • Wrong containerjoinSession needs a real-sized RelativeLayout; wrap_content/zero size renders invisibly (ERROR_CALLING_VIEW_REF_NULL if null).
  • joinSession crashes libhermes_executor.so not found — SoLoader merged-mapping init missing; see Prerequisites § SoLoader.
  • Joining before init+login resolveERROR_COMETCHAT_CALLS_SDK_INIT / ERROR_AUTH_TOKEN; chain off the callbacks.
  • ERROR_COMETCHAT_CALLS_SDK_INIT after a user switch is MISLEADING (AUDIT-194) — re-init between logout and login: references/user-switch.md.
  • No runtime permissions — the call joins with a dead mic/camera; request CAMERA + RECORD_AUDIO first.
  • Listeners registered before join — register in joinSession's onSuccess with a LifecycleOwner.
  • Hang-up on one SDK only (ringing)leaveSession without CometChat.endCall leaves the peer in-call; do both.
  • Android can't START screen share (receive-only), and has no virtual-background page — don't promise either.
  • Rotation / Home kill the callconfigChanges (+ supportsPictureInPicture); keep alive with CometChatOngoingCallService.launch(activity) (needs the <service>).
  • Ongoing-call service LEAKS if you only .abort in onSessionLeft (AUDIT-227) — also CometChatOngoingCallService.abort(this) in onDestroy; abort on EVERY teardown path.
  • No-op in-call actions (5.0.4 SDK bug, AUDIT-187): raiseHand/lowerHand, mid-call setLayout, startRecording/stopRecording — no effect/callback/error; prefer the SDK's own buttons.
  • Backgrounded apps do not ring and do not hear the accept — foreground only; VoIP push for the rest (§ 1:1 ringing).
  • Mixing majors — Calls SDK is 5.x (joinSession); startSession/CallSettingsBuilder + /calls/v4/android are the OLD tree.

Verify it works

  • Tier-1 catalog: every emitted symbol is in android-calls-v5.json (node test-suite/scripts/verify-catalog.mjs).
  • Tier-2 fences: the Kotlin compiles against the installed calls-sdk-android:5.0.4 (npm run verify:fences:android-v6); your app builds at minSdk 26 (the manifest merge is the floor check).
  • Tier-3 device: ./gradlew :app:assembleDebug (at minSdk 26), then on an emulator/device: grant mic+camera → joinSession shows the SDK call surface with its own controls → a second device on the same sessionId appears as a tile → Home shows the ongoing-call notification (the <service> works) → leave ends cleanly (onSessionLeft). Ringing: two devices/users, BOTH apps FOREGROUND — accept joins both, hang-up ends both; then background the callee and confirm your outgoing UI times out cleanly (no callback comes). The pack's reviewer proves the round-trip via CallsSmokeTests.

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Cometchat Android V5 Calls Sdk AI skill do?

Add voice & video calling to any Android app FROM SCRATCH with the headless CometChat Calls SDK v5 (`com.cometchat:calls-sdk-android:5.0.4`) — no UI Kit. Kotlin: initFromSettings→login→generateToken→joinSession into a RelativeLayout, lifecycle-bound listeners, meet-style session rooms AND 1:1 ringing (Chat SDK v5 signaling + Calls SDK media), call logs, layouts, audio modes, recording, PiP, background service. Triggers: 'add calling from scratch android', 'standalone video call kotlin', 'headless calls sdk android', 'build my own call UI android', 'meeting room join by session id android',...

Why use Cometchat Android V5 Calls Sdk on TypingMind?

Because you install it once and use it with any model. Cometchat Android V5 Calls Sdk is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Cometchat Android V5 Calls Sdk in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-android-v5-calls-sdk. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Cometchat Android V5 Calls Sdk?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Cometchat Android V5 Calls Sdk?

As many as you like. As long as a model supports skills, you can use Cometchat Android V5 Calls Sdk with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Cometchat Android V5 Calls Sdk AI skill free?

Yes. It is published on GitHub by cometchat under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇