Extend Kestra with the API
Extend Kestra by using the API.
Kestra is API-first, so it’s straightforward to connect external systems to your flows or call the platform directly. This guide focuses on the Kestra API itself and how you can extend or integrate Kestra from other services.
Using the API Reference
The docs include references for both the Open Source and Cloud & Enterprise APIs so you know exactly what endpoints are available. Opening the Open Source reference shows a structured layout that’s easy to scan:

Making Requests with Authentication
If you have Basic Auth enabled or you’re using the Enterprise Edition, authenticate each request. With cURL you can pass credentials via -u username:password. The example below uses the defaults from the Kestra Docker Compose:
curl -X POST -u '[email protected]:kestra' http://localhost:8080/api/v1/executions/company.team/hello_world
Enterprise users can generate API tokens and send them as Bearer headers:
curl -X POST http://localhost:8080/api/v1/executions/company.team/hello_world \
-H "Authorization: Bearer YOUR_API_TOKEN"
The remaining examples assume authentication is disabled.
Create a Flow
To create a flow via API, open the Flows section and look for the /api/v1/main/flows POST endpoint. It expects a YAML payload containing the flow definition.
Our body of Content-Type application/x-yaml will look like the example below:
id: created_by_api
namespace: company.team
tasks:
- id: hello
type: io.kestra.plugin.core.log.Log
message: Hello World! 🚀
Send the request with cURL:
curl -X POST http://localhost:8080/api/v1/main/flows -H "Content-Type:application/x-yaml" -d "id: created_by_api
namespace: company.team
tasks:
- id: hello
type: io.kestra.plugin.core.log.Log
message: Hello World! 🚀"
The response looks like this:
{
"id": "created_by_api",
"namespace": "company.team",
"revision": 1,
"disabled": false,
"deleted": false,
"tasks":
[
{
"id": "hello",
"type": "io.kestra.plugin.core.log.Log",
"message": "Hello World! \uD83D\uDE80",
},
],
"source": "id: created_by_api\nnamespace: company.team\n\ntasks:\n - id: hello\n type: io.kestra.plugin.core.log.Log\n message: Hello World! \uD83D\uDE80",
}
Execute a Flow
To execute a flow, provide the namespace and flow ID. The sample flow below (hello_world) lives in the company.team namespace and accepts a string input:
id: hello_world
namespace: company.team
inputs:
- id: greeting
type: STRING
defaults: hey
tasks:
- id: hello
type: io.kestra.plugin.core.log.Log
message: "{{ inputs.greeting }}"
Because the input has a default, we can call the POST endpoint /api/v1/main/executions/{namespace}/{id} without providing additional data:
curl -X POST \
http://localhost:8080/api/v1/main/executions/company.team/hello_world
To override inputs, send them as form data with -F:
curl -X POST \
http://localhost:8080/api/v1/main/executions/company.team/hello_world \
-F greeting="hey there"
The response includes execution metadata and a link to the UI:
{
"id": "MYkTmLrI36s10iVXHwRbR",
"namespace": "company.team",
"flowId": "hello_world",
"flowRevision": 10,
"inputs": {
"greeting": "hey"
},
"labels": [
{
"key": "system.correlationId",
"value": "MYkTmLrI36s10iVXHwRbR"
}
],
"state": {
"current": "CREATED",
"histories": [
{
"state": "CREATED",
"date": "2024-11-21T16:31:27.943162175Z"
}
],
"duration": 0.044177500,
"startDate": "2024-11-21T16:31:27.943162175Z"
},
"originalId": "MYkTmLrI36s10iVXHwRbR",
"deleted": false,
"metadata": {
"attemptNumber": 1,
"originalCreatedDate": "2024-11-21T16:31:27.943194342Z"
},
"url": "http://localhost:8080//ui/executions/company.team/hello_world/MYkTmLrI36s10iVXHwRbR"
}
See the Executions documentation for additional examples.
Get Information from an Execution
The execution response returns the execution ID, which you can use to fetch additional details once the run completes. Using MYkTmLrI36s10iVXHwRbR from the earlier example, call the GET endpoint /api/v1/main/executions/{executionId}:
curl -X GET http://localhost:8080/api/v1/main/executions/MYkTmLrI36s10iVXHwRbR
The response includes state transitions, durations, and outputs:
Modify the flow to emit an output:
id: hello_world
namespace: company.team
tasks:
- id: hello
type: io.kestra.plugin.core.debug.Return
format: "This is an output"
Fetching execution 59uQXHbkMy5YwHEDom72Xv now shows the output payload:
Accessing the KV Store
Kestra’s KV Store keeps flows stateful. You can create, update, and delete entries via the API—either from code running inside a flow or from external systems.
Add a key/value pair with the PUT endpoint /api/v1/main/namespaces/{namespace}/kv/{key}. The example below writes "Hello, World" to my_key in the company.team namespace:
curl -X PUT -H "Content-Type: application/json" http://localhost:8080/api/v1/main/namespaces/company.team/kv/my_key -d '"Hello, World"'
Verify in Kestra that the entry exists:

Update the value by sending a different body, for example "This is a modified value":
curl -X PUT -H "Content-Type: application/json" http://localhost:8080/api/v1/main/namespaces/company.team/kv/my_key -d '"This is a modified value"'
Kestra shows the key as updated:

Opening the entry reveals the new value.

Fetch the value with the GET endpoint /api/v1/main/namespaces/{namespace}/kv/{key}:
curl -X GET http://localhost:8080/api/v1/main/namespaces/company.team/kv/my_key
The response contains the type and value:
{
"type": "STRING",
"value": "This is a modified value"
}
See the KV Store documentation for more operations.
Get and Upload Namespaces Files
Beyond flows, you can manage namespace files via the API.
Use the GET endpoint /api/v1/main/namespaces/{namespace}/files/directory to list files in a namespace:

For company.team:
curl -X GET http://localhost:8080/api/v1/main/namespaces/company.team/files/directory
The response is an array of file metadata:
[
{
"type": "File",
"size": 13,
"fileName": "example.txt",
"lastModifiedTime": 1731430406183,
"creationTime": 1731430400773
},
{
"type": "File",
"size": 27,
"fileName": "example.js",
"lastModifiedTime": 1731415024668,
"creationTime": 1730997234841
},
{
"type": "File",
"size": 19,
"fileName": "example.sh",
"lastModifiedTime": 1731415024667,
"creationTime": 1730997234839
},
{
"type": "File",
"size": 171,
"fileName": "example.ion",
"lastModifiedTime": 1731430044778,
"creationTime": 1731430012804
},
{
"type": "File",
"size": 21,
"fileName": "example.py",
"lastModifiedTime": 1731415024667,
"creationTime": 1729781670534
}
]
Use the GET endpoint /api/v1/main/namespaces/{namespace}/files to fetch file contents:
Example request for example.txt:
curl -X GET 'http://localhost:8080/api/v1/main/namespaces/company.team/files?path=example.txt'
which returns:
Hello, World!
Upload files using the POST endpoint /api/v1/main/namespaces/{namespace}/files. The example below uploads api_example.py with the following content:
import requests
r = requests.get("https://kestra.io")
print({r.status_code})
Run:
curl -X POST 'http://localhost:8080/api/v1/main/namespaces/company.team/files?path=api_example.py' -H "Content-Type:multipart/form-data" -F "fileContent=@api_example.py"
Note: Make sure fileContent has the correct path to your file.
After the upload, the file appears in the Namespace editor:

Was this page helpful?