Develop and extend
How-to

Add a communication platform

Add a communication platform across typed configuration, encrypted credentials, lifecycle orchestration, runtime builders, message access controls, telemetry, health, webhooks, and onboarding.

For
Platform developers and maintainers
On this page
  1. What you will accomplish
  2. Before you begin
  3. Understand the platform boundary
  4. Plan the platform contract
  5. Add the persisted configuration
  6. Add the database migration
  7. Extend Agent lifecycle orchestration
  8. Build runtime configuration
  9. Enforce message access controls
  10. Add inbound message delivery
  11. Extend telemetry and activity
  12. Add health and observability
  13. Add the web app experience
  14. Test the complete platform
  15. Verify the platform end to end
  16. Troubleshooting
  17. Completion checklist
  18. Source map
  19. Related documentation
  • Platforms
  • Platform contribution
  • 25 minutes

A communication platform carries messages between people and an Agent. Adding one requires more than accepting a bot token: Agent Barn must persist a typed configuration, enforce runtime compatibility, generate safe Hermes or OpenClaw resources, control who can invoke the Agent, ingest conversation activity, expose health, and provide a complete onboarding and configuration experience.

This guide covers communication transports such as Slack, Microsoft Teams, Telegram, and Discord. For external tools such as GitHub, Jira, Confluence, Google Workspace, or Pipedrive, use Add an integration.

Audience
Platform developers and maintainers
Repository
agent-barn
Deployment namespace
Existing deployment resources continue to use the agent-farm namespace conventions.

What you will accomplish

By the end of this guide, you will be able to:

  • Distinguish a communication platform from an external provider integration
  • Define a runtime and transport compatibility contract
  • Add typed, encrypted platform configuration
  • Extend Agent creation, updates, reads, start, stop, and deletion
  • Generate platform configuration for Hermes, OpenClaw, or both
  • Enforce channel, group, direct-message, and mention policies
  • Add a provider-authenticated webhook when the platform requires one
  • Preserve platform identity in telemetry and conversation activity
  • Extend runtime health checks and monitoring
  • Add onboarding and configuration UI
  • Verify the platform through unit, API, browser, runtime, and Kubernetes tests

Before you begin

You should have:

  • A working Agent Barn development environment
  • Docker for migrated PostgreSQL integration tests
  • Access to a non-production account on the target platform
  • Test application or bot credentials
  • The platform’s official bot, messaging, webhook, and authorization documentation
  • A confirmed integration mechanism in Hermes, OpenClaw, or both
  • A clear decision about direct messages, groups, channels, mentions, and identity restrictions

Read these guides first:

Understand the platform boundary

A communication platform and an integration belong to different contracts.

Communication platform External provider integration
Carries inbound and outbound Agent messages Gives the Agent tools for an external service
Stored as Agent.platform Stored as an Agent Secret provider
Has dedicated platform configuration Uses a provider-specific credential content model
Participates directly in Agent startup Is materialized after platform runtime setup
Controls groups, channels, DMs, and mentions Controls service scopes and tool capabilities
May require a public webhook or persistent connection Usually uses a CLI profile, OAuth token, or runtime capability
Determines conversation identity and routing Does not determine the Agent’s primary message transport

Current runtime matrix

Runtime Slack Teams Telegram Discord
Hermes Yes No Yes Yes
OpenClaw Yes Yes Yes Yes

Runtime and platform are separate persisted choices:

Persisted choices
agent.agent_type → openclaw or hermes
agent.platform   → slack, teams, telegram, or discord

A new platform does not have to support both runtimes. It must, however:

  • Declare the supported combinations
  • Reject unsupported combinations in the API
  • Hide or disable incompatible choices in the web app
  • Document the resulting matrix
  • Test every supported combination
  • Test rejection of every unsupported combination

Do not silently fall back to a different runtime.

Plan the platform contract

Before editing AgentPlatform, define the complete platform contract.

Transport

Decide how messages reach the runtime:

  • Persistent socket connection
  • Long polling
  • Provider webhook
  • Public API callback relayed to the Agent pod
  • A runtime-native gateway plugin

For a webhook platform, decide:

  • Which public URL the provider calls
  • Which component validates the provider signature or bearer credential
  • Whether the API handles the event or proxies it to the runtime
  • Which headers must be preserved
  • Which internal Service port receives the request
  • How retries and duplicate deliveries are handled
  • What happens when the Agent is stopped
  • Whether the callback URL depends on API_EXTERNAL_URL

Credentials

Identify:

  • Required credentials
  • Optional credentials
  • Which values are secret
  • Which values may be safely returned
  • Whether credentials can be validated before persistence
  • Whether one bot credential may be used by multiple Agents
  • Whether rotation changes the external bot identity
  • Whether credentials are runtime-specific

Message access

Define independent policies for:

  • Shared groups, workspaces, guilds, or servers
  • Individual channels or chats
  • Direct messages
  • Allowed users
  • Allowed roles
  • Home or alert destinations
  • Mention requirements
  • Thread or reply behavior

Use fail-closed defaults unless the product contract deliberately requires open access.

Lifecycle

Decide:

  • Whether creation leaves the Agent stopped
  • Whether the canonical UI starts it during provisioning
  • Whether API creation itself auto-starts it
  • What credential failures produce 400 versus ERROR
  • Which platform settings can be changed
  • Whether changes require Apply or Apply & Restart
  • How stop, delete, and credential rotation affect the external connection

Activity

Define how the runtime identifies:

  • Message ID
  • Session key
  • Channel or chat ID
  • Channel display name
  • Thread ID
  • Sender ID
  • Sender display name
  • Conversation type
  • Inbound versus outbound direction
  • Tool-call identity

Do not derive these rules after deployment from whatever happens to appear in logs.

Add the persisted configuration

The authoritative Agent platform models live in api/domains/agents/models.py. A platform addition normally needs:

  1. A new AgentPlatform value
  2. A dedicated database configuration model
  3. Create fields
  4. Update fields
  5. A read-safe configuration DTO
  6. A field on AgentRead
  7. Compatibility validation between platform and runtime
Platform enum
class AgentPlatform(str, enum.Enum):
    # Existing platforms...
    RELAYCHAT = "relaychat"

Use a dedicated configuration table:

Configuration table
class AgentRelayChatConfig(BaseModel, table=True):
    __tablename__: str = "agent_relaychat_config"

    agent_id: UUID = SqlField(
        foreign_key="agent.id",
        nullable=False,
        unique=True,
        ondelete="CASCADE",
    )
    bot_token_encrypted: str = SqlField(nullable=False)
    workspace_ids: list[str] = SqlField(
        default_factory=list,
        sa_column=Column(sa.JSON(), nullable=False, server_default="[]"),
    )
    channel_ids: list[str] = SqlField(
        default_factory=list,
        sa_column=Column(sa.JSON(), nullable=False, server_default="[]"),
    )
    allowed_user_ids: list[str] = SqlField(
        default_factory=list,
        sa_column=Column(sa.JSON(), nullable=False, server_default="[]"),
    )
    group_policy: str = SqlField(
        default="allowlist",
        sa_column=Column(sa.String(), nullable=False, server_default="allowlist"),
    )
    dm_policy: str = SqlField(
        default="off",
        sa_column=Column(sa.String(), nullable=False, server_default="off"),
    )

Prefer platform-specific policy enums when the values form a real closed contract.

Policy enums
class RelayChatGroupPolicy(str, enum.Enum):
    OPEN = "open"
    ALLOWLIST = "allowlist"


class RelayChatDmPolicy(str, enum.Enum):
    OFF = "off"
    OPEN = "open"
    ALLOWLIST = "allowlist"

Separate secrets from safe configuration

The read DTO must exclude secrets.

Read DTO
class AgentRelayChatConfigRead(PydanticBaseModel):
    model_config = ConfigDict(from_attributes=True)

    workspace_ids: list[str]
    channel_ids: list[str]
    allowed_user_ids: list[str]
    group_policy: RelayChatGroupPolicy
    dm_policy: RelayChatDmPolicy
    bot_username: str | None = None

Extend create and update contracts

Add platform-specific fields to AgentCreate and AgentUpdate.

Create fields
class AgentCreate(PydanticBaseModel):
    relaychat_bot_token: str | None = Field(default=None, min_length=1)
    relaychat_workspace_ids: list[str] = Field(default_factory=list)
    relaychat_channel_ids: list[str] = Field(default_factory=list)
    relaychat_allowed_user_ids: list[str] = Field(default_factory=list)
    relaychat_group_policy: RelayChatGroupPolicy = RelayChatGroupPolicy.ALLOWLIST
    relaychat_dm_policy: RelayChatDmPolicy = RelayChatDmPolicy.OFF

Validate required credentials and runtime compatibility in the request model.

Compatibility validator
@model_validator(mode="after")
def validate_platform_credentials(self):
    if (
        self.platform == AgentPlatform.RELAYCHAT
        and self.agent_type not in {AgentType.OPENCLAW}
    ):
        raise ValueError("RelayChat is supported only by OpenClaw")

    if (
        self.platform == AgentPlatform.RELAYCHAT
        and not self.relaychat_bot_token
    ):
        raise ValueError(
            "relaychat_bot_token is required for RelayChat agents"
        )

    return self

Reject fields belonging to another platform during updates. A Slack Agent must not silently accept RelayChat configuration, and a RelayChat Agent must not silently accept Slack fields.

Preserve platform immutability

The platform is selected when the Agent is created. It is not changed through AgentUpdate. Moving an existing Agent to another platform would require:

  • A new credential contract
  • Removal of old platform configuration
  • Creation of new configuration
  • Potential runtime compatibility changes
  • Conversation-history interpretation
  • External bot lifecycle handling
  • A transaction covering the complete transition

Do not introduce platform mutation as a side effect of adding a new platform.

Add the database migration

A new communication platform requires an Alembic migration. The migration normally:

  1. Replaces ck_agent_platform with a constraint that includes the new value
  2. Creates the platform configuration table
  3. Adds a foreign key to agent.id with ON DELETE CASCADE
  4. Makes agent_id unique
  5. Adds indexes or constraints required by the platform
  6. Defines safe server defaults for JSON collections and policies
  7. Provides a deliberate downgrade

An illustrative constraint change is:

Constraint change
op.drop_constraint("ck_agent_platform", "agent", type_="check")
op.create_check_constraint(
    "ck_agent_platform",
    "agent",
    (
        "platform IN "
        "('slack', 'teams', 'telegram', 'discord', 'relaychat')"
    ),
)

The current agent.platform column is a bounded string. Confirm that the new persisted value fits its database length, or widen the column in the migration.

Decide whether bot identities must be unique

Slack and Discord enforce distinct active bot tokens across Agents. Their configuration tables store:

  • The encrypted token for runtime use
  • A SHA-256 token hash for equality checks
  • A unique partial index over non-null hashes

The hash is not a credential substitute. It exists only to enforce uniqueness without comparing decrypted values.

If a platform prohibits one bot identity from serving multiple Agent deployments, add the same kind of database-backed invariant. Do not rely only on an application pre-check; concurrent requests must still be stopped by the database.

Translate the named uniqueness constraint into a stable 409 Conflict.

Extend Agent lifecycle orchestration

Agent lifecycle behavior is orchestrated in api/domains/agents/service.py. The current implementation uses explicit platform branches. Keep the new behavior typed and locally discoverable rather than introducing an untyped configuration blob.

Add service field groups

Define the platform’s complete update field set.

Field group
_RELAYCHAT_CONFIG_FIELDS = frozenset(
    {
        "relaychat_bot_token",
        "relaychat_workspace_ids",
        "relaychat_channel_ids",
        "relaychat_allowed_user_ids",
        "relaychat_group_policy",
        "relaychat_dm_policy",
    }
)

Add secret-bearing fields to _CREDENTIAL_FIELDS so rotating them requires agent.secret.manage.

Credential fields
_CREDENTIAL_FIELDS = frozenset(
    {
        # Existing credential fields...
        "relaychat_bot_token",
    }
)

Routing and access-policy fields use agent.update. Credential mutation additionally requires agent.secret.manage.

Extend creation

During creation:

  1. Require agent.create
  2. Validate runtime compatibility
  3. Validate credentials before persistence when possible
  4. Validate any global bot-identity uniqueness rule
  5. Create the Organization-owned Agent and creator access atomically
  6. Encrypt credentials
  7. Persist the platform configuration
  8. Persist Skills and integrations
  9. Follow the chosen start behavior
  10. Return only read-safe configuration

Do not leave a partially created Agent if platform configuration persistence fails. Follow the existing cleanup or explicit transaction pattern appropriate to the surrounding creation flow.

Extend reads and lists

Update:

  • Detail hydration
  • Paginated list hydration
  • Platform-specific bulk repository reads
  • AgentRead
  • Safe platform configuration mapping
  • UI Zod schemas
  • Test fixtures

List behavior must remain Organization- and Agent Access-scoped before count and pagination.

Extend updates

For stopped Agents:

  • Reject fields belonging to a different platform
  • Revalidate rotated credentials
  • Encrypt new secret values
  • Preserve omitted secrets
  • Update only explicitly supplied fields
  • Recheck any uniqueness invariant
  • Persist routing and policy fields
  • Return read-safe configuration

Running Agent configuration changes use the existing Apply & Restart workflow. The API still independently rejects direct updates while the Agent is running.

Extend start

Add a platform branch that:

  1. Loads the platform configuration
  2. Fails clearly if the configuration row is missing
  3. Decrypts credentials
  4. Revalidates credentials when start-time validation is part of the contract
  5. Builds runtime-specific configuration
  6. Builds a Kubernetes Secret
  7. Builds a Service with any required ports
  8. Selects the proper Deployment builder
  9. Continues through shared integrations, Skills, ingest, and Kubernetes assembly

A recoverable credential or connection failure may move the Agent to ERROR. A successful later start clears the previous error.

Extend stop and delete

Verify that stop and delete:

  • Snapshot logs where applicable
  • Remove active Kubernetes resources
  • Preserve historical Agent identity and activity
  • Release token uniqueness state when the Agent is deleted
  • Do not log credentials
  • Preserve lifecycle Domain Event behavior
  • Return the expected status and authorization errors

Build runtime configuration

Runtime builders live under api/domains/agents/builders/. Add explicit builders for each supported runtime.

OpenClaw

An OpenClaw platform normally needs:

  • A channel configuration block
  • A binding to the main Agent
  • A secret builder
  • Environment variable names expected by the runtime plugin
  • Plugin installation or restoration
  • Mention, group, DM, and identity policy mapping
  • Telemetry compatibility
  • Health behavior

An illustrative configuration builder is:

Config overlay
def build_openclaw_config_overlay_relaychat(
    model: str,
    litellm_base_url: str,
    channel_ids: list[str] | None = None,
    allowed_user_ids: list[str] | None = None,
    group_policy: str = "allowlist",
    dm_policy: str = "off",
    approval_mode: str = "auto",
) -> dict:
    return _openclaw_config_core(
        model,
        litellm_base_url,
        binding_channel="relaychat",
        channels={
            "relaychat": {
                "enabled": True,
                "groupPolicy": group_policy,
                "dmPolicy": dm_policy,
                "allowFrom": allowed_user_ids or [],
                "requireMention": True,
                "channels": {
                    channel_id: {
                        "enabled": True,
                        "requireMention": True,
                    }
                    for channel_id in (channel_ids or [])
                },
            }
        },
    )

The Secret builder should include only required runtime values:

Secret builder
def build_secret_relaychat(
    agent_id: UUID,
    org_id: UUID,
    namespace: str,
    relaychat_bot_token: str,
    litellm_api_key: str,
    litellm_base_url: str,
) -> client.V1Secret:
    return client.V1Secret(
        metadata=client.V1ObjectMeta(
            name=_resource_name(agent_id),
            namespace=namespace,
            labels=_labels(agent_id, org_id),
        ),
        string_data={
            "RELAYCHAT_BOT_TOKEN": relaychat_bot_token,
            "LITELLM_API_KEY": litellm_api_key,
            "LITELLM_BASE_URL": litellm_base_url,
            "AGENT_PLATFORM": "relaychat",
        },
    )

Register new builder exports in api/domains/agents/builders/__init__.py.

If the connector is an optional OpenClaw package, update:

  • The pinned base image
  • Runtime initialization
  • Startup installation or restoration
  • Base-image smoke tests
  • Workflow contract checks

Hermes

A Hermes platform normally needs:

  • A platform-specific Hermes configuration section
  • Display configuration
  • Platform credentials in the Kubernetes Secret
  • AGENT_PLATFORM
  • Access-control plugins when upstream controls are insufficient
  • ConfigMap inclusion for those plugins
  • Startup installation
  • Health and circuit-breaker awareness
  • Pinned-image verification

Hermes access-control plugins should fail closed. Missing IDs, malformed events, or unavailable configuration must not accidentally allow messages.

Keep secrets out of the ConfigMap

Kubernetes Secret data may contain

  • Bot tokens
  • App tokens
  • App passwords
  • Signing secrets
  • Runtime API keys
  • Ingest keys
  • LiteLLM keys

ConfigMaps may contain

  • Runtime configuration without credential values
  • Access policies
  • Setup scripts that read environment variables
  • Plugin source
  • Rendered Agent Markdown
  • Skill manifests

Enforce message access controls

Every supported platform must define safe shared-room behavior.

Mention gating

In a shared channel or group, an Agent responds only when explicitly mentioned.

A fresh mention is required for each shared-room message whenever the runtime can enforce it. Previous thread participation must not grant indefinite permission to respond.

Direct messages are exempt from mention gating but remain subject to the direct-message policy.

Do not rely on runtime defaults. Generate the setting explicitly so an upstream default change cannot silently open access.

Group and channel boundaries

Treat these as separate decisions:

  • Which workspaces, servers, guilds, or groups are allowed
  • Which channels or chats inside them are allowed
  • Whether the group policy is open or allowlisted
  • Whether an empty allowlist denies all or means unrestricted

Document and test the empty-list behavior.

User and role boundaries

A platform may support:

  • Explicit user IDs
  • Role IDs
  • Organization or workspace membership
  • Open user access inside an allowed channel boundary

If open user access is disabled, require at least one allowed user or role. Enforce this in both request validation and runtime configuration.

Direct messages

Use an explicit policy:

Policy Behavior
off Ignore all direct messages
allowlist Accept direct messages only from configured users
open Accept direct messages from any platform user who can reach the bot

Do not represent off as an undocumented empty allowlist unless the runtime mapping and tests prove that behavior.

Runtime parity

When both runtimes support the platform, verify that the same product policy produces equivalent behavior even when runtime configuration differs. Equivalent product behavior is more important than identical configuration syntax.

Add inbound message delivery

Choose one delivery topology.

Persistent connection or polling

For socket or polling transports:

  • The runtime connects directly to the platform
  • Credentials are injected into the pod
  • No public Agent callback route is required
  • Start-time credential validation should fail clearly
  • Connection loss must be visible through health

Provider webhook

For webhook transports, add a thin public route and keep platform workflow in the service or runtime. A route may:

  1. Read the raw body
  2. Preserve the headers needed for authentication
  3. Delegate to the service
  4. Return the proxied status, body, and content type

The service must:

  • Resolve the Agent without crossing tenant boundaries
  • Return 404 for missing, deleted, or wrong-platform Agents
  • Reject delivery while the Agent is not running
  • Identify the correct internal Agent Service
  • Proxy to the platform-specific port and path
  • Preserve only required headers
  • Bound request size and timeout according to the provider contract
  • Ensure that the request is authenticated either before proxying or by the runtime receiving it

Public callback URL

If the platform needs a callback URL, derive it from the configured external API URL:

Callback URL
https://agent-barn.example.com/api/v1/webhooks/<platform>/<agent-id>/messages

Return the URL as safe Agent metadata when operators must copy it into the provider console.

If the Agent pod needs a new webhook port, extend the shared Service builder with a named port. Do not expose the pod directly through a separate public ingress when the API relay is the intended boundary.

Extend telemetry and activity

Both runtimes report normalized messages and tool-call state to the Ingest API using a fresh per-start ingest key. A platform addition must preserve:

  • Agent ID
  • Message ID
  • Session key
  • Conversation type
  • Direction
  • Channel or chat identity
  • Thread identity
  • Sender identity
  • Occurrence time
  • Tool-call external identity

Runtime telemetry

If the existing telemetry plugin already observes the new channel, add contract fixtures proving its payloads.

If the runtime emits a different record shape:

  • Extend the runtime-side normalization
  • Validate posted payloads against the real Ingest models
  • Cover inbound and outbound messages
  • Cover threads and direct messages
  • Cover pending and completed tool calls
  • Run the contract inside the pinned runtime image when behavior depends on upstream internals

Conversation parsing

Inspect api/domains/conversations/. Update platform-specific session prefixes or fallback parsers when required. Do not add parser branches when the normalized push path already provides the complete contract.

Identity enrichment

Agent Barn enriches missing user and channel names best-effort. If the platform offers safe directory lookup:

  • Add a small infrastructure client
  • Decrypt the platform credential only at the service boundary
  • Resolve only missing IDs
  • Bound timeouts
  • Cache provider lookups when appropriate
  • Treat enrichment failure as non-fatal
  • Avoid logging tokens or full provider responses

Update both Ingest-time enrichment and Conversation read-time enrichment when the platform needs them.

Teams currently provides no equivalent directory enrichment. A new platform may also deliberately return stored IDs when no safe lookup exists.

Add health and observability

A platform is incomplete if operators cannot distinguish a healthy Agent from a disconnected bot. Review:

Health servers
api/domains/agents/scripts/hermes/healthz-server.py
api/domains/agents/scripts/openclaw/healthz-server.js

Extend health behavior to cover:

  • Missing credentials
  • Invalid or revoked credentials
  • Runtime channel connection state
  • Platform circuit-breaker state
  • Manual pause versus failed connection
  • Provider timeouts
  • Unsupported AGENT_PLATFORM values

Health checks must not mutate provider state.

When the platform uses Hermes, preserve the distinction between:

  • A recoverable platform connection failure
  • A circuit breaker opened by repeated failures
  • A platform intentionally paused by an operator

Update:

  • Health unit tests
  • Runtime builder tests
  • Kubernetes readiness behavior
  • Base-image smoke tests
  • Platform-specific metrics only when they express a stable operational contract

The Agent Service and monitoring discovery should continue using the stable Agent Service labels. Do not create a new monitoring topology solely for one communication platform.

Add the web app experience

A platform contribution must update every web app boundary that treats the platform as a closed union.

API schemas and types

Update:

Schema and hook files
ui/src/features/agents/schemas.ts
ui/src/features/agents/hooks/use-create-agent.ts
ui/src/features/agents/hooks/use-update-agent.ts

Add the platform to:

  • AgentSchema.platform
  • Create request platform unions
  • Update request fields
  • Platform-specific read schema
  • Agent response schema
  • Test fixtures

Hiring flow

Update:

Hiring files
ui/src/features/agents/components/hire-dialog.tsx
ui/src/features/agents/components/hire-dialog-steps.tsx

Add:

  • Platform choice
  • Runtime compatibility behavior
  • Credential step
  • Provider setup instructions
  • Required-field validation
  • Channel and DM policy defaults
  • Create payload mapping
  • Provisioning behavior
  • Completion behavior
  • Accessible labels
  • Playwright coverage

If the platform requires a downloadable application manifest, generate only fields grounded in the provider specification. Treat URLs, permission IDs, scopes, and icon requirements as versioned external contracts.

Agent configuration

Update:

Configuration files
ui/src/features/agents/components/agent-channel-settings.tsx
ui/src/features/agents/components/agent-keys-settings.tsx

Provide:

  • Read-only configuration summary
  • Credential rotation
  • Channel or group editing
  • User and role restrictions
  • Direct-message policy
  • Callback endpoint display when applicable
  • Apply or Apply & Restart behavior
  • Permission-aware controls

Reuse the shared shadcn Select primitives for user-facing selectors. Keep SelectItem components inside a SelectGroup. Do not introduce browser-native confirmation dialogs.

Platform presentation

Update platform-sensitive display surfaces such as:

  • Agent cards
  • Platform badges
  • Filters
  • Empty states
  • Provisioning text
  • Activity labels
  • Fixtures and page-object support

Do not infer runtime compatibility only in presentation code. The backend remains authoritative.

Test the complete platform

Cover each contract at the lowest reliable layer, then test the assembled workflow.

Concern Minimum coverage
Platform value Request and response schema tests
Runtime compatibility Valid combinations and rejected combinations
Required credentials Missing, blank, and malformed input
Credential encryption Plaintext absent from persisted rows and read responses
Configuration model Defaults, constraints, and cascade behavior
Migration Fresh install and upgrade to the new head
Bot uniqueness Pre-check, database race protection, and release on deletion
Create Platform config is persisted and safe metadata returned
Update Routing changes, credential rotation, wrong-platform rejection
Running update 409 direct update behavior
Authorization agent.update, agent.secret.manage, inaccessible Agent, cross-Organization
Start Missing config, invalid credentials, resource generation, status transition
Stop Log snapshot and runtime cleanup
Delete Runtime cleanup, soft deletion, and token-hash release
OpenClaw builder Exact channel, binding, policy, secret, and plugin config
Hermes builder Exact platform, policy plugins, secret, and ConfigMap config
Mention gating Every supported runtime/platform pair
Group policy Open and allowlisted behavior
DM policy Off, allowlisted, and open behavior
User restrictions Open-user and restricted-user behavior
Webhook Authentication headers, wrong platform, stopped Agent, internal proxy
Telemetry Posted payload validates against real Ingest models
Conversations Platform session identity, threads, and names
Health Valid, invalid, disconnected, paused, and unsupported states
Web app Hiring, configuration, rotation, channel policy, and restart
Runtime image Connector exists and its expected contract works in the pinned image

Representative test locations include:

Test locations
api/tests/unit/test_openclaw_builders.py
api/tests/unit/test_hermes_builders.py
api/tests/unit/test_healthz_server_metrics.py
api/tests/unit/test_hermes_telemetry_push_plugin.py
api/tests/unit/test_openclaw_telemetry_push_plugin.py
api/tests/integration/test_agents.py
api/tests/integration/test_agent_rbac.py
api/tests/integration/test_ingest.py
api/tests/integration/test_conversations.py
api/tests/integration/test_tool_calls.py
api/tests/integration/test_kubernetes_client.py
ui/tests/e2e/hire-dialog.spec.ts
ui/tests/e2e/agent-configuration-page.spec.ts
ui/tests/pages/data-support/agent-data-support.po.ts

Run the relevant repository checks:

Verification
make check-api
make check-migrations
make test-api
make test-api-k8s
make lint-ui
make check-ui
make test-ui

Run API coverage when evaluating untested branches:

Coverage
make coverage

When the platform changes a runtime image, also run the matching image build, smoke test, and workflow contract checks.

Verify the platform end to end

Use a non-production provider application.

  1. Create the platform application or bot.
  2. Grant only the required scopes, permissions, intents, or events.
  3. Select a supported Agent runtime.
  4. Choose the new communication platform.
  5. Enter the required credentials.
  6. Configure the most restrictive practical group and DM policies.
  7. Create the Agent.
  8. Confirm the API response excludes credential values.
  9. Confirm the configuration row contains encrypted credentials.
  10. Start the Agent.
  11. Confirm the expected Kubernetes Secret, ConfigMap, Service, and Deployment exist.
  12. Confirm the pod becomes ready.
  13. Send an unmentioned message in a shared room and confirm the Agent does not respond.
  14. Mention the Agent and confirm it responds.
  15. Send a second unmentioned message in the same thread and confirm the documented fresh-mention behavior.
  16. Test an unauthorized channel, group, user, or role.
  17. Test direct messages under every supported policy.
  18. Confirm inbound and outbound messages appear in Agent activity.
  19. Confirm tool calls appear with stable identities.
  20. Rotate the platform credential through Apply & Restart.
  21. Revoke the old credential and confirm the Agent uses the replacement.
  22. Stop the Agent and verify delivery behavior while stopped.
  23. Delete a test Agent and confirm runtime resources are removed.
  24. Review logs, Domain Events, activity, and generated resources for accidental secret exposure.

For a webhook platform, also:

  1. Configure the public callback URL.
  2. Send a provider-authenticated callback.
  3. Confirm the API relays it to the correct running Agent.
  4. Confirm a wrong-platform Agent returns 404.
  5. Confirm a stopped Agent rejects delivery.
  6. Confirm invalid signatures or credentials are rejected.
  7. Confirm duplicate provider delivery follows the documented idempotency behavior.

Troubleshooting

Symptom Likely cause What to check
Platform value returns 422 Enum, database constraint, or UI union is incomplete AgentPlatform, migration, Zod schema, request types
Runtime/platform pair returns 422 The selected runtime is unsupported Compatibility validator and UI choice logic
Agent creation succeeds without a config row Creation branch or repository save is missing Agent Service platform branch and cleanup behavior
Credentials appear in the API response Unsafe configuration DTO Platform read model and UI schema
Credential rotation is allowed without secret permission Field is absent from _CREDENTIAL_FIELDS Credential-field classification and RBAC tests
Wrong-platform fields are accepted Cross-platform update validation is incomplete Platform field sets and rejection matrix
Agent starts but cannot receive messages Runtime connector or transport configuration is incomplete Plugin, binding, webhook, token, Service port
Agent responds to unmentioned messages Mention gating was left to a runtime default Explicit builder configuration and runtime contract tests
Agent ignores every group message Empty allowlist semantics or group mapping is wrong Group policy, IDs, and runtime translation
Direct messages remain open off was mapped incorrectly DM policy translation and access plugins
Hermes works but OpenClaw fails Only one builder, script, or image was updated Runtime-specific builder and base-image contract
OpenClaw works but Hermes fails Hermes plugin or startup installation is missing ConfigMap plugin files, startup script, health state
Webhook returns 404 Agent is absent, deleted, or belongs to another platform Callback path and platform check
Webhook returns 503 Agent is not running Lifecycle state and Agent pod
Webhook reaches the wrong port Service or proxy configuration is incomplete Named port, target port, and runtime webhook path
Messages appear without names Directory enrichment is absent or failed Platform client, IDs, timeout, and non-fatal fallback
Activity is missing Telemetry plugin does not recognize the platform Runtime payload, session keys, Ingest contract
Health reports unsupported platform Health server lacks the new platform branch AGENT_PLATFORM handling in both runtime health servers
Platform does not appear in hiring Platform union or wizard sequence is incomplete Platform choice, steps, state, and payload mapping
Existing deployment fails migration ck_agent_platform or string length was not updated safely Alembic migration and PostgreSQL upgrade test
Two Agents use one forbidden bot identity No database uniqueness invariant exists Token hash, partial unique index, and conflict translation

Completion checklist

Work through each card before opening the pull request.

  • Contract

  • Persistence and API

  • Migration

  • Lifecycle

  • Runtime and messaging safety

  • Webhook and transport

  • Activity and health

  • Web app

  • Documentation and verification

Source map

Concern Authoritative source
Platform enum and configuration models api/domains/agents/models.py
Agent lifecycle orchestration api/domains/agents/service.py
Platform configuration persistence api/domains/agents/repository.py
Agent HTTP routes api/domains/agents/routes.py
Platform-specific routes api/domains/agents/slack_routes.py, api/domains/agents/webhook_routes.py
Shared Kubernetes resources api/domains/agents/builders/common.py
Hermes builders api/domains/agents/builders/hermes.py
OpenClaw builders api/domains/agents/builders/openclaw.py
Runtime builder exports api/domains/agents/builders/__init__.py
Hermes startup and plugins api/domains/agents/scripts/hermes/
OpenClaw startup and plugins api/domains/agents/scripts/openclaw/
Hermes base image hermes-base/
OpenClaw base image openclaw-base/
Kubernetes client api/infrastructure/kubernetes/
Ingest normalization api/domains/ingest/
Conversation parsing and reads api/domains/conversations/
Tool-call persistence api/domains/tool_calls/
Provider-specific clients api/infrastructure/slack/, api/infrastructure/telegram/, api/infrastructure/discord/
Web app Agent contracts ui/src/features/agents/schemas.ts
Agent creation and updates ui/src/features/agents/hooks/
Hiring flow ui/src/features/agents/components/hire-dialog.tsx, hire-dialog-steps.tsx
Channel configuration ui/src/features/agents/components/agent-channel-settings.tsx
Credential configuration ui/src/features/agents/components/agent-keys-settings.tsx
Agent product contract docs/features/agents.md
Runtime contract docs/architecture/runtime-and-deployment.md
Activity and telemetry contract docs/features/activity-and-ingest.md
Verification rules docs/guidelines/testing.md
Documentation