Runtime module system
Understand the Convai runtime module system, including when to use custom modules, the module lifecycle states, and which SDK extension points are safe to use.
The Convai Unity SDK is built around a module system that gives optional features — lip sync, emotion, vision, narrative design — a defined place in the runtime lifecycle. You can add your own modules using the same system: they receive the same startup sequence, access the same services, and can share interfaces with other modules.
Do you need a custom module?
A custom module is the right tool when you need behavior that:
Participates in the SDK runtime lifecycle — starts and stops with the SDK, not independently
Shares services with other SDK modules — e.g., exposes an
IAudioAnalysisServicethat the emotion module or your own code consumesReacts to SDK domain events — character speech state, emotion changes, action triggers
Integrates hardware or platform systems — haptic devices, biometric sensors, scoring engines
You do not need a custom module for:
Reacting to SDK events in a MonoBehaviour — subscribe directly via
context.Eventsfrom anIInjectablecomponent, or use the public events onConvaiCharacterSimple custom behavior on a character — add a MonoBehaviour to the character's GameObject
Calling the Convai REST API from your own scripts — use
ConvaiManager.ActiveManagerdirectly
If in doubt, start with a MonoBehaviour and only escalate to IConvaiModule when you need lifecycle integration.
What a module is
A module is a class that implements IConvaiModule. It:
Has a stable
ModuleIdstring (unique, lowercase, hyphen-separated by convention — e.g.,"my-company.haptic-feedback").Declares dependencies on other modules via
RequiredModulesand on runtime services viaRequiredServices.Participates in the runtime lifecycle: Register → Start → Pause ↔ Resume → Stop.
Can expose typed services to other modules via
IModuleContext.ProvideModuleService<T>().
The module system handles startup ordering automatically — modules are started in dependency order and stopped in reverse.
Module lifecycle
The lifecycle has five states. RegisterAsync runs for all modules before any StartAsync call, giving every module a guaranteed window to register services before any module begins active processing.
For the full lifecycle method contract and implementation examples, see Implement a custom module.
Safe extension points and internal APIs
Use only the public extension points listed below. Internal types and platform-specific layers change without notice.
Safe extension points
Custom module behavior
Implement IConvaiModule and register via RegisterModule() or AddModule()
Inter-module services
ProvideModuleService<T>() / TryGetModuleService<T>()
Custom credentials
Override CreateRuntimeBuilder() and call builder.UseConfig()
Custom identity
SetEndUserIdentityProvider() / SetEndUserMetadataProvider(), or builder
Custom persistence
Override CreateRuntimeBuilder() and call builder.UsePersistence()
Character-level component integration
Implement IInjectable<IConvaiCharacterDependencies> on a MonoBehaviour in the character's hierarchy
Event subscriptions
IEventHub.Subscribe<T>() / Unsubscribe<T>()
Log routing
ConvaiLogger.RegisterSink(ILogSink)
Internal APIs — do not override
ConvaiRuntime internals
Private by design; subject to change without notice
Transport layer (ITransportProvider implementations)
Platform-specific; overriding breaks platform support
RTVI protocol handler (RTVIHandler)
Serialization format is tied to Convai's backend — any override desyncs the protocol
ConvaiRoomManager internals
Room coordinator internals are not extension points
Module execution ordering beyond RequiredModules
The topological sort is automatic; do not rely on registration order
Do not reflect into internal types or bypass the builder to inject dependencies. SDK internals change between versions — code that bypasses the public API will break on upgrade with no warning.
Next steps
Implement a custom moduleEvent systemLast updated
Was this helpful?