Install
openclaw skills install @workingdevshero/automate-itCreate content tasks and automations in Automate It, do the content work yourself or leave it to the built-in worker, poll task status through the human review gate, and fetch links to published posts. To support that it also reads and writes the surrounding workspace — skills (brand voice and house style), workspace files and folders, connected publishing integrations, link shortening — all bounded by the API key's scopes and the caller's workspace role. Deleting tasks, content items, automations, or folders is possible but requires explicit confirmation on each command. Use when asked to draft, schedule, or publish social content via Automate It, or to check the status or published results of a submitted task.
openclaw skills install @workingdevshero/automate-itAutomate It is a content platform with a human review gate: tasks describe content to create, a worker generates the content, a human reviews and approves it, and it publishes to the connected platforms (X, LinkedIn, Instagram, Threads, Facebook, Discord, Bluesky, articles, and more).
You can participate two ways:
--claim, attach your content, complete it into the human review queue. Automate It provides the review checkpoint and the credentialed publishing; you provide the brains.All commands use the bundled CLI: ./ait.mjs (Node 18+ or Bun; also runs as node ait.mjs …). It speaks the same MCP server that human-driven clients (Claude Code, etc.) use. Errors print {"error": "..."} to stderr and exit 1.
./ait.mjs help is the complete list of what this skill can do. There is no generic tool passthrough: every command is documented here, and anything not listed isn't reachable. Two limits apply throughout:
--yes. task delete, task delete-content, task clear-content, automation delete, and folders delete permanently remove data and refuse to run without it. Pass --yes only when the operator asked for that deletion by name; never to clear an error, and never on your own initiative. See Destructive operations.Required environment:
AUTOMATE_IT_API_KEY — an Automate It API key (starts with ak_). The human operator creates this in Automate It under Profile → API keys.AUTOMATE_IT_WORKSPACE (optional) — default workspace id. If unset and the key has exactly one workspace, it is auto-resolved. Every command also accepts --workspace <id>.AUTOMATE_IT_API_URL (optional) — defaults to https://api.automate.it.com.Verify access before doing real work:
./ait.mjs workspaces # workspaces the key can reach
./ait.mjs whoami # the user you act as
If a command fails with MCP HTTP 401, the key is missing, mistyped, or revoked — stop and tell the operator. If it fails with missing required scope, the key lacks a scope (see Permissions) — report which one is missing rather than retrying.
Use this when you (or your runtime) already produce the content and Automate It's job is human approval + publishing.
Create the task already claimed. --claim creates it in working status, so the built-in worker never touches it — there is no race to lose.
./ait.mjs task create --claim \
--title "Post about the v2.0 launch" \
--instructions "Context for reviewers: announcing Acme v2.0" \
--output-types x
The response includes the task JSON — save the id.
Claiming assigns the task to you: the built-in worker never touches it, the platform's dead-task recovery leaves it alone (that sweep is for the built-in worker's own crashed runs), and a later rejection routes back to you. Take as long as the content needs — but don't abandon a claimed task: nothing times it out, so it sits in working until you complete it or a human deletes it.
Attach your content. One call per content item; use sortOrder for threads (0 = first post).
./ait.mjs task add-content <taskId> --type x \
--body "Acme v2.0 is live — new API, faster everything. https://acme.dev/blog/v2"
For images/video: get a presigned URL, PUT the file, then reference it:
./ait.mjs upload-url --filename hero.png --mime-type image/png
# PUT your file to the returned uploadUrl, then:
./ait.mjs task add-content <taskId> --type x --body "..." \
--media '[{"type":"image","url":"<permanentUrl>"}]'
Complete it into review.
./ait.mjs task complete <taskId>
The task lands in the human review queue (review status). From here it's the same as delegate mode: poll, and fetch links after publish.
If your content is already finished, skip the three-step loop entirely with the one-shot submit — it creates the task with the content attached and lands it directly in the review queue:
./ait.mjs task submit --title "Post about the v2.0 launch" --type x \
--body "Acme v2.0 is live — new API, faster everything. https://acme.dev/blog/v2"
If a human rejects your content, the task drops back to todo and revisionCount goes up. It is still assigned to you (claiming assigned it), so the built-in worker can't touch it. Reclaim it — task complete only accepts a task in working — then revise.
Revise in place. task add-content appends another post — use it again and the task carries both the rejected draft and the fix.
./ait.mjs task claim <taskId> # back to "working", yours again
./ait.mjs task get <taskId> # newest `comments` entry says why it was rejected
# each contentItems[] entry has the id you need
./ait.mjs task update-content <taskId> <contentItemId> --body "<revised copy>"
./ait.mjs task complete <taskId>
To rewrite a multi-post thread from scratch, ./ait.mjs task clear-content <taskId> --yes first, then add each post again. task delete-content <taskId> <contentItemId> --yes drops a single one. Both destroy drafted work — prefer update-content, which revises in place and destroys nothing.
Explain yourself if the reviewer's note was ambiguous, or if you deliberately didn't follow it:
./ait.mjs task comment <taskId> --comment "Kept the link — removing it drops the CTA."
Create the task without --claim. Describe the content in --instructions the way you would brief a copywriter: topic, angle, tone, audience, links to include.
./ait.mjs task create \
--title "Post about the v2.0 launch" \
--instructions "Write an upbeat announcement of Acme v2.0 for developers. Mention the new API and link to https://acme.dev/blog/v2." \
--output-types x,linkedin
Poll until it resolves. Poll every 60 seconds or so while generating; once it reaches review, a few checks an hour is plenty — humans are slow.
./ait.mjs task get <taskId>
| status | meaning | what you do |
|---|---|---|
todo | queued, not started | wait |
working | content is being generated | wait (in worker mode this means you owe content) |
review | waiting on a human reviewer | wait (slow — human in the loop) |
approved | human approved, not yet published | wait (or report approval) |
published | live on the destination platforms | fetch links, report success |
failed | generation or publish failed | report failure to the operator |
deleted | task was deleted by a human | stop tracking it, report |
Fetch the published links once status is published:
./ait.mjs task links <taskId>
Returns {"taskId": "…", "status": "published", "links": [{"platform": "x", "postUrl": "https://…", "postId": "…"}]} — report the postUrl values back to whoever asked.
--publish-mode uses the workspace's default publish mode (workspace setting; ships as scheduled). Only pass a mode when the operator asked for specific timing behavior.--publish-mode scheduled --publish-at 2026-07-10T15:00:00Z: publishes at the given time after approval. Omit --publish-at and the platform auto-schedules the task at approval — the next open slot per the workspace's per-platform posting targets (e.g. 2 X posts/day), at a good hour for the destination in the workspace timezone.--publish-mode immediate: publishes automatically the moment a reviewer approves.--publish-mode manual: after approval, a human (or a reviewer key) also has to publish or schedule it explicitly.--no-review skips the human review gate entirely (auto-publish on completion). Never use this unless the operator has explicitly told you to.Valid --output-types / --type values: x, linkedin, instagram, facebook, threads, discord, bluesky, article, email, carousel, podcast, rss, spreadsheet, document.
Only platforms the workspace has actually connected will publish. Don't guess and don't ask — check:
./ait.mjs integrations # which platforms are connected
Every post is validated against its platform's limit when you add it — and again when you edit it with task update-content — and rejected if it's over. Know the limit before you write:
| Platform | Limit | Counted as |
|---|---|---|
x | 280, or 25,000 with Premium | Weighted — any URL counts 23 regardless of length, most emoji count 2 |
bluesky | 300 | Grapheme clusters — a multi-code-point emoji counts 1 |
threads | 500 | Characters, but emoji count as their UTF-8 byte length (4 for most) |
discord | 2,000 | Characters |
instagram | 2,200 | Characters |
linkedin | 3,000 | Characters |
youtube | 5,000 (description; title 100) | Characters |
facebook | 63,206 | Characters |
article, email, carousel, podcast, rss, spreadsheet, and document have no text limit.
Three platforms don't count raw length, so a draft that looks fine by eye can still be rejected. Measure every post and thread item before you attach it:
./ait.mjs limits # every platform's limit
./ait.mjs limits --platform threads --text "<draft>"
# {"platform": "threads", "characterLimit": 500, "text": {"length": 501, "charactersRemaining": -1, "fits": false}, …}
Going over doesn't just fail one post — on X, Threads, and Bluesky a thread publishes one post at a time, so an over-limit item halfway down can leave the earlier posts live and the rest unsent.
If a post won't fit, split it across more posts with an increasing sortOrder rather than truncating the idea.
./ait.mjs shorten <url> --title "v2 launch" shortens a link, which also makes it trackable. It won't save you characters on X — every URL counts 23 there regardless — but it does on platforms that count raw length.
When a task publishes to rss and to social platforms, put the token {articleUrl} in the social post body. At publish time it's replaced with the article's real URL — which doesn't exist until the feed item is created, so you can't write it yourself.
./ait.mjs task add-content <taskId> --type article --title "Acme v2.0" --body "<the article>"
./ait.mjs task add-content <taskId> --type x --body "Acme v2.0 is live. Read more: {articleUrl}"
If the task has no rss output (or the feed publish fails), the token and the space before it are stripped, so the post still reads correctly. The token only resolves for rss — a podcast output does not populate it.
A skill is reusable instruction — brand voice, formatting rules, house style — plus any reference files bundled with it. Check for skills before you write anything.
./ait.mjs skills list # id, name, description
./ait.mjs skills get <skillId> # full instructions + bundled file ids
Attach them by name (or id) when creating a task:
./ait.mjs task create --title "Launch post" --skills "brand voice,changelog style" --claim
This is the part agents get wrong. Attaching a skill only does something when the built-in worker runs the task — it injects the instructions and files for you. In worker mode (--claim) the worker never runs, so nothing is injected. Read the instructions yourself with skills get and apply them. Attaching is still worth doing: it records which skills the content was written against.
Bundled files are workspace files. Fetch one by getting a URL and downloading it yourself:
./ait.mjs files download-url <fileId> # returns a time-limited downloadUrl
curl -o brand.pdf "<downloadUrl>"
./ait.mjs task list --status review # everything waiting on a human
./ait.mjs task list --limit 10 # recent tasks
./ait.mjs task get <taskId> # full detail for one task
./ait.mjs files list --search brand # workspace file browser
If tasks arrive from automations or teammates and you're the worker, take them one at a time:
./ait.mjs task claim-next # atomically claims the oldest todo task
Use this rather than task list --status todo followed by task claim <id>: those are two calls, and another worker can take the task in between. claim-next returns nothing when the queue is empty. It never hands you a task assigned to someone else.
Humans (or other agents) can assign tasks to your user instead of the built-in worker. Assigned tasks are invisible to the built-in worker — they wait for you. Check for them periodically, then run the normal work loop:
./ait.mjs task list --mine --status todo # work waiting on you
./ait.mjs task claim <taskId> # only the assignee can claim an assigned task
# … generate content …
./ait.mjs task add-content <taskId> --type x --body "..."
./ait.mjs task complete <taskId>
If a task you worked comes back with a higher revisionCount (a reviewer rejected it), you'll find it in task list --mine --status todo again — read the rejection comment via task get, revise in place with task update-content, and task complete again. Because it stays assigned to you, the built-in worker can't claim it out from under you while you revise.
You can also assign at creation: task create --assign <userId|me>, or task submit --assign me.
If your key belongs to a reviewer/admin and the operator has asked you to help triage, these exist: task next-review, task approve <id>, task reject <id> --comment "...", task publish <id>, task schedule <id> --at <ISO date> (or --auto for the next open slot per the workspace's posting targets, or --clear to return it to manual hold). --auto is the right choice for clearing a backlog of held posts — the tool result names the stamped time. Do not approve or publish content you produced yourself — the whole point of the review gate is that a different human (or their delegate) signs off.
A task is one-off; an automation is a standing brief that spawns tasks on a schedule. Use automations only when asked for recurring content.
./ait.mjs automation create --name "Weekly changelog roundup" \
--instructions "Summarize this week's merged changes as a friendly post." \
--output-types x --schedule '{"type":"cron","value":"0 9 * * 1"}'
./ait.mjs automation list
./ait.mjs automation get <automationId>
./ait.mjs automation update <automationId> --name "Renamed"
./ait.mjs automation run <automationId> # trigger now; returns the spawned task to poll
automation run returns a task — poll it with task get. If the response includes "alreadyRunning": true, an earlier run is still in flight; poll that task instead of triggering again.
Media you upload and files bundled with skills live in the workspace file browser, organized into folders. You can read and reorganize it:
./ait.mjs files list --search brand # find files
./ait.mjs files download-url <fileId> # time-limited URL — fetch it yourself
./ait.mjs folders list # every folder; --root for top-level only
./ait.mjs folders list --parent <folderId> # children of one folder
Mutations, for when the operator asks you to tidy up or file something you produced:
./ait.mjs folders create --name "Q3 launch" [--parent <folderId>]
./ait.mjs folders rename <folderId> --name "Q3 launch (final)"
./ait.mjs files move <fileId> --folder <folderId> # omit --folder to move it to the root
./ait.mjs files copy <fileId> --folder <folderId> # duplicate, leaving the original
folders delete is destructive — it takes every file and nested folder with it. See below.
Reorganizing someone's files is a real change to their workspace, not a cleanup you should do unprompted. Move and rename what the operator asked about; leave the rest.
Five commands permanently remove data and refuse to run without --yes:
| Command | Destroys |
|---|---|
task delete <taskId> --yes | the task and everything attached to it |
task delete-content <taskId> <contentItemId> --yes | one content item |
task clear-content <taskId> --yes | every content item on the task |
automation delete <automationId> --yes | the automation and every task it ever spawned, including published ones |
folders delete <folderId> --yes | the folder and every file and folder inside it |
automation delete is the widest of these by far: it hard-deletes the automation's whole task history, not just the schedule. If the operator wants the automation to stop running, they almost certainly want it paused or updated, not deleted — ask before reaching for this.
None of this is undoable and there is no trash can. Rules:
--yes only when the operator named that deletion in their instructions. "Clean up," "start over," or a rejection comment asking for a rewrite is not a deletion request.update-content revises in place; a reviewer rejecting a task is the normal way to kill unwanted content.The API key's scopes bound what you can do (enforced server-side):
tasks:read — task list / get / links / next-reviewtasks:write — task create / claim / claim-next / complete / comment / approve / reject / publish / deletecontent:write — task add-content / update-content / delete-content / clear-content, and task submit (which attaches content, so it needs tasks:write and content:write)files:read — files list / download-url, folders listfiles:write — upload-url, files move / copy, folders create / rename / deleteskills:read — skills list / getautomations:read / automations:write — automation commands, including automation runWorkspace roles apply on top. Any member — including a viewer — can create and work tasks, but a viewer's tasks always require review (--no-review is ignored for them). Creating or deleting automations requires an admin; approving and publishing require a reviewer or admin. A 403/missing required scope error names what's missing — report it to the operator; do not retry.
Ask for the narrowest key that does the job. A submitter needs tasks:read, tasks:write, content:write, and skills:read; a status watcher needs only tasks:read. A key without a scope simply can't reach those tools — that's the point.
failed or rejected, surface the reviewer's comment and the task id to the operator — do not silently retry more than once.--yes. See Destructive operations.