For the complete documentation index, see llms.txt. For a full content snapshot, see llms-full.txt. Append .md to any kestra.io/docs/* URL for plain Markdown.

Available on:Enterprise EditionCloud

This reference describes the assets property you add to a task, the fields of an asset, and the ways a task can register assets.

For what assets are and when to use them, see Assets. For triggers, tasks, and functions that act on assets, see the asset tasks and triggers reference.

The assets property

Every task accepts an assets property:

PropertyTypeDefaultDescription
inputsList of asset references—Assets the task reads. Each entry needs an id. Add type to type an asset that doesn’t exist yet.
outputsList of assets—Assets the task creates or updates. Each entry needs an id and a type.
enableAutoBooleanfalseAccept assets emitted by the plugin or by script log lines. See Automatic emission.
assetFailureBehaviorWARN, FAIL, or IGNOREWARNWhat happens to the task state when an asset can’t be written. See Failure behavior.

Asset declarations are an Enterprise Edition feature. In the open-source edition, a flow with an assets property fails validation with the message assets are only available in Enterprise Edition.

Declare inputs and outputs

This flow loads raw orders into a reporting table. The task reads raw.orders and writes analytics.orders_daily:

id: build_orders_daily
namespace: company.data
tasks:
- id: build_table
type: io.kestra.plugin.jdbc.postgresql.Queries
url: jdbc:postgresql://postgres:5432/analytics
username: "{{ secret('POSTGRES_USER') }}"
password: "{{ secret('POSTGRES_PASSWORD') }}"
sql: |
CREATE TABLE IF NOT EXISTS analytics.orders_daily AS
SELECT order_date, count(*) AS orders, sum(amount) AS revenue
FROM raw.orders
GROUP BY order_date;
assets:
inputs:
- id: raw.orders
outputs:
- id: analytics.orders_daily
namespace: company.data
type: io.kestra.plugin.ee.assets.Table
displayName: Daily orders
description: One row per day with order count and revenue.
metadata:
system: postgres
database: analytics
schema: analytics
name: orders_daily
owner: data-team

When build_table finishes:

  • If the task succeeded, Kestra creates analytics.orders_daily, or updates it if it exists.
  • If raw.orders doesn’t exist, Kestra creates it as an io.kestra.core.models.assets.External placeholder in company.data.
  • Kestra records lineage from raw.orders to analytics.orders_daily, from raw.orders to the flow, and from the flow to analytics.orders_daily.
  • Kestra records a usage entry for both assets on this execution.

Rendering and task outputs

Kestra renders the assets block after the task finishes, so expressions in it can read the task’s own outputs with outputs.<task_id>.<output>. Use this when the resource name is only known at runtime:

- id: download
type: io.kestra.plugin.core.http.Download
uri: https://example.com/exports/latest.csv
assets:
outputs:
- id: "exports.{{ execution.startDate | date('yyyyMMdd') }}"
type: io.kestra.plugin.ee.assets.File
metadata:
path: "{{ outputs.download.uri }}"

If an expression in inputs can’t be rendered, Kestra still processes outputs, and the reverse.

What happens when a task fails

When a task ends in FAILED, Kestra records lineage and usage for its inputs and outputs, and still creates placeholders for unknown inputs, but doesn’t create or update its output assets. A failed run never overwrites an asset’s metadata or updated timestamp.

A task with allowFailure: true that fails ends in WARNING instead, so Kestra writes its output assets.

Asset fields

An asset in outputs has these fields:

FieldRequiredDescription
idYesUnique within the tenant. Starts with a letter or digit, then letters, digits, ., _, :, or -. Up to 150 characters.
typeYesA built-in type or any custom string.
namespaceNoNamespace the asset belongs to, used for filtering and access control. Lowercase letters, digits, ., _, and -. See Namespace and Global assets.
displayNameNoHuman-readable name shown in the UI
descriptionNoMarkdown description
metadataNoMap of custom key-value pairs

Any other key you add to an output entry is stored in metadata. For example, system: postgres at the top level of the entry and metadata.system: postgres produce the same asset.

Identity

Kestra identifies an asset by its id within a tenant. The namespace and the type aren’t part of the identity. Two declarations with the same id always refer to the same asset, even in different namespaces or flows.

Update rules

When a task writes an asset that already exists, Kestra merges the new declaration into the stored asset:

FieldRule
typeThe declared type replaces the stored one. An External placeholder never replaces a real type.
namespaceA declared namespace replaces the stored one. An omitted namespace keeps the stored one.
displayName, descriptionA declared value replaces the stored one. An omitted value keeps the stored one.
metadataMerged key by key. Declared keys replace stored keys. Keys you don’t declare are kept. A flow can’t remove a key; to remove one, edit the asset in the UI or through the API.

Each write updates the asset’s updated timestamp and emits an UPDATED event, even when no field changed.

Namespace and Global assets

The namespace controls who can see and change an asset. Users with namespace-scoped ASSET permissions only see assets in the namespaces they have access to.

An asset without a namespace is a Global asset. Namespace-level access control doesn’t apply to Global assets: any user with an ASSET permission in the tenant can see them. Use Global assets for resources that every namespace should be able to reference.

How the namespace is set depends on where the asset comes from:

SourceNamespace when you omit it
New asset in outputsNone. The asset is created as a Global asset.
Existing asset in outputsThe stored namespace is kept.
Placeholder created from inputsThe namespace of the flow that references it.
New asset emitted by a pluginNone. Plugins emit assets without a namespace, so they’re created as Global assets.

A flow can’t send an explicit empty namespace. Omitting namespace on a new output is the only way to create a Global asset from a flow.

Cross-namespace writes

When a flow writes an asset in another namespace, Kestra checks that the flow’s namespace is allowed to access the target namespace. The check applies to the asset’s current namespace and, when it changes, to the new one. It also applies when a typed input retypes an existing asset. If the check fails, the write is rejected and the failure behavior applies.

Namespaces allow all others by default, so the check only blocks a write when the target namespace restricts its allowed namespaces. To allow the write, add the flow’s namespace to that list. Global assets aren’t checked.

Typed inputs

An input entry can include a type. If the asset doesn’t exist yet, Kestra creates it with that type instead of External. If it exists with another type, Kestra changes its type, subject to locks and the cross-namespace check.

assets:
inputs:
- id: warehouse.raw.customers
type: io.kestra.plugin.ee.assets.Table

Built-in asset types

TypeCommon metadata keys
io.kestra.plugin.ee.assets.Tablesystem, database, schema, name
io.kestra.plugin.ee.assets.Datasetsystem, location, format
io.kestra.plugin.ee.assets.Filesystem, path
io.kestra.plugin.ee.assets.VMprovider, region, state
io.kestra.core.models.assets.ExternalNone. Kestra assigns this type to placeholders created from untyped inputs.

The metadata keys are conventions. Kestra stores them in metadata like any other key, and the dependencies graph uses system and schema for grouping.

You can also use any custom string as a type, such as AWS_BUCKET or company.infra.LoadBalancer. Custom types behave like built-in types, except that no plugin emits them unless the plugin defines them. Some plugins define their own types, for example io.kestra.plugin.ee.assets.HelmRelease.

Automatic emission

Some plugins detect the resources a task reads and writes and emit assets themselves. Script tasks can do the same by printing a special log line. In both cases, set assets.enableAuto: true on the task. Without it, Kestra discards emitted assets.

Plugins emit assets without a namespace, so a new emitted asset is a Global asset. To move it into a namespace, declare the same ID in a task’s outputs with a namespace. Later emissions keep that namespace.

Emitted assets and declared assets combine. You can turn on enableAuto and also declare inputs or outputs, for example to connect a plugin’s outputs to upstream assets the plugin can’t see.

Plugins that emit assets

Plugin and tasksEmitsAsset IDs
JDBC Query and Queries (all databases)One Table output per CREATE TABLE statement. Query emits at most one. No inputs.<database>.<table>. The schema isn’t part of the ID.
dbt DbtCLI, dbt Cloud CheckStatus, and dbt Cloud TriggerRun when wait is trueOne Table output per model, seed, and snapshot, with its parents and sources as Table inputs. Adds test results as dbtTestStatus (pass, fail, or warn), dbtTestsTotal, and dbtTestsFailed when parseRunResults is true.<database>.<schema>.<name>
Fivetran connectors.Sync and connectors.StatusOne Dataset output per connector schema and one Table output per enabled tableTables: <database>.<schema>.<table>
Google Cloud BigQuery RunTransferConfigOne Table output for the destination table, or one Dataset output when the table isn’t fixed. Emitted only after a successful run.<project>.<dataset>.<table> or <project>.<dataset>
Hex projects.RunOne Dataset output for the project, with system: hex. No inputs.The projectId
Qlik Cloud apps.ReloadOne io.kestra.plugin.qlikcloud.assets.App output for the reloaded app. Set the task’s emitAssets to false to turn this off.The app ID
Ansible AnsibleCLIOne VM input per host in an INI inventory file named exactly inventory.ini at the working directory rootThe host name
Helm Upgrade, Rollback, Status, and UninstallOne io.kestra.plugin.ee.assets.HelmRelease output, with the chart and valuesFrom files as inputs, plus one io.kestra.plugin.ee.assets.KubernetesResource per managed resource. Uninstall deletes these assets.Built from the cluster, Kubernetes namespace, and release name

dbt and Fivetran use the same <database>.<schema>.<table> ID format, so a Fivetran table and the dbt source that reads it join into one lineage chain. JDBC IDs leave out the schema, so they don’t join with dbt or Fivetran IDs. To connect them, declare the matching IDs yourself.

Plugins that only emit outputs, such as Hex and Qlik Cloud, can’t see their upstream tables. Declare assets.inputs on those tasks with the IDs the upstream plugins emit:

- id: reload_dashboard
type: io.kestra.plugin.qlikcloud.apps.Reload
tenantUrl: https://mytenant.eu.qlikcloud.com
apiKey: "{{ secret('QLIK_API_KEY') }}"
appId: 60f2e3b1a1b2c3d4e5f6a7b8
assets:
enableAuto: true
inputs:
- id: analytics.marts.fct_orders
type: io.kestra.plugin.ee.assets.Table

For each plugin’s properties, see its page in the plugin catalog. For a complete dbt example, see Track lineage across a dbt pipeline.

Emit assets from a script

A script task can emit assets by printing a line in this format to standard output:

::{"assets": {"inputs": [{"id": "<input id>"}], "outputs": [{"id": "<output id>", "type": "<type>"}]}}::

The task must have assets.enableAuto: true. Each output entry accepts the same fields as a declared output.

This Shell task writes a CSV export and emits it as an asset that reads from crm.contacts:

id: export_contacts
namespace: company.data
tasks:
- id: export
type: io.kestra.plugin.scripts.shell.Commands
taskRunner:
type: io.kestra.plugin.core.runner.Process
outputFiles:
- contacts.csv
commands:
- printf 'id,email\n1,ada@example.com\n' > contacts.csv
- |
echo '::{"assets": {"inputs": [{"id": "crm.contacts"}], "outputs": [{"id": "exports.contacts_csv", "type": "io.kestra.plugin.ee.assets.File", "metadata": {"path": "contacts.csv"}}]}}::'
assets:
enableAuto: true

In Python, the Kestra library sends the same structure with Kestra.assets(). Add kestra to the task’s dependencies:

- id: export_report
type: io.kestra.plugin.scripts.python.Script
dependencies:
- kestra
assets:
enableAuto: true
script: |
from kestra import Kestra
Kestra.assets({
"outputs": [
{
"id": "reports.weekly_revenue",
"type": "io.kestra.plugin.ee.assets.File",
"metadata": {"path": "s3://company-reports/weekly_revenue.csv"},
}
]
})

Failure behavior

Writing an asset can fail after the task itself succeeds, for example when the asset is locked, when the cross-namespace check rejects the write, or when an expression in the assets block can’t be rendered. assetFailureBehavior decides what happens to the task state:

ValueEffect
WARNA successful task ends in WARNING. This is the default.
FAILThe task ends in FAILED.
IGNOREThe task state doesn’t change. The failure only appears in the task logs.

Kestra tries to write every declared asset before it applies the failure behavior, so one failed asset doesn’t stop the others. The task’s allowFailure and allowWarning properties still apply after the escalation. A task that already failed keeps its failed state.

assets:
assetFailureBehavior: FAIL
outputs:
- id: finance.ledger
type: io.kestra.plugin.ee.assets.Table

An invalid declaration is different. If a rendered asset breaks a field rule, for example an id with a space, the task fails regardless of assetFailureBehavior.

The value accepts expressions.

Next steps

Was this page helpful?