Invocation Model
How all agent work flows through a single unified entry point with consistent policy enforcement — rate limiting, idempotency, trigger validation, and observability.
Unified Entry Point
Every agent invocation — whether from the API, a channel message, a cron schedule, a workflow step, or a system event — flows through the same pipeline:
Invocation Sources
Each invocation carries metadata about its origin:
| Source | Description | Always Accepted? |
|---|---|---|
Api | Direct HTTP API call | Yes (every agent implicitly accepts) |
Channel | Message from Slack, Telegram, etc. | Only if agent has matching channel trigger |
Cron | Fired by a scheduled job | Yes (schedules are platform-managed) |
Workflow | Step in a multi-agent pipeline | Only if agent has a workflow trigger |
Event | System/lifecycle event | Only if agent has matching event pattern |
Trigger Acceptance Logic
InvocationContext
The full payload delivered to the invocation layer:
{
"source": {
"Workflow": {
"workflow_id": "a1b2c3d4-...",
"step_index": 2,
"upstream_agent_id": "550e8400-..."
}
},
"message": "Process this claim...",
"idempotency_key": "req-abc123",
"correlation_id": "7c9e6679-..."
}
| Field | Purpose |
|---|---|
source | Origin metadata for routing, telemetry, and audit |
message | The payload to deliver to the agent |
idempotency_key | Optional deduplication key (requests with same key return cached result) |
correlation_id | UUID for distributed tracing across steps/services |
Rate Limiting
Per-agent sliding window rate limiter:
- Window: 60 seconds (sliding)
- Default limit: 60 invocations per minute per agent
- Implementation: in-memory
HashMap<AgentId, Vec<Instant>>— timestamps older than 60s are evicted on each check - Disabled: set limit to 0 for a specific agent
When exceeded:
{
"error": {
"category": "RateLimited",
"message": "Agent has exceeded the rate limit of 60 invocations per minute",
"details": {"agent_id": "...", "limit": "60"}
}
}
Idempotency
Optional deduplication for exactly-once semantics:
- Requests with the same
idempotency_keyreturn the cached result without re-executing - Cache is in-memory with TTL (bounded size)
- Useful for webhook retries and workflow step replays
Trigger Registry
The trigger registry manages per-agent trigger configurations and synchronizes with agent lifecycle:
Trigger States
| State | Meaning |
|---|---|
Active | Trigger is accepting matching events |
Paused | Temporarily inactive (agent suspended) |
Disabled | Permanently inactive (agent terminated, cannot reactivate) |
Lifecycle Synchronization
| Agent Transition | Trigger Effect |
|---|---|
| Running → Suspended | All triggers → Paused |
| Suspended → Running | All triggers → Active |
| Any → Terminated | All triggers → Disabled |
Telemetry
Every invocation records structured metrics:
{
"agent_id": "550e8400-...",
"source": "Workflow",
"duration_ms": 2100,
"tokens_used": 342,
"success": true,
"correlation_id": "7c9e6679-..."
}
Available via the API as per-agent invocation metrics:
- Total invocations
- Error count / error rate
- Average duration
- Per-source breakdown (how many from API vs Channel vs Cron vs Workflow)
InvocationResult
The structured response returned from every invocation:
{
"content": "There are 23 open claims in the queue.",
"tokens_used": 342,
"duration_ms": 2100,
"correlation_id": "7c9e6679-...",
"source": {"Api": {}}
}
Trigger Types
Agents declare what invocation sources they accept via trigger configuration:
# In agent config
triggers = [
{ lifecycle = {} },
{ channel = { channel_type = "slack" } },
{ cron = { expression = "0 */6 * * *" } },
{ workflow = {} },
{ event = { pattern = "agent_spawned:claims-*" } }
]
| Trigger Type | Accepts Source |
|---|---|
lifecycle | Lifecycle events (boot, shutdown) |
channel | Channel messages matching channel_type |
cron | Scheduled invocations (platform-managed) |
workflow | Workflow step invocations |
event | System events matching pattern |
api | Direct API calls (implicit, always present) |