UnifiedEvent & UnifiedAction: The Type System Powering Every Integration
UnifiedEvent & UnifiedAction: The Type System Powering Every Integration
Sidekick connects to 100+ services — chat platforms, email, calendars, dev tools, smart home devices, and more. For the agent engine to reason across all of them coherently, every integration must speak the same language. That language is the UnifiedEvent and UnifiedAction type system, introduced in v1.0.0.
The Problem: 100+ APIs, 100+ Shapes
Every external service has its own event format. A GitHub push webhook looks nothing like a Slack message, which looks nothing like a Google Calendar update. Without normalization, the agent engine would need bespoke logic for every service — impossible to maintain and impossible to scale.
Sidekick solves this with a unified adapter architecture. Every integration is responsible for one thing: translating its native API payloads into a standard shape the engine understands. That standard shape is UnifiedEvent (for inbound data) and UnifiedAction (for outbound instructions).
UnifiedEvent
A UnifiedEvent represents anything that happened in a connected service — a new email arrived, a repo was pushed to, a smart home sensor triggered. Regardless of which integration produced it, every event the agent engine receives is a UnifiedEvent.
// Every adapter normalizes its raw webhook/poll payload into this shape
interface UnifiedEvent {
id: string; // Unique event ID
source: string; // Integration identifier (e.g. "github", "gmail")
type: string; // Event type (e.g. "push", "message.received")
timestamp: string; // ISO 8601 timestamp
payload: Record<string, unknown>; // Normalized event data
raw?: unknown; // Original payload (preserved for debugging)
}
Validation
All UnifiedEvent instances are validated at the adapter boundary using a Zod schema. If an adapter produces a malformed event, it is caught and rejected before it can reach the agent engine:
import { UnifiedEventSchema } from '@sidekick/types';
const result = UnifiedEventSchema.safeParse(rawPayload);
if (!result.success) {
// Invalid event — logged and dropped before reaching the engine
}
UnifiedAction
A UnifiedAction represents something the agent engine wants to do — send a Slack message, create a calendar event, merge a pull request. The engine emits actions in this standard shape; adapters translate them into the appropriate API calls.
// The agent engine emits this shape; adapters execute the underlying API call
interface UnifiedAction {
id: string; // Unique action ID
target: string; // Integration identifier (e.g. "slack", "gcal")
type: string; // Action type (e.g. "message.send", "event.create")
payload: Record<string, unknown>; // Action parameters
correlationId?: string; // Links the action back to a triggering event
}
Validation
Like events, all actions are validated before dispatch:
import { UnifiedActionSchema } from '@sidekick/types';
const result = UnifiedActionSchema.safeParse(agentOutput);
if (!result.success) {
// Invalid action — not dispatched to the adapter
}
Database Serialization
Both types include serialization helpers for persisting to and restoring from the database. This enables:
- Audit trails — every event received and action taken is recorded.
- Replay — events can be reprocessed if an adapter or skill is updated.
- Retry — failed actions can be re-dispatched without re-fetching the original payload.
import { serializeUnifiedEvent, deserializeUnifiedEvent } from '@sidekick/types';
// Persist
const row = serializeUnifiedEvent(event);
await db.insert('events', row);
// Restore
const event = deserializeUnifiedEvent(row);
Why This Matters for ClawHub Skills
Sidekick is fully compatible with OpenClaw's 13,000+ community skills via ClawHub. Every skill is written against the same UnifiedEvent/UnifiedAction contract. When a SKILL.md is imported, Sidekick's runtime maps its event triggers and action outputs to this type system automatically — local binary requirements are translated to cloud services transparently. No changes to the skill are needed.
Building an Adapter
If you are implementing a new integration, your adapter has two responsibilities:
- Inbound: Translate raw payloads from the external service into
UnifiedEventinstances and pass them to the engine. - Outbound: Accept
UnifiedActioninstances from the engine and execute the appropriate API call against the external service.
The type definitions and Zod schemas are exported from @sidekick/types:
import {
UnifiedEvent,
UnifiedEventSchema,
UnifiedAction,
UnifiedActionSchema,
serializeUnifiedEvent,
deserializeUnifiedEvent,
serializeUnifiedAction,
deserializeUnifiedAction,
} from '@sidekick/types';
Every adapter that correctly implements this contract works with the full Sidekick platform — including the agent engine, the audit log, action retry, and ClawHub skill compatibility — with no additional integration work.