Agent Skill

Have an AI agent build a marketing automation

Create, fix and activate a graph automation (welcome, abandoned cart, follow-up) with Sending, enroll contacts by hand, and read the logs to find out why a contact never got a message. Use this for recurring sends triggered by an event, and before changing automations that are already live.

Skill build-automation · the same bytes agents read at /.well-known/agent-skills/build-automation/SKILL.md

Build an automation

An automation is a graph (a DAG): a trigger, then wait, condition, action and message nodes. It lives in two states: draft and active version.

1. Look at what is already there

list_automations

Do this always, before creating or activating. It also shows the triggers of the published version (in triggers: there can be more than one), and that is the piece of information that prevents damage: triggers like contact.list_added and contact.tag_added start real sends.

If you add a contact to a list while an active automation is hooked to that list, you have just sent. The same goes for tag_contacts.

2. Create the draft

The body is a Journey: name, entry (id of the starting node) and nodes. Every node has an id, a type and the edges to other ids (or null, which ends the branch).

create_automation {
  journey: {
    name: "Welcome",
    entry: "t1",
    nodes: [
      { id: "t1", type: "trigger", event: "contact.list_added",
        match: { listId: "<uuid>" }, next: "tag1" },
      { id: "tag1", type: "add_tag", tagIds: ["<uuid>"], next: "w1" },
      { id: "w1", type: "wait", duration: "P1D", next: "m1" },
      { id: "m1", type: "message", preferredChannel: "email",
        fallbackChain: ["email"], templateByChannel: { email: "<uuid>" }, next: "done" },
      { id: "done", type: "exit" }
    ]
  }
}

This creates the draft and version 1. Nothing is sent yet.

The uuids of tags, lists, templates and automations come from list_tags, list_lists and list_automations: do not invent them.

Available nodes

Sending · message (email/WhatsApp/Telegram with fallback, optional attachmentIds and ai), notify (email to your own team, not to the contact).

Flow · wait (exactly one of duration ISO-8601, until date, or untilCondition), wait_for_event, branch (onTrue/onFalse), split (percentages across 2 to 4 branches), goto (jump to another step), start_automation, end_automation, webhook, goal, exit.

Contact · add_tag, remove_tag, subscribe, unsubscribe, update_contact (null deletes a key), add_note, score, conversion.

The complete list with the fields of each one is in the description of create_automation: read it instead of guessing.

Several entry triggers

An automation can start from several events: add more trigger nodes, each with its own next. The contact enters through the trigger that matches the event, so two entry points can lead to two different branches or to the same step.

{ id: "t1", type: "trigger", event: "contact.list_added", match: { listId: "<uuid>" }, next: "w1" },
{ id: "t2", type: "trigger", event: "cart.abandoned", next: "w1" },

entry must be the id of a trigger: it is where contacts enrolled by hand start from, when no event is in play.

Three rules the save enforces

You need at least one trigger, two triggers with the same event and the same match are refused (the second would add nothing), and a trigger other than the entry must have a next: a disconnected one would do nothing.

Cycles are allowed only through a goto node (with a ceiling of 50 hops per contact). A loop made of next edges chasing each other is refused: it is almost always a writing mistake.

Careful with add_tag and subscribe: they re-emit contact.tag_added and contact.list_added, so they can start another automation. Check list_automations before using them.

3. Fix without recreating

get_automation { automationId }      → reads back the published Journey
update_automation { automationId, journey }   → publishes a new version

Always start from the DAG that get_automation returns: rebuilding one from scratch loses the parts you did not know about. Contacts already inside stay on the version they entered with; the change applies to new entries.

activate_automation { automationId }

From here on, every contact that satisfies the trigger enters the flow and receives real messages. Have the user confirm explicitly before calling it: this is the point of no return.

5. Enroll contacts by hand

This is for people who are already there, without waiting for the event to happen again (a recovery campaign, a realignment after an import):

enroll_contacts { automationId, listId }        // or segmentId, or contactIds

One audience at a time. It works only on active automations, and the contacts enter from the entry, so the steps that send do send: ask for confirmation as you would for activate_automation. Anyone already inside is ignored (one enrollment per contact) and templates using {{event_*}} find empty values, because no event fired here.

The operation is asynchronous: the response says it has been queued. For the outcome use get_automation_log.

Events

wait_for_event nodes and custom entries are fed by the events API:

POST https://sending.dev/api/v1/events
{ "name": "cart.abandoned", "email": "[email protected]", "properties": { ... } }

An event can bring new contacts into the flow and release the ones waiting on a wait_for_event.

Working out what happened

When a contact "got nothing", do not guess:

get_automation_log { automationId, email: "[email protected]" }

It lists the steps that were walked, with their outcome. A skipped status is not an error: it says what stopped that step, and it is almost always the answer (address on the suppression list, plan quota reached, missing template or resource). With nodeId you narrow it to a single step of the flow.

For the overall effect on one person:

get_contact_journey { email }

It returns messages, inbound mail, touches and conversions in one timeline, telling transactional apart from marketing.

Attachments in nodes

The message node carries attachmentIds: upload first with upload_attachment and put the id there. Do not delete those attachments while the automation is active, or the deferred sends will break.

Use it

Point your agent at the file, or paste it into a system prompt. The tools it names live on our MCP server, so an agent needs to be connected first and needs the scopes for what it is about to do.

Connect over MCP · Documentation · Raw markdown

The other skills

Start with Sending

Give your agent something worth sending.

3,000 messages a month, free. No card. The MCP endpoint is on every plan, including this one.