SKILL.md
Actions
Three things share the name, and telling them apart is most of the work:
- An action is one operation of an integration against one object:
"NetSuite upsert customer", "Enrich name". The catalogue is the
actionsresource; each has anidand anintegrationName. - An action column is an action wired into a table: which action, its
params, and theinputmapping that says which of the row's columns feed it. It lives in the table'sobjectsColumns(the tables skill covers adding and changing one). - An action run is one execution of one action column against one row.
Its status is one of
ACTIVE,DELAYED,WAITING,COMPLETED,FAILED. Runs are kept for 14 days.
You reach them through the RevOS MCP server's generic tools:
| Call | What it does |
|---|---|
api_read { "resource": "actions", "params": { "fields": "id,name,integrationName" } } | the catalogue; add "onlyUsed": "true" for the ones a table uses |
api_read { "resource": "actions", "id": "<action id>" } | one action: name, description, integrationName |
api_read { "resource": "actions", "id": "<action id>", "method": "errorInstructions" } | what this organization wrote about that action's errors |
api_read { "resource": "action-runs", "params": { … } } | list runs: filter, orderBy, fields, pageSize |
api_read { "resource": "action-runs", "id": "<run id>" } | one run, with its input and result |
api_write { "resource": "action-runs", "data": { "tableId": "…", "columnId": "…" } } | run an action column — see references/run.md first |
The calls in this skill are complete as written: go straight to them.
api_search and api_details are for when one of them is refused, or you
need a filter or field this skill doesn't name.
Something failed: start from the run
Most questions here are "why did this fail". Work in levels and stop at the first one that answers the question: a failure whose cause is already named in the run's own result doesn't need the table's schema, and pulling it anyway costs the user a slower answer buried in detail they didn't ask for.
Find the run. The user may hand you a run id straight from the
action-run drawer in the UI. More often they name a record — "deal
31415926535", a company, a contact. That id is the row's objectId: a row
usually carries the id its record has in the source system. One call then
returns the newest runs for it with their payloads — there is no need to
read the run by id afterwards — and the newest one tells you whether it is
still failing:
api_read { "resource": "action-runs",
"params": { "filter": "objectId == \"<record id>\"",
"orderBy": "createdAt desc", "pageSize": 3 } }
With neither a run id nor a record, list the failures without the payloads:
api_read { "resource": "action-runs",
"params": { "filter": "status == \"FAILED\" && modelId == \"<table id>\"",
"orderBy": "createdAt desc", "pageSize": 10,
"fields": "id,actionId,columnId,modelId,objectId,objectName,status,attemptsMade,createdAt" } }
modelId is the table's id. Narrow with columnId or objectId when you
know them.
Read the run by id. One call, and it carries the spine as well as both
payloads: actionId, columnId, modelId, objectId, objectName,
status, attemptsMade, input and result.
Read the organization's error instructions for the run's actionId.
This is the next call after the run, every time, however clear the error
already looks: api_read on actions with that id and
method: "errorInstructions", described
below. They often map the error
message straight to its cause and fix, and say who fixes it.
Where the error lives. There is no separate error column. result
holds the whole run result, a union discriminated by metadata.success. On
a failure it carries:
metadata.error.message— the message to trust and to quote;metadata.error.aiMessage— a generated rewording, present only if the explain-error flow ran. Never present it as the system's own error;error— a normalized object withmessage,code,details,isUnrecoverableandretryAfter.
error.isUnrecoverable: true means the framework deliberately stopped
retrying: the condition is permanent (rejected credentials, a validation
the target system will never accept), so suggesting a re-run unchanged is
wrong.
A large share of failures are fully explained right here — credentials the
target system rejected, a rate limit, a permission, a validation message
that already names the field and the reason. When result, read together
with the organization's instructions, says something the user can act on,
that is the answer: quote it, say what to do, stop.
When the id doesn't resolve. A table cell goes on showing a failure
long after the run behind it was removed, so an id can name a run older
than the 14-day window. Say so and stop — no page of action-runs can
contain it. If the user quoted the error text, that text is still worth
reading back to them. Then offer what does work: re-run the action and
diagnose the fresh failure.
When the run alone doesn't answer — the error is about a value that was missing, empty or malformed, or the user asks where a value should have come from — go on to references/debug.md: the mapping, the lineage, and how to say where the fault is.
The organization's error instructions
Organizations write down what their actions' errors mean and how they are
fixed: what a particular NetSuite error really means for them, who fixes
what, how a link to their CRM is built. Read them once for each failed run
you diagnose, by the run's actionId:
api_read { "resource": "actions", "id": "<the run's actionId>", "method": "errorInstructions" }
It returns three texts, each null when nothing was written:
global— applies to every action in this organization;integration— every action of this action's integration;action— this action alone.
They add up rather than override one another: the action-level text assumes you've read the integration-level one.
They are where a convention that makes your obvious recommendation wrong
lives. When they give wording, a link format, or an owner for the fix, use
theirs. If all three are null, the organization wrote nothing for this
action; carry on without them. If the read itself is refused, don't retry:
carry on from the run, and say you couldn't read them.
Running an action
Running acts on connected systems — it creates and updates records in the
customer's CRM or ERP — so only do it when the user asks, and read
references/run.md before the first api_write on
action-runs.
When the user wants one record synced now — they give an id or a link and say "sync it", "push it", "force it" — the source has to be pulled before the action runs, or it sends the old values again. That is its own procedure: references/sync-now.md.
Adding or changing an action column
That is a change to the table, not to the action: the tables skill, its
column reference. The catalogue here tells you which actionId to use and,
through the action's description, what input it expects.
Reporting back
Lead with the cause in the user's terms, then what to do about it and who
does it. Quote metadata.error.message verbatim when the fix involves a
third party — a paraphrased vendor error is worse than useless when the
user takes it to that vendor's support. Name the row by objectName, not
by id. Don't dump input or result.