AI auto-reply automations
Automatically reply to every inbound WhatsApp DM on a number with your AI agent. The automation owns the inbound → agent → send graph; the messages API delivers the reply with the same service-window, opt-out, and billing rules as any other send.
1. Create an AI agent
Create the agent persona the automation will invoke, and keep its id. Agent creation is API/SDK-only today — the dashboard does not yet have an AI agent creation screen.
curl https://api.tyxter.com/v1/ai-agents \
-H "authorization: Bearer $TYXTER_API_KEY" \
-H "content-type: application/json" \
-d '{
"name": "support_bot",
"system_prompt": "You are a friendly support agent. Answer briefly."
}'2. Create, version, and publish the automation
Create the automation, add a version whose graph is the canonical inbound → ai_agent.invoke → message.send shape, then publish that version. Bind the inbound_message trigger to the WhatsApp number that should auto-reply via its phone_number_id. Published versions are immutable; a run always points at the exact version it executes.
3. The contact messages the number
When a contact sends an inbound WhatsApp text, Tyxter receives it, honors the contact's opt-out state, and creates a single automation run. Runs are deduplicated per inbound message, so provider retries never produce a double reply.
4. The agent replies automatically
The run invokes your AI agent with the contact's message and sends the agent's reply back through the messages API. If the agent yields no usable reply, the builder-configured fallback message is sent instead.
One auto-reply per number
At most one enabled inbound_message automation may be bound to a given phone_number_id per environment. Publishing or enabling a second inbound automation for the same number is rejected with 409 automation_inbound_number_conflict.
Beyond auto-reply: the workflow engine
An automation graph is a directed, acyclic set of nodes connected by edges. A trigger node starts a run; the runtime walks the graph, persists each node's output, and resumes across waits. The full node config for every type is published as a per-type oneOf in /openapi.json so you (or an agent) can author a valid graph from the spec alone.
Node catalog
- Triggers —
manual.trigger,webhook.trigger(config.slug),inbound_message.trigger(config.phone_number_id),schedule.trigger(config.cron+ optionalconfig.timezone), andflow_completed.trigger(config.flow_id). ai_agent.invoke— runs a configured AI agent byai_agent_idand records its reply for downstream nodes. The agent is a blackbox: the node never carries model, prompt, tool, or MCP config — that lives in the agent itself.http.request— an outbound HTTP call with SSRF protection; authenticate with a stored credential (see below).condition— evaluates a booleanconfig.expressionand routes to thetrue/falseoutgoing edge.delay/time_gate— pause the run (durably) and resume later.message.send/template.send— send through the messages API (inherits service-window, opt-out, billing, idempotency).webhook.emit— emit a customer-defined event (config.event_name+config.data) delivered to your subscribed endpoints asautomation.custom.
Referencing run data with expressions
String config fields are interpolated against the run context by a sandboxed engine — never eval. Three namespaces: trigger.* (the run input), nodes.<id>.output.* (any upstream node's output, surviving delay / time_gate), and run.*.
{ "type": "message.send",
"config": {
"to": "{{ trigger.contact_phone }}",
"from": "{{ trigger.business_number }}",
"body": "AI says: {{ nodes.agent.output | default:'(no reply)' }}"
} }Filters: default, lower, upper, json, date. Boolean expressions (for condition nodes and edge guards) support comparisons, &&/||/!, contains, and an anchored, ReDoS-safe matches. Invalid expressions are rejected when you create the version.
Reliability: retries, timeouts, cancellation
Side-effect nodes carry an optional config.retry = { max_attempts, backoff_ms } (http.request and ai_agent.invoke retry transient failures by default) and a config.timeout_ms budget. A wedged call that exhausts its retries lands the run in timed_out. Cancel a run with
automation.run_cancelled webhook and stops at the next step boundary.Authenticated HTTP (credentials vault)
An http.request node authenticates via config.auth = { credential_id }, referencing a stored credential whose secret is encrypted at rest and injected only at send time — it never appears in graph JSON, run summaries, logs, or the OpenAPI spec.
Receiving third-party webhooks
A webhook.trigger can verify a third party's native signature instead of a relay. Set config.verification.mode to stripe ( verifies Stripe-Signature), github (verifies X-Hub-Signature-256), or shared_secret_header; the default tyxter_hmacverifies Tyxter's own signed envelope. A forged or unsigned request is rejected with a 404.