Skip to main content
Version: 1.13.x

Manifest reference

This page is the reference for the extension.json manifest used by manifest-shipping extensions, including curated packages and graduated standalone packages.

The lfx-bundles metapackage uses manifest-less discovery through the lfx.bundles group and does not use extension.json.

The canonical JSON Schema lives at https://schemas.langflow.org/extension/v1.json and is generated from the Pydantic model at lfx.extension.manifest.ExtensionManifest.

To export the live schema, run:

lfx extension schema --output extension.schema.json

Use the $schema reference in your manifest so editors can autocomplete and validate inline:

{
"$schema": "https://schemas.langflow.org/extension/v1.json",
"id": "lfx-my-extension",
"version": "0.1.0",
"name": "My extension",
"lfx": { "compat": ["1"] },
"bundles": [{ "name": "my_bundle", "path": "components/my_bundle" }]
}

Top-level fields​

FieldTypeRequiredDescription
idstringyesGlobally-unique extension ID. Lowercase, hyphenated, starts with a letter, 2-64 chars. Convention: lfx-<name>.
versionstringyesSemVer 2.0.0 version string for this extension release.
namestringyesHuman-readable display name shown in the Langflow palette. 1-200 chars.
descriptionstring | nullnoOptional one-paragraph summary, max 2000 chars.
lfxobjectyesCompatibility declaration against the BUNDLE_API contract.
bundlesarraynoBundle list. v0 accepts at most one bundle; omit it for a provider-only extension.
providersarraynoModel providers contributed to the unified model-provider registry.
capabilitiesobjectnoOptional capability flags. Defaults to all-false.
integrationsarraynoBundle-owned integration capability manifests. Defaults to an empty list.
$schemastringnoOptional pointer to this JSON Schema; editors use it for autocomplete.

additionalProperties: false — any field not listed here is rejected with a typed error. Reserved names (services, routes, hooks, starterProjects, userConfig) are documented under Deferred fields and surface a more specific error code.

An extension must declare at least one entry in either bundles or providers. It can declare both when one package contributes components and model providers.

lfx: compatibility declaration​

"lfx": { "compat": ["1"] }
FieldTypeDescription
compatarray of stringsNon-empty list of BUNDLE_​API.md contract versions this extension supports. Each entry is a positive-integer string.

The runtime compares str(BUNDLE_API_VERSION) against this list. A mismatch fails install with version-constraint-unsatisfied. v0 accepts only "1"; lists like ["1", "2"] become meaningful when a future BUNDLE_API revision ships.

bundles​

"bundles": [
{ "name": "my_bundle", "path": "components/my_bundle" }
]
FieldTypeDescription
namestringBundle name; addressable as ext:<bundle>:<Class>@<slot>. Lowercase snake_case, starts with a letter, 2-64 chars.
pathstringPath to the bundle directory, relative to the manifest. Must not start with / or contain ...

v0 enforces maxItems: 1; multi-bundle extensions are rejected with multi-bundle-unsupported and ship in a later epic. The list can be empty when the extension declares at least one model provider.

providers​

A provider-only extension can add a provider without adding a component bundle:

{
"$schema": "https://schemas.langflow.org/extension/v1.json",
"id": "lfx-acme-models",
"version": "0.1.0",
"name": "Acme Models",
"lfx": { "compat": ["1"] },
"providers": [
{
"name": "Acme",
"provider_id": "acme.models",
"display_name": "Acme Models",
"aliases": ["Acme Legacy"],
"metadata": {
"icon": "Bot",
"variables": [],
"mapping": {
"model_class": "ChatAcme",
"model_param": "model"
}
},
"api_key_required": false,
"model_class": {
"module": "langchain_acme",
"attr": "ChatAcme",
"install_hint": "langchain-acme"
},
"catalog_loader": "lfx_acme.catalog:load_models"
}
]
}
FieldTypeRequiredDescription
namestringyesCanonical provider name used by existing saved flows and provider selectors.
provider_​idstringnoStable lowercase machine identity used by policy and extension compatibility. Must match ^[a-z0-9][a-z0-9._​-]*$. Legacy manifests can omit it and receive a deterministic ID derived from name.
display_​namestringnoUser-facing label. It can change without changing provider_​id or saved-flow identity.
aliasesarray of stringsnoUnique legacy names accepted when resolving the provider's stable identity.
metadataobjectyesProvider icon, variables, API documentation URL, and mapping. mapping.model_​class must be non-empty.
model_​classobjectnoLazy LLM class import with module, attr, and optional install_​hint. Omit it when reusing an already-registered class.
embeddingobjectnoOptional embedding class import and parameter mapping. Includes class_​name, module, attr, param_​mapping_​key, param_​mapping, and optional install_​hint.
api_​key_​requiredboolnoWhether unified runtime helpers reject missing API-key credentials. Defaults to true.
liveboolnoEnables always-on live model discovery. Mutually exclusive with conditional_​live.
conditional_​liveboolnoEnables live discovery only when the provider has a custom endpoint configured. Mutually exclusive with live.
live_​discoverystringnoLazy module:callable that receives (user_​id, model_​type) and returns live model rows.
validatorstringnoLazy module:callable that validates (provider, variables, model) and raises on invalid credentials.
catalog_​loaderstringnoLazy module:callable returning the provider's static model metadata rows.

catalog_loader must return a flat list[dict]. Every row requires a non-empty name; model_type can be llm or embeddings and defaults to llm. Langflow overwrites any supplied provider value with the descriptor's canonical name, supplies the provider icon when omitted, and rejects duplicate (model_type, name) identities. Deployments that require an extension catalog can call validate_registered_provider_catalogs() or get_registry_snapshot(validate_catalogs=True) during readiness.

Providers that use ambient authentication or need no credentials should set api_key_required to false. They remain eligible for unified model options when metadata.variables is empty or contains only optional configuration fields.

Built-in providers take precedence over extension declarations. A provider-name collision is skipped without preventing other providers or components from loading. Stable-ID or alias collisions are reported as provider-invalid.

capabilities​

Optional. Defaults to { "requiresCredentials": false }.

FieldTypeDescription
requiresCredentialsboolIf true, the loader records that components in this bundle expect credential variables to be configured before use.

Additional capability keys are rejected with extra="forbid" so a misspelled key surfaces immediately rather than silently turning a feature off.

integrations​

Integration bundles publish provider capabilities in a separate, versioned JSON file owned by the bundle. The extension manifest points to that file:

"integrations": [
{
"provider_id": "google",
"bundle": "google",
"path": "capabilities.v1.json"
}
]
FieldTypeRequiredDescription
provider_​idstringyesStable lowercase provider key used by discovery, policy, and connection resolution.
bundlestringyesName of the bundle that owns the capability manifest. It must match an entry in bundles[].
pathstringyesJSON file path relative to that bundle's directory. Absolute paths, .., and symlink escapes are rejected.

The referenced file uses this shape:

{
"schema_version": 1,
"provider_id": "google",
"display_name": "Google Workspace",
"auth_profiles": [
{
"id": "user",
"kind": "oauth2_authorization_code",
"identity": "user_delegated",
"supports_pkce": true,
"supports_refresh": true
}
],
"capabilities": [
{
"id": "google.drive.files.search",
"display_name": "Drive: Search Files",
"auth_profile_id": "user",
"identity": "user_delegated",
"required_scopes": ["drive.file"],
"policy_keys": ["integrations.google.drive.search"],
"substrate": "sdk",
"maturity": "ga",
"deployment_contexts": ["hosted", "self_managed", "desktop", "headless"],
"risk": "read",
"component_ref": "GoogleDriveSearchComponent"
}
]
}

schema_version is currently 1. Authentication modes are declared by auth_profiles[].kind; actions reference a profile through auth_profile_id. Each action declares scopes, policy keys, execution substrate (sdk, rest, or mcp), maturity, supported deployment contexts, risk, and at least one execution target (component_ref or mcp_tool). Unknown fields are rejected throughout.

Every entry in policy_keys must use the governance grammar integrations.<provider_id>.<action>, in lowercase, where <provider_id> is the manifest's own provider_id. Operators block actions by these keys through the policy bundle, so a key outside that namespace would make the action ungovernable and is rejected at load time. Blocking any one key of an action blocks the whole action; a component stays visible while at least one of its actions remains allowed.

During loading, each valid file is exposed as a LoadedIntegration in LoadResult.integrations and retained in BundleRecord.integrations. Discovery and policy code can consume the parsed capability_manifest from the load result or call BundleRegistry.list_integrations() for the process-wide snapshot. Extensions that omit integrations continue to load with an empty list.

Provider IDs are owned identities in the installed registry: one bundle owns each provider ID. Capability IDs and policy keys cannot be claimed by another provider or bundle. A provider may group several of its own actions under one policy key. This lets discovery, connection resolution, and policy use the provider key without depending on an extension-qualified identity. Alternative SDK, Composio, and MCP bundles must use distinct provider and policy namespaces when installed together.

The registry rejects a conflicting installation with integration-identity-conflict, naming both extensions and the conflicting key. Startup skips the rejected bundle's components and integration metadata and emits the error; an already installed owner remains active. Hot reload checks ownership before swapping live modules or metadata, so a rejected reload preserves the previous bundle. Reloading the owning bundle is allowed, and removing it releases its keys.

After adding an integration manifest on a release branch, synchronize bundle dependency floors with the repository script instead of editing pyproject.toml by hand:

uv run python scripts/ci/sync_bundle_lfx_pin.py 1.13.0

The command is idempotent and applies the canonical lfx>=1.13.0.dev0,<2.0.0 floor to bundles that need an update.

The Integration manifests - lfx dependency floor CI check enforces this pairing for every bundle that declares integrations, including JSON and TOML manifests. It reads [project].dependencies and requires an unconditional lfx dependency whose lower bound is at least 1.13.0.dev0; declaring lfx.compat = ["1"] alone is insufficient because that names the bundle API contract, not the lfx release. Bundles without integrations are exempt. The guard applies when future provider bundles add their manifests, even if the synchronization command produces no changes today.

The validator and loader check the feature's runtime floor before parsing the strict manifest schema and report lfx-version-too-old with the installed and required versions. Earlier lfx builds cannot inherit this diagnostic; dependency resolution and the CI guard prevent installing these manifests with older releases. Unreleased development builds that used the earlier inline integration shape must be updated to a build supporting bundle-owned references; those shapes were never a released compatibility contract.

Deferred fields​

The schema strips these names from the published properties map but reserves them via x-deferred-fields, so a manifest that sets one gets a specific error code instead of the generic "additional property" message.

Reserved keyReplacement error codeFuture epic
servicesfield-deferred-in-this-milestoneB2 — non-component primitives
routesfield-deferred-in-this-milestoneB2 — non-component primitives
hooksfield-deferred-in-this-milestoneB2 — non-component primitives
starterProjectsfield-deferred-in-this-milestonelater milestone
userConfigfield-deferred-in-this-milestonelater milestone

A manifest that sets any of these to a non-null value is rejected at validate / load time. Setting them to null is allowed (the loader treats null and absent identically).

Pyproject alternative​

If you'd rather not ship a separate extension.json next to pyproject.toml, declare the same fields under [tool.langflow.extension]:

[tool.langflow.extension]
id = "lfx-my-extension"
version = "0.1.0"
name = "My extension"

[tool.langflow.extension.lfx]
compat = ["1"]

[[tool.langflow.extension.bundles]]
name = "my_bundle"
path = "components/my_bundle"

The loader prefers an extension.json when both exist. The Pydantic validator is the same so error codes and field semantics are identical.

Error codes raised against this manifest​

The loader and validator both emit typed errors keyed by the manifest field that triggered them. The full reference is on the Extension error codes page; the codes most relevant when authoring a manifest are:

CodeCause
manifest-invalidThe extension manifest or a referenced integration capability manifest fails schema validation.
integration-identity-conflictTwo registered integrations claim the same provider ID, capability ID, or policy key.
lfx-version-too-oldThe running lfx version predates bundle-owned integration references; upgrade lfx.
manifest-not-foundNo extension.json and no [tool.langflow.extension] section at the extension root.
version-constraint-unsatisfiedlfx.compat does not include this Langflow's BUNDLE_​API_​VERSION.
field-deferred-in-this-milestoneA reserved field was set to a non-null value.
multi-bundle-deferred-in-this-milestonebundles has more than one entry.
path-escapeA bundles[].path resolves outside the manifest root (typically a symlink).
bundle-path-not-foundbundles[].path does not exist or is not a directory.
provider-invalidA provider descriptor or one of its lazy import paths is malformed. The invalid provider is skipped.
provider-skippedA provider name collides with a built-in or previously loaded provider. The existing provider wins.

Run lfx extension validate <path> to see every error as a structured object with code, message, location, hint, and ref_url.

See also​

Was this page helpful?

Support
Search