curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/workflows \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Email me new signups",
"definition": {
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
},
"trigger": {
"config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
}
},
"description": "Sends an email whenever a User record is created.",
"change_summary": "Initial version"
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/workflows"
payload = {
"name": "Email me new signups",
"definition": {
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
},
"trigger": { "config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
} },
"description": "Sends an email whenever a User record is created.",
"change_summary": "Initial version"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Email me new signups',
definition: {do: [], document: {dsl: '1.0.0', name: 'notify', version: '1.0.0'}},
trigger: {
config: {cron_expression: '0 9 * * *', timezone: 'UTC', trigger_type: 'scheduled'}
},
description: 'Sends an email whenever a User record is created.',
change_summary: 'Initial version'
})
};
fetch('https://app.base44.com/api/apps/{app_id}/workflows', 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",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Email me new signups',
'definition' => [
'do' => [
],
'document' => [
'dsl' => '1.0.0',
'name' => 'notify',
'version' => '1.0.0'
]
],
'trigger' => [
'config' => [
'cron_expression' => '0 9 * * *',
'timezone' => 'UTC',
'trigger_type' => 'scheduled'
]
],
'description' => 'Sends an email whenever a User record is created.',
'change_summary' => 'Initial version'
]),
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"
payload := strings.NewReader("{\n \"name\": \"Email me new signups\",\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 \"description\": \"Sends an email whenever a User record is created.\",\n \"change_summary\": \"Initial version\"\n}")
req, _ := http.NewRequest("POST", 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.post("https://app.base44.com/api/apps/{app_id}/workflows")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Email me new signups\",\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 \"description\": \"Sends an email whenever a User record is created.\",\n \"change_summary\": \"Initial version\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/workflows")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Email me new signups\",\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 \"description\": \"Sends an email whenever a User record is created.\",\n \"change_summary\": \"Initial version\"\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"
}
}
}Create workflow
Creates a workflow and starts it running.
The new workflow is active immediately, so a scheduled trigger begins firing on its schedule and an event trigger starts listening as soon as this returns. Create it, then call Toggle workflow status if you want it paused instead.
A workflow’s name is unique per app across everything that is not archived. Reusing a name is rejected.
This is only allowed from the app’s main branch.
curl --request POST \
--url https://app.base44.com/api/apps/{app_id}/workflows \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "Email me new signups",
"definition": {
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
},
"trigger": {
"config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
}
},
"description": "Sends an email whenever a User record is created.",
"change_summary": "Initial version"
}
'import requests
url = "https://app.base44.com/api/apps/{app_id}/workflows"
payload = {
"name": "Email me new signups",
"definition": {
"do": [],
"document": {
"dsl": "1.0.0",
"name": "notify",
"version": "1.0.0"
}
},
"trigger": { "config": {
"cron_expression": "0 9 * * *",
"timezone": "UTC",
"trigger_type": "scheduled"
} },
"description": "Sends an email whenever a User record is created.",
"change_summary": "Initial version"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'Email me new signups',
definition: {do: [], document: {dsl: '1.0.0', name: 'notify', version: '1.0.0'}},
trigger: {
config: {cron_expression: '0 9 * * *', timezone: 'UTC', trigger_type: 'scheduled'}
},
description: 'Sends an email whenever a User record is created.',
change_summary: 'Initial version'
})
};
fetch('https://app.base44.com/api/apps/{app_id}/workflows', 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",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'Email me new signups',
'definition' => [
'do' => [
],
'document' => [
'dsl' => '1.0.0',
'name' => 'notify',
'version' => '1.0.0'
]
],
'trigger' => [
'config' => [
'cron_expression' => '0 9 * * *',
'timezone' => 'UTC',
'trigger_type' => 'scheduled'
]
],
'description' => 'Sends an email whenever a User record is created.',
'change_summary' => 'Initial version'
]),
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"
payload := strings.NewReader("{\n \"name\": \"Email me new signups\",\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 \"description\": \"Sends an email whenever a User record is created.\",\n \"change_summary\": \"Initial version\"\n}")
req, _ := http.NewRequest("POST", 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.post("https://app.base44.com/api/apps/{app_id}/workflows")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Email me new signups\",\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 \"description\": \"Sends an email whenever a User record is created.\",\n \"change_summary\": \"Initial version\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/apps/{app_id}/workflows")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Email me new signups\",\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 \"description\": \"Sends an email whenever a User record is created.\",\n \"change_summary\": \"Initial version\"\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.
Body
Name for the workflow. Must be unique among the app's workflows that are not archived.
"Email me new signups"
The steps to run, as a CNCF Serverless Workflow v1.0 document. Check it with Validate a workflow definition first.
{ "do": [], "document": { "dsl": "1.0.0", "name": "notify", "version": "1.0.0" } }
What starts the workflow. The trigger goes inside config, whose trigger_type picks the kind and whose remaining fields configure it. Add a top-level condition to skip a dispatch unless a jq expression over the payload is truthy.
{ "config": { "cron_expression": "0 9 * * *", "timezone": "UTC", "trigger_type": "scheduled" } }
What the workflow is for, in your own words.
"Sends an email whenever a User record is created."
Note describing this version, kept in the workflow's version history.
"Initial version"
Response
The created 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?