curl --request PUT \
--url https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Email me new signups",
"description": "Sends an email whenever a User record is created.",
"definition": {
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
},
"trigger": {
"config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
}
},
"change_summary": "Send to the ops alias instead"
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}"
payload = {
"name": "Email me new signups",
"description": "Sends an email whenever a User record is created.",
"definition": {
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
},
"trigger": { "config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
} },
"change_summary": "Send to the ops alias instead"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Email me new signups',
description: 'Sends an email whenever a User record is created.',
definition: {do: [], document: {dsl: '1.0.0', name: 'notify', version: '1.0.0'}},
trigger: {
config: {cron_expression: '0 9 * * *', timezone: 'UTC', trigger_type: 'scheduled'}
},
change_summary: 'Send to the ops alias instead'
})
};
fetch('https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Email me new signups',
'description' => 'Sends an email whenever a User record is created.',
'definition' => [
'do' => [
],
'document' => [
'dsl' => '1.0.0',
'name' => 'notify',
'version' => '1.0.0'
]
],
'trigger' => [
'config' => [
'cron_expression' => '0 9 * * *',
'timezone' => 'UTC',
'trigger_type' => 'scheduled'
]
],
'change_summary' => 'Send to the ops alias instead'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}"
payload := strings.NewReader("{\n \"name\": \"Email me new signups\",\n \"description\": \"Sends an email whenever a User record is created.\",\n \"definition\": {\n \"do\": [],\n \"document\": {\n \"dsl\": \"1.0.0\",\n \"name\": \"notify\",\n \"version\": \"1.0.0\"\n }\n },\n \"trigger\": {\n \"config\": {\n \"cron_expression\": \"0 9 * * *\",\n \"timezone\": \"UTC\",\n \"trigger_type\": \"scheduled\"\n }\n },\n \"change_summary\": \"Send to the ops alias instead\"\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.put("https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Email me new signups\",\n \"description\": \"Sends an email whenever a User record is created.\",\n \"definition\": {\n \"do\": [],\n \"document\": {\n \"dsl\": \"1.0.0\",\n \"name\": \"notify\",\n \"version\": \"1.0.0\"\n }\n },\n \"trigger\": {\n \"config\": {\n \"cron_expression\": \"0 9 * * *\",\n \"timezone\": \"UTC\",\n \"trigger_type\": \"scheduled\"\n }\n },\n \"change_summary\": \"Send to the ops alias instead\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Email me new signups\",\n \"description\": \"Sends an email whenever a User record is created.\",\n \"definition\": {\n \"do\": [],\n \"document\": {\n \"dsl\": \"1.0.0\",\n \"name\": \"notify\",\n \"version\": \"1.0.0\"\n }\n },\n \"trigger\": {\n \"config\": {\n \"cron_expression\": \"0 9 * * *\",\n \"timezone\": \"UTC\",\n \"trigger_type\": \"scheduled\"\n }\n },\n \"change_summary\": \"Send to the ops alias instead\"\n}"
response = http.request(request)
puts response.read_body{
"id": "68b1c0d4e7b91d003c45a1f2",
"app_id": "6820f3a4e7b91d003c45a1f2",
"name": "Email me new signups",
"status": "active",
"social_post": {
"platform": "linkedin",
"title": "Meet our new app",
"body": "Our new app is ready to try.",
"hashtags": [
"launch"
]
},
"file_key": "email-me-new-signups",
"description": "Sends an email whenever a User record is created.",
"status_reason": "consecutive_failures",
"current_version_id": "9f2c1a7b3e5d84f60c1b2a9e7d4f8c3b6a5e2d1f0c9b8a7e6d5c4b3a2f1e0d9c",
"trigger": {
"config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
}
},
"app_type_context": {
"anchor_message_id": "68b1c0d4e7b91d003c45a1f2",
"conversation_id": "0195f2a1-4c3e-7b21-9f0d-2a5c8e1b4d77"
},
"last_run_at": "2026-08-25T09:12:44Z",
"last_run_status": "success",
"consecutive_failures": 0,
"total_runs": 48,
"successful_runs": 44,
"failed_runs": 3,
"created_date": "2026-07-02T11:04:00Z",
"updated_date": "2026-08-20T16:31:00Z",
"created_by": "you@example.com",
"definition": {
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
}
}Update workflow
Updates a workflow. Send only the fields you want to change. Pass change_summary to label what changed.
Anything you leave out keeps its stored value. Sending a definition that differs from the current one saves it as a new immutable version, and the workflow runs that version from then on. Earlier versions stay readable through List workflow versions.
Updating does not change whether the workflow is running.
This is only allowed from the app’s main branch.
curl --request PUT \
--url https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Email me new signups",
"description": "Sends an email whenever a User record is created.",
"definition": {
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
},
"trigger": {
"config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
}
},
"change_summary": "Send to the ops alias instead"
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}"
payload = {
"name": "Email me new signups",
"description": "Sends an email whenever a User record is created.",
"definition": {
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
},
"trigger": { "config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
} },
"change_summary": "Send to the ops alias instead"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Email me new signups',
description: 'Sends an email whenever a User record is created.',
definition: {do: [], document: {dsl: '1.0.0', name: 'notify', version: '1.0.0'}},
trigger: {
config: {cron_expression: '0 9 * * *', timezone: 'UTC', trigger_type: 'scheduled'}
},
change_summary: 'Send to the ops alias instead'
})
};
fetch('https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Email me new signups',
'description' => 'Sends an email whenever a User record is created.',
'definition' => [
'do' => [
],
'document' => [
'dsl' => '1.0.0',
'name' => 'notify',
'version' => '1.0.0'
]
],
'trigger' => [
'config' => [
'cron_expression' => '0 9 * * *',
'timezone' => 'UTC',
'trigger_type' => 'scheduled'
]
],
'change_summary' => 'Send to the ops alias instead'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}"
payload := strings.NewReader("{\n \"name\": \"Email me new signups\",\n \"description\": \"Sends an email whenever a User record is created.\",\n \"definition\": {\n \"do\": [],\n \"document\": {\n \"dsl\": \"1.0.0\",\n \"name\": \"notify\",\n \"version\": \"1.0.0\"\n }\n },\n \"trigger\": {\n \"config\": {\n \"cron_expression\": \"0 9 * * *\",\n \"timezone\": \"UTC\",\n \"trigger_type\": \"scheduled\"\n }\n },\n \"change_summary\": \"Send to the ops alias instead\"\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.put("https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Email me new signups\",\n \"description\": \"Sends an email whenever a User record is created.\",\n \"definition\": {\n \"do\": [],\n \"document\": {\n \"dsl\": \"1.0.0\",\n \"name\": \"notify\",\n \"version\": \"1.0.0\"\n }\n },\n \"trigger\": {\n \"config\": {\n \"cron_expression\": \"0 9 * * *\",\n \"timezone\": \"UTC\",\n \"trigger_type\": \"scheduled\"\n }\n },\n \"change_summary\": \"Send to the ops alias instead\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/workflows/{workflow_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Email me new signups\",\n \"description\": \"Sends an email whenever a User record is created.\",\n \"definition\": {\n \"do\": [],\n \"document\": {\n \"dsl\": \"1.0.0\",\n \"name\": \"notify\",\n \"version\": \"1.0.0\"\n }\n },\n \"trigger\": {\n \"config\": {\n \"cron_expression\": \"0 9 * * *\",\n \"timezone\": \"UTC\",\n \"trigger_type\": \"scheduled\"\n }\n },\n \"change_summary\": \"Send to the ops alias instead\"\n}"
response = http.request(request)
puts response.read_body{
"id": "68b1c0d4e7b91d003c45a1f2",
"app_id": "6820f3a4e7b91d003c45a1f2",
"name": "Email me new signups",
"status": "active",
"social_post": {
"platform": "linkedin",
"title": "Meet our new app",
"body": "Our new app is ready to try.",
"hashtags": [
"launch"
]
},
"file_key": "email-me-new-signups",
"description": "Sends an email whenever a User record is created.",
"status_reason": "consecutive_failures",
"current_version_id": "9f2c1a7b3e5d84f60c1b2a9e7d4f8c3b6a5e2d1f0c9b8a7e6d5c4b3a2f1e0d9c",
"trigger": {
"config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
}
},
"app_type_context": {
"anchor_message_id": "68b1c0d4e7b91d003c45a1f2",
"conversation_id": "0195f2a1-4c3e-7b21-9f0d-2a5c8e1b4d77"
},
"last_run_at": "2026-08-25T09:12:44Z",
"last_run_status": "success",
"consecutive_failures": 0,
"total_runs": 48,
"successful_runs": 44,
"failed_runs": 3,
"created_date": "2026-07-02T11:04:00Z",
"updated_date": "2026-08-20T16:31:00Z",
"created_by": "you@example.com",
"definition": {
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
}
}Authorizations
Personal access token, sent as Authorization: Bearer <token>.
Path Parameters
ID of the app whose workflows you want to work with.
ID of the workflow, as returned in id by List workflows.
Body
New name. Leave it out to keep the current one. Must be unique among the app's workflows that are not archived, so renaming to one another live workflow already uses is rejected.
"Email me new signups"
New description. Leave it out to keep the current one.
"Sends an email whenever a User record is created."
New definition. Leave it out to keep the current one. One that differs from the current definition is saved as a new immutable version, and the workflow runs it from then on. An invalid definition is rejected with the validation errors, and nothing is saved.
{
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
}
Note describing this change, kept in the version history. This is only recorded when definition actually changes, since that's what creates the version it's attached to.
"Send to the ops alias instead"
Response
The updated workflow.
ID of the workflow.
"68b1c0d4e7b91d003c45a1f2"
ID of the app the workflow belongs to.
"6820f3a4e7b91d003c45a1f2"
Name of the workflow.
"Email me new signups"
Whether the workflow runs: active, inactive, or archived. See Workflow status for what each means and how it changes.
"active"
Current post content for a social publishing workflow, or null when no linked post is available.
Show child attributes
Show child attributes
Name of the workflow's file in the app's code. This is null on workflows saved before files were kept.
"email-me-new-signups"
What the workflow is for.
"Sends an email whenever a User record is created."
Why Base44 changed the workflow's status on its own, as one of consecutive_failures, end_condition_reached, migration_activation_failed, or workflows_not_available. This is null when you changed the status yourself. See Workflow status for what each code means.
"consecutive_failures"
Version the workflow runs today, as the SHA-256 hash of that definition. This is null until a definition is saved.
"9f2c1a7b3e5d84f60c1b2a9e7d4f8c3b6a5e2d1f0c9b8a7e6d5c4b3a2f1e0d9c"
What starts the workflow. The trigger sits under config, keyed by trigger_type.
{
"config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
}
}
Caller-specific context captured when the workflow was created, such as the conversation that authored it. This API never sets it, so a workflow you create through it starts with null. Updating a workflow through this API doesn't clear an existing value either, so a workflow originally authored through the Base44 app editor or a superagent keeps its context here even after an API update.
{
"anchor_message_id": "68b1c0d4e7b91d003c45a1f2",
"conversation_id": "0195f2a1-4c3e-7b21-9f0d-2a5c8e1b4d77"
}
When the workflow last started running, as an ISO 8601 UTC timestamp. This is null before its first run.
"2026-08-25T09:12:44Z"
How the workflow's most recent run ended: success, failed, or cancelled. This is null before the first run. See Workflow status for how this compares to a run's own status.
"success"
Runs that have failed in a row. Resets on the next success.
0
Runs the workflow has started, ever.
48
Runs that finished successfully, ever.
44
Runs that ended in an error, ever.
3
When the workflow was created, as an ISO 8601 UTC timestamp.
"2026-07-02T11:04:00Z"
When the workflow was last changed, as an ISO 8601 UTC timestamp.
"2026-08-20T16:31:00Z"
Email of whoever created the workflow.
"you@example.com"
The steps the workflow runs, as a CNCF Serverless Workflow v1.0 document. This is null when no version has been saved yet. Only this endpoint returns it. List workflows leaves it out.
{
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
}
Was this page helpful?