---
title: "Validate Automation Graph"
description: "Check a step graph without saving anything."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.speakai.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Validate Automation Graph

Use the Speak AI MCP server tool `validate_automation_graph` to check a step graph
without saving anything.

## What does it do?

Check a step graph without saving anything. Reports the same problems
`create_automation` and `update_automation` would refuse (branch wiring, rules a
condition cannot actually read, shapes the Speak web editor could not reopen) plus
non-blocking warnings. Use it to iterate on a branched automation instead of
discovering the problems one failed save at a time.

## Parameters

`validate_automation_graph` takes 3 parameters, 1 of them required.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `steps` | array of object | Yes | Ordered array of graph steps (1-20). Each step is an object: \{ stepId: string (unique within the array), stepType: one of "speak-upload" \| "magic-prompt" \| "translation" \| "filter" \| "condition" \| "notify" \| "outbound-webhook" \| "composio-action", dependsOn?: string[] (stepIds this step runs after), branch?: "true"\|"false" (which outcome of an upstream condition step this step belongs to) \} plus ONE config key matching stepType: • speak-upload -\> speakUpload: \{ sourceMode: "url"\|"file", sourceUrl (required when sourceMode="url"; tokens allowed. If the token resolves to an object, the first http(s) URL inside it is used), folderId (required, unless folderRouting.mode="dynamic" where it becomes the optional fallback), name? (tokens allowed, mixable with static text), language? (language code or token), fieldsMap?: \{ \<customFieldId\>: "\<value\>" \} (writes payload values into Speak custom fields on the uploaded media; values are usually \{\{trigger.payload.\<path\>\}\} tokens. Get field ids from `list_fields`), folderRouting?: \{ mode: "static"\|"dynamic", sourceKey (payload key holding the destination folder name, required when dynamic), onNoMatch: "create"\|"default" (create a folder named after the value, or fall back to folderId) \} \} • magic-prompt -\> magicPrompt: \{ prompt (required unless fieldIds given, max 20000), title?, assistantType? ("general"\|"researcher"\|"marketer"\|"sales"\|"recruiter"\|"custom", default "general"), assistantTemplateId? (required if assistantType="custom"), fieldIds?: string[] (max 10. Extract answers into these custom fields), analysisInput? ("transcript" (default) \| "audio" \| "video"): what the model receives. "audio" lets it hear tone and delivery, "video" also lets it see what is on screen; on a video file "audio" extracts the audio track first. Premium: requires the account's audio/video analysis opt-in, and costs credits per hour of media \} • translation -\> translation: \{ targetLanguage: region-qualified locale code, e.g. "es-ES", "fr-FR" (bare codes like "es" are rejected) \} • filter -\> filter: \{ logic: "AND"\|"OR" (default "AND"), rules: [\{ field, op, value? \}] (1-20) \}. The run continues only when the rules match, otherwise it stops silently • condition -\> condition: same \{ logic, rules \} shape as filter, but instead of stopping it routes: downstream steps marked branch:"true"/"false" run according to the outcome. Branch wiring rules, all enforced before the request is sent: • A leg is a CHAIN: the first step of a leg depends on the condition, the rest depend on the step before them in the same leg, and every step on the leg carries the same branch marker. • A step that runs after the branch (a merge) depends on the LAST step of every leg that carries on. When a leg is empty its last step IS the condition, and the merge then carries that leg's marker. • A leg ends the run simply by having nothing depend on its last step. • Once anything carries a branch marker, EVERY other step needs a dependsOn: a step with no parents is an entry point and runs on both branches, and a second entry point is rejected. • A condition with no steps on either side is rejected: both paths would do the same thing. • Branches nest at most three deep, and a nested condition must be the last step of the leg it sits on, or the automation cannot be reopened in the Speak web editor. • A scheduled automation cannot branch: a schedule runs over a batch and a condition resolves once for the whole batch, so the leg would run against media that did not match. Use a filter, which narrows the batch. • notify -\> notify: \{ channel: "in_app"\|"email"\|"slack", target?, message (required, tokens allowed) \} • outbound-webhook -\> outboundWebhook: \{ url (required, tokens allowed), method? ("GET"\|"POST"\|"PUT"\|"PATCH"\|"DELETE", default "POST"), headers?: \{ \<name\>: \<value\> \}, bodyTemplate?: string \| object (tokens allowed) \} • composio-action -\> composio: \{ app, action, connectedAccountId?, argsTemplate? \} (Composio is currently behind a server flag and may be unavailable) Filter/condition rule fields depend on what flows into the step: MEDIA -\> name\|duration\|sourceLanguage\|tags\|transcript\|speakers or a custom field id; INSIGHT (straight after a magic-prompt step) -\> answer only, so put a branch on a media field BEFORE the AI step. Neither a filter nor a condition can read the inbound webhook payload (they see the media and earlier step answers only) so a payload path such as "contact.status" is refused: upload first with speakUpload.fieldsMap to write that value into a custom field, then test the field id instead. Ops by field type. Text: eq\|neq\|contains\|ncontains\|startsWith\|exists; number: eq\|neq\|gt\|lt\|exists; array: contains\|ncontains\|exists ("exists" takes no value; gt/lt values are numbers). Token syntax (usable in fields marked 'tokens allowed'): \{\{trigger.payload.\<path\>\}\} reads the inbound webhook payload (dot paths and [n] array indices; paths are relative to trigger.childKey when set. Discover valid paths with `get_inbound_webhook` after sending a test payload); \{\{step.\<stepId\>.\<path\>\}\} reads a previous step's output (speak-upload -\> mediaId, magic-prompt -\> answer, outbound-webhook -\> status/response). A positional \{\{step.\<index\>.\<path\>\}\} form also exists but is REFUSED on a branched automation: the index counts stored order ([condition, true leg, false leg, merge]), not run order, and a step on the branch that was not taken produces nothing. The token would resolve to an empty string inside whatever the step sends. |
| `trigger` | object | No | Trigger object (the automation's root). Always include triggerSlug. Supported shapes: • Media analyzed in folder(s): \{ type: "folders", triggerSlug: "media_analyzed", folderIds: string[] (min 1) \} • Inbound webhook (receive external payloads): \{ type: "folders", triggerSlug: "inbound_webhook", webhookId? (from `provision_inbound_webhook`; omit to auto-provision a new one on create), childKey? (dot-path narrowing which part of the payload feeds the automation, e.g. "data") \}. The create/update response includes inboundWebhook.inboundUrl: the public URL to POST payloads to. • Custom field updated: \{ type: "folders", triggerSlug: "field_updated", values: string[] (watched custom field ids, min 1), fieldValueMatches?: [\{ fieldId, values: string[] \}] (fire only when the field changes TO one of these values; empty values = any change), fieldMatchLogic?: "AND"\|"OR" (how multiple fieldValueMatches combine, default "OR") \} • Composio app event: \{ type: "composio", provider: "composio", app, triggerSlug, connectedAccountId \} (requires a connected account; may be behind a server flag) Notes: "tags"/"keywords" trigger types are rejected for graph automations. The server stores inbound-webhook triggers with type "webhook" internally: send type "folders" plus the slug as shown above. |
| `runType` | enum: instant, schedule | No | Run type the graph would be saved with. A schedule refuses any branch. |

## Behaviour

The Speak AI MCP server publishes these behaviour hints for
`validate_automation_graph`.

- Only reads data. Does not modify your workspace.
- Does not delete or overwrite existing data.
- Repeating an identical call is safe.
- Stays inside Speak AI. Does not reach other systems.

## Related

- [Automations](/mcp/tools/automations/) lists the rest of the Automations category.
- [Tool reference](/mcp/tools/) lists every Speak AI MCP server tool.
- [MCP server](/mcp/) covers setup, supported clients, and what agents can do once
  connected.

Source: https://docs.speakai.co/mcp/tools/automations/validate_automation_graph/index.mdx
