Create automation
curl --request POST \
--url https://app.famulor.de/api/user/automate/flows \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"flow": {},
"sample": {},
"assistant_id": 123,
"bind_webhook": "<string>",
"confirm_side_effects": true
}
'import requests
url = "https://app.famulor.de/api/user/automate/flows"
payload = {
"name": "<string>",
"flow": {},
"sample": {},
"assistant_id": 123,
"bind_webhook": "<string>",
"confirm_side_effects": True
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
flow: {},
sample: {},
assistant_id: 123,
bind_webhook: '<string>',
confirm_side_effects: true
})
};
fetch('https://app.famulor.de/api/user/automate/flows', 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.famulor.de/api/user/automate/flows",
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' => '<string>',
'flow' => [
],
'sample' => [
],
'assistant_id' => 123,
'bind_webhook' => '<string>',
'confirm_side_effects' => true
]),
CURLOPT_HTTPHEADER => [
"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.famulor.de/api/user/automate/flows"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"flow\": {},\n \"sample\": {},\n \"assistant_id\": 123,\n \"bind_webhook\": \"<string>\",\n \"confirm_side_effects\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
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.famulor.de/api/user/automate/flows")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"flow\": {},\n \"sample\": {},\n \"assistant_id\": 123,\n \"bind_webhook\": \"<string>\",\n \"confirm_side_effects\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.famulor.de/api/user/automate/flows")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"flow\": {},\n \"sample\": {},\n \"assistant_id\": 123,\n \"bind_webhook\": \"<string>\",\n \"confirm_side_effects\": true\n}"
response = http.request(request)
puts response.read_body{
"automation_id": "f4EaLhOW2zoEsXXSOJP2r",
"webhook_url": "https://automate.famulor.ai/api/v1/webhooks/f4EaLhOW2zoEsXXSOJP2r",
"status": "active",
"test": {
"run_status": "SUCCEEDED",
"steps": [
{ "name": "trigger", "status": "SUCCEEDED", "classification": "ok", "error": null },
{ "name": "step_1", "status": "SUCCEEDED", "classification": "ok", "error": null }
]
},
"response": { "ok": "true", "echo": "hello" },
"binding": null
}
{
"automation_id": "aB3xYz01MnOpQrStUvWxY",
"webhook_url": null,
"status": "active_untested",
"test": {
"run_status": "not_tested",
"steps": []
},
"response": null,
"binding": null
}
{
"message": "This automation contains steps that would REALLY send messages, emails or start calls during the test run: Send SMS. Confirm with the user first — point those steps at a safe recipient the user owns — then retry with confirm_side_effects set to true.",
"error": "side_effects_require_confirmation"
}
Automations
Create automation
Create a Famulor automation from a definition, activate it, and prove it with a real test run.
POST
/
api
/
user
/
automate
/
flows
Create automation
curl --request POST \
--url https://app.famulor.de/api/user/automate/flows \
--header 'Content-Type: application/json' \
--data '
{
"name": "<string>",
"flow": {},
"sample": {},
"assistant_id": 123,
"bind_webhook": "<string>",
"confirm_side_effects": true
}
'import requests
url = "https://app.famulor.de/api/user/automate/flows"
payload = {
"name": "<string>",
"flow": {},
"sample": {},
"assistant_id": 123,
"bind_webhook": "<string>",
"confirm_side_effects": True
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
flow: {},
sample: {},
assistant_id: 123,
bind_webhook: '<string>',
confirm_side_effects: true
})
};
fetch('https://app.famulor.de/api/user/automate/flows', 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.famulor.de/api/user/automate/flows",
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' => '<string>',
'flow' => [
],
'sample' => [
],
'assistant_id' => 123,
'bind_webhook' => '<string>',
'confirm_side_effects' => true
]),
CURLOPT_HTTPHEADER => [
"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.famulor.de/api/user/automate/flows"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"flow\": {},\n \"sample\": {},\n \"assistant_id\": 123,\n \"bind_webhook\": \"<string>\",\n \"confirm_side_effects\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
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.famulor.de/api/user/automate/flows")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"flow\": {},\n \"sample\": {},\n \"assistant_id\": 123,\n \"bind_webhook\": \"<string>\",\n \"confirm_side_effects\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.famulor.de/api/user/automate/flows")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"flow\": {},\n \"sample\": {},\n \"assistant_id\": 123,\n \"bind_webhook\": \"<string>\",\n \"confirm_side_effects\": true\n}"
response = http.request(request)
puts response.read_body{
"automation_id": "f4EaLhOW2zoEsXXSOJP2r",
"webhook_url": "https://automate.famulor.ai/api/v1/webhooks/f4EaLhOW2zoEsXXSOJP2r",
"status": "active",
"test": {
"run_status": "SUCCEEDED",
"steps": [
{ "name": "trigger", "status": "SUCCEEDED", "classification": "ok", "error": null },
{ "name": "step_1", "status": "SUCCEEDED", "classification": "ok", "error": null }
]
},
"response": { "ok": "true", "echo": "hello" },
"binding": null
}
{
"automation_id": "aB3xYz01MnOpQrStUvWxY",
"webhook_url": null,
"status": "active_untested",
"test": {
"run_status": "not_tested",
"steps": []
},
"response": null,
"binding": null
}
{
"message": "This automation contains steps that would REALLY send messages, emails or start calls during the test run: Send SMS. Confirm with the user first — point those steps at a safe recipient the user owns — then retry with confirm_side_effects set to true.",
"error": "side_effects_require_confirmation"
}
Famulor 1.0 API (legacy). This page applies only to Famulor 1.0 (
app.famulor.de) and is retained for legacy compatibility. For the current platform, use the Famulor 2.0 API reference.The test run executes the automation for real. Definitions containing steps that send messages or emails, or start calls, are refused unless you explicitly pass
confirm_side_effects: true — and when you do, those steps really send during the test. Point them at recipients you own.The definition format
Theflow object is the automation’s definition. The preferred shape is a flat list:
{
"trigger": { ... },
"steps": [ step1, step2, ... ]
}
BRANCH step’s conditional steps go under its onSuccessAction / onFailureAction. (A hand-nested tree, where each step sits under the previous one’s nextAction, is also accepted.)
A definition always replaces the whole automation — it is never merged. A trigger with no steps is rejected (no_steps): an automation that does nothing cannot be activated.
Supported triggers (trigger.settings)
| Trigger | pieceName / triggerName | Behavior |
|---|---|---|
| Webhook | @activepieces/piece-webhook / catch_webhook | You get a webhook_url to call from external systems. The payload arrives wrapped: reference fields as {{trigger['body']['field']}}. Tested synchronously with your sample. |
| Phone call ended | @famulor/piece-famulor / phoneCallEnded | Requires assistant_id. Attached to the assistant after a green test. Payload fields live directly on the trigger: {{trigger['extracted_variables']['x']}}, {{trigger['customer_phone']}}, … |
| Inbound call | @famulor/piece-famulor / inboundCall | Requires assistant_id. Runs before the assistant answers; must end in a respond step returning a flat map of strings. |
| New conversation | @famulor/piece-famulor / newConversation | Requires assistant_id. |
| Conversation ended | webhook trigger + bind_webhook: "conversation_ended" | Requires assistant_id. Fires when a chat ends; payload arrives wrapped, so reference fields through {{trigger['body']['...']}}. |
| Schedule | @activepieces/piece-schedule / every_x_minutes, every_day, … | Cannot be fired on demand — activates armed as active_untested; the first scheduled run is the proof (check runs). |
| External integrations | the integration’s own trigger (new spreadsheet row, new CRM contact, new lead, …) | Your connected account is attached automatically; if it is missing or expired you get a needs_connection / needs_reconnection error with the exact steps to fix it in the Famulor app. Activates as active_untested. |
{{step_1['body']['field']}} for HTTP steps (their JSON nests under body). Common step shapes: HTTP requests, code transforms, branches, delays, respond steps, and Famulor platform actions (send SMS/WhatsApp, start a call, re-queue a lead). The easiest way to learn a step’s exact shape is to read an existing automation with Get automation or apply a template and inspect what it built.
Request body
string
required
A short human name for the automation (max 255 characters)
object
required
The automation definition —
{"trigger": {...}, "steps": [...]} as described above. Max 1 MB.object
A realistically-shaped sample payload for the test run (what the trigger will receive). For assistant events, a canonical sample built from the assistant’s own variables is used when omitted. Max 256 KB.
integer
Required for assistant-event automations (
phoneCallEnded, inboundCall, newConversation, and bind_webhook): the assistant this automation attaches to. It starts receiving that assistant’s real events after the test passes. (For the platform triggers, selecting the assistant inside the trigger’s settings.input.assistant also works — the explicit parameter wins.)string
For a webhook-triggered definition only: attach it to the assistant’s conversation-ended event. The only supported value is
conversation_ended. Requires assistant_id.boolean
Required (
true) when the definition contains steps that send messages or emails, start calls, or make non-GET HTTP requests — the test run executes them for real.Response
Returns201 when the automation is active (active / active_untested), 200 when it was built but its test run failed (test_failed), and 422 for a definition that never reached the test run (see error codes below).
string
The ID of the created automation
string | null
For webhook-triggered automations (including conversation-ended ones): the URL external systems call to fire it.
null for assistant-event and schedule automations.string
active — the test run passed; the automation is live (and attached, for assistant events).
active_untested — the trigger cannot be fired on demand (schedules, external integrations); the automation is live and armed, and the first real event is the proof.
test_failed — the test run failed; the automation was left disabled. Read test.steps for the per-step classification.object
The test run result
Show test properties
Show test properties
string
SUCCEEDED, PAUSED, FAILED, or not_tested (untestable triggers); rarely no_run when the test produced no execution record. PAUSED counts as success: the run is parked at a Delay step waiting for its target time — every step before the delay already ran.array
Per-step outcome of the test run
Show step properties
Show step properties
string
The step name (
trigger, step_1, …)string
SUCCEEDED, FAILED or PAUSEDstring
ok, paused_at_delay, or — for failures — wiring_error (the definition is wrong: fix it and repair via Update automation), missing_connection (an account must be connected in the Famulor app first), missing_record (the sample referenced data that does not exist — often fine).string | null
The step’s error message, when it failed
object | string | null
For synchronously-tested automations (webhook and inbound triggers): what the automation responded during the test run
object | null
For assistant-event automations:
{"type": "post_call" | "inbound" | "conversation" | "conversation_ended", "assistant_id": <id>, "bound": <bool>}. bound is true only after a green test.Error codes (422)
Hard failures return{"message": "...", "error": "<code>"} and nothing is activated. Notable codes:
| error | Meaning |
|---|---|
side_effects_require_confirmation | The definition contains steps that would really send during the test — retry with confirm_side_effects: true after checking the recipients. |
no_steps | The trigger has no steps chained to it. |
invalid_definition / unsupported_trigger | The definition is missing its trigger, or uses a trigger type that is not supported. |
assistant_required / assistant_not_found | The trigger needs an assistant_id, or the assistant does not belong to your account. |
binding_conflict | The assistant already has an automation (or a custom webhook) for this event — an assistant has one slot per event type. Update the existing automation instead, or delete it first. |
needs_connection / needs_reconnection | The trigger’s integration account must be connected (or reconnected) in the Famulor app first — the message contains the exact steps. |
import_failed / publish_failed / create_failed | The definition was rejected or could not be activated. |
{
"automation_id": "f4EaLhOW2zoEsXXSOJP2r",
"webhook_url": "https://automate.famulor.ai/api/v1/webhooks/f4EaLhOW2zoEsXXSOJP2r",
"status": "active",
"test": {
"run_status": "SUCCEEDED",
"steps": [
{ "name": "trigger", "status": "SUCCEEDED", "classification": "ok", "error": null },
{ "name": "step_1", "status": "SUCCEEDED", "classification": "ok", "error": null }
]
},
"response": { "ok": "true", "echo": "hello" },
"binding": null
}
{
"automation_id": "aB3xYz01MnOpQrStUvWxY",
"webhook_url": null,
"status": "active_untested",
"test": {
"run_status": "not_tested",
"steps": []
},
"response": null,
"binding": null
}
{
"message": "This automation contains steps that would REALLY send messages, emails or start calls during the test run: Send SMS. Confirm with the user first — point those steps at a safe recipient the user owns — then retry with confirm_side_effects set to true.",
"error": "side_effects_require_confirmation"
}
Related pages: List automation templates, Apply automation template, and Authentication.