Agent API Routes#
Dify's console agent API routes live under /console/api/agent (singular) and are split across two controller files in api/controllers/console/agent/:
roster.pyβ CRUD and lifecycle operations for roster agents (list, create, get, update, delete, publish, draft management, API keys, logs, statistics, versions)composer.pyβ Agent composition endpoints used by both standalone agents (/agent/<agent_id>/composer) and inline workflow-node agents (/apps/<app_id>/workflows/draft/nodes/<node_id>/agent-composer)
Agent app operations (access, features, sandbox) are handled through application services in api/extensions/application_services/agent.py and persistence in api/repositories/app/agent_app_repository.py. Controllers resolve agent IDs to runtime app models via AgentAppRepository.resolve_runtime_app_id() or call service methods that internally delegate to AgentRosterService.
A thin helper module, app_helpers.py, provides resolve_agent_runtime_app_model() and related convenience functions for direct AgentRosterService queries.
v1.16.0 Plural β Singular Unification (PR #37465)#
PR #37465 (merged ~June 2026) executed a hard rename of every agent route from the old plural namespace to singular:
| Old (plural) | New (singular) |
|---|---|
GET/POST /console/api/agents | GET/POST /console/api/agent |
/agents/<agent_id>/versions | /agent/<agent_id>/versions |
/agents/invite-options | /agent/invite-options |
/apps/<app_id>/agent-composer | /agent/<agent_id>/composer |
/apps/<app_id>/agent-composer/validate | /agent/<agent_id>/composer/validate |
/apps/<app_id>/agent-composer/candidates | /agent/<agent_id>/composer/candidates |
The refactor also changed the primary addressing key: instead of the internal app_id, callers now use the public agent_id. Internal resolution still maps through AgentRosterService.get_agent_app_model() .
Known Bugs from the Refactor#
404 on Agent Creation in Dev Mode (Issue #38646)#
After the unification, the frontend in dev mode was still calling POST /console/api/agents (plural). The backend no longer registered that path, so every agent creation attempt returned 404 . Fix: update frontend calls to POST /console/api/agent (singular).
500 on Agents Configure Page β Custom API Tool Providers (Issue #39169)#
The Agents configure page's credential schema fetch unconditionally used the builtin provider URL template (/tool-provider/builtin/<provider>/credential/...) regardless of providerType. For custom API tools whose tool.id is a UUID (not a plugin identifier), the plugin daemon received a request for langgenius/<UUID> and raised PluginNotFoundError, surfacing as a 500 . Fix (PR #39206): gate both useGetApi and CredentialStatus on providerType !== 'builtin' to short-circuit non-builtin providers before making the request .
Current Route Map#
Core Agent Routes (roster.py)#
Key endpoints defined in roster.py:
| Method | Path | Purpose |
|---|---|---|
GET/POST | /agent | List / create agent apps |
GET/PUT/DELETE | /agent/<agent_id> | Get / update / delete agent |
POST | /agent/<agent_id>/publish | Publish draft (see Publish Response) |
GET/PUT/DELETE | /agent/<agent_id>/build-draft | Draft management |
POST | /agent/<agent_id>/build-draft/checkout | Checkout build draft |
POST | /agent/<agent_id>/build-draft/apply | Apply build draft |
POST | /agent/<agent_id>/copy | Copy agent |
GET/POST | /agent/<agent_id>/api-keys | Manage service API keys |
GET | /agent/<agent_id>/logs | Conversation logs |
GET | /agent/<agent_id>/statistics/summary | Usage statistics |
GET | /agent/<agent_id>/versions | Version history |
POST | /agent/<agent_id>/versions/<version_id>/restore | Restore version |
Text-to-Speech Routes (app_helpers.py / audio.py)#
Agent-specific text-to-speech endpoints added in PR #42575, defined in api/controllers/console/app/audio.py and using resolve_existing_agent_runtime_app_model() from app_helpers.py:
| Method | Path | Purpose | RBAC |
|---|---|---|---|
POST | /agent/<agent_id>/text-to-audio | Generate audio from text using agent's configured TTS settings | AGENT_TEST_AND_RUN |
GET | /agent/<agent_id>/text-to-audio/voices | Retrieve available TTS voices for the agent (workspace default model) | AGENT_PREVIEW |
These routes mirror the existing Chatflow/App endpoints (/apps/<app_id>/text-to-audio and /apps/<app_id>/text-to-audio/voices) but resolve the agent's runtime app model via AgentRosterService.get_existing_agent_runtime_app_model() instead of requiring a direct app ID. Generated TypeScript contracts live in packages/contracts/generated/api/console/agent/.
Agent V2 Backend Plumbing (PR #38162)#
PR #38162 synced Agent V2 daily changes, wiring new services and controllers:
- New services:
api/services/agent_config_service.py,api/services/agent_tool_inner_service.py,api/services/agent/config_skill_normalize_service.py - Inner API controllers:
api/controllers/inner_api/agent/tools.py,api/controllers/inner_api/plugin/agent_config.py - Inspector endpoint:
api/controllers/console/app/agent_config_inspector.pyfor inspecting agent configuration - Config layer rename: The "drive" concept was renamed to "config" throughout the runtime stack β
DifyDriveLayerConfigβDifyConfigLayerConfig;build_drive_layer_config()βbuild_config_layer_config()
The Agent V2 runtime node itself lives in api/core/workflow/nodes/agent_v2/ and is separate from the console route layer .
API Contract Schemas (PR #37210)#
PR #37210 tightened OpenAPI contract schemas for Agent V2:
- Replaced
dict[str, Any]fields inapi/models/agent_config_entities.pywith typed models (AgentPermissionConfig,AgentCliToolConfig, etc.) - Added
json_schema_extra={"x-dify-opaque": True}to fields that remain intentionally loose at runtime - Removed "inaccurate" deprecation markers from generated
packages/contracts/generated/api/console/agents/orpc.gen.tsto unblock client migration
Publish Response Schema#
The POST /agent/<agent_id>/publish endpoint returns an AgentPublishResponse containing:
| Field | Type | Required | Description |
|---|---|---|---|
result | string | Yes | Publication status (always "success" on successful response) |
publication_kind | "first" | "update" | Yes | Classifies whether the agent had a publish-visible active snapshot before this publication: "first" if none existed, otherwise "update". Not a historical first-publish indicator; does not depend on draft edits, Web App/API enablement state, or the requesting user's access permissions. |
active_config_snapshot_id | string | Yes | UUID of the created configuration snapshot |
active_config_snapshot | AgentConfigSnapshotSummaryResponse | null | No | Summary of the active snapshot (includes id, version, created_at, created_by) |
draft | AgentConfigDraftSummaryResponse | null | No | Draft metadata post-publication (typically present if a build draft exists) |
The frontend uses publication_kind to display distinct first-publication vs. update confirmation messages.