Executions

Read executions, follow their lineage, preview calls, rerun, replay, and cancel.

ActionExecution

FieldTypeMeaning
idID!Global identifier for the node(id) query.
executionIdUUID!Unique identifier. Automatic executions derive it from the event and action version; reruns use the executionId supplied to rerunExecution.
actionIdUUID!Identifier of the action this execution ran.
actionVersionInt!The action version pinned when the execution was created.
actionAction!That pinned version, fetched when selected.
rerunOfActionExecutionThe source execution of a manual rerun. Null for automatic executions.
replayOfId, replayOfUUID, ActionExecutionThe execution a replay reran. Null for an execution that is not a replay.
replayActionReplayHow a replay ran. Null for an execution that is not a replay.
parentExecutionIdUUIDThe execution that called this tool, such as an agent or another tool. Null for an execution not called by an execution.
parentInvocationNumberIntThe parent invocation that made the call. Null for an execution not called by an execution.
callSequenceIntThe call's position among the calls its parent invocation made, from 1. Null when the caller did not number its calls.
callPath[ActionCallFrame!]!The call's position in its call tree, one frame per call from the top of the tree down to this one. Empty for an execution not called by an execution. See Call paths.
parentActionExecutionThe calling execution, subject to your read permissions.
children(invocationNumber, sort, first, after)ActionExecutionConnection!Committed tool calls made by this execution, including native tool calls, ordered by creation time. Pass invocationNumber to restrict to one invocation's calls.
eventActionEventThe event that matched the action, or the tool call, delivered as input. Null for a session turn.
matchedTriggers[Int!]!Positions in the pinned version's definition.triggers of every trigger that matched the event, in ascending order; on an intake record, those that failed. Empty for other manual reruns, which do not evaluate triggers.
turnActionSessionTurnFor an execution that runs a session turn: that turn, with its number, status, inputs, and what it committed. For a replay of one, the turn it replays. See Session turns.
sessionActionSessionFor an execution that runs a session turn: the session, at its current version.
intakeActionIntakeSet on an intake record: the record of an event that failed to start or resume a session, or of a rerun of such a record. Null for any other execution.
draftActionDefinitionFor an execution an MCP server's invoke previewed of an unpublished definition: the definition it ran, with its tools and skills resolved as a write resolves them. Its actionVersion is then the action's version when it ran. Null when a stored version ran, which is actionVersion, and for any other execution. An execution with a draft is always preview, and is never rerun or replayed.
statusActionExecutionStatus!See Execution status.
overdueBoolean!True while a RUNNING or ACCEPTED invocation is past its deadline.
deadlineTimestamp!When the whole execution expires, from the action's timeout setting.
nextInvocationAtTimestampWhen a RETRY_SCHEDULED execution becomes eligible again.
invocationActionInvocationThe current invocation. Null until the first invocation starts.
skillLoadExecutionId, skillLoadUUID, ActionExecutionFor a call to a tool a skill made available, the load that made it available. Null otherwise. See Tools a skill makes available.
previewBoolean!True for every execution whose writes were discarded, so that nothing it did committed: a replay, a request to an MCP server in preview mode, an agent's execution in preview mode, an execution started in preview mode, a preview, of an MCP program or of an execution an MCP server's invoke ran, with the invoke call that started a preview agent, a rerun of any of these, and every call made beneath them. Filter executions on preview to find what committed nothing. The marker is about business writes only: the execution's own record still commits, and so does a task's cancellation, such as a cancel_task or tasks/cancel request to an MCP server in preview mode, which takes effect; the cancelled execution's cancelledBy names the request.
writeSet[ActionWrittenRecord!]!For a preview call, each record version it would have written, in write order. Empty for any other execution.
writeSetOmittedInt!Writes left out of the end of writeSet because it exceeds the size an execution records. Zero when writeSet is complete.
writeSetUnreadableInt!Records left out of writeSet because you may not read them: a record is listed only when you could read it by its ID as it was written, and a deletion only when you could read the record it removed. Zero when you may read every record.
startedByActionExecutionStartHow the execution was started: by a client with startAction, or by another execution, such as an MCP server's request that created a task. Null for an execution that was triggered or called. See ActionExecutionStart.
cancelledByActionExecutionCancellationWho cancelled the execution: a client with cancelExecution; an MCP server's tasks/cancel request, with executionId and execution naming it; for an MCP request's execution, its client going away before the result, recorded as the request's principal; or Twisp stopping the server that ran the request, recorded as the principal twisp:actions. Null otherwise, including for an execution cancelled by closing its session.
rolledBackByExecutionIdUUIDFor a call made beneath a call that failed, whatever its own status: the failed call's execution, the innermost when several failed, such as a programmatic_tool_caller call or an MCP program. A failed call's writes roll back with everything beneath it, and its calls stay recorded. Also set on each call a failed invocation of a preview execution an MCP server started made, naming that execution. Null otherwise.
created, modifiedTimestamp!When the execution was first created, and the time of its last change.
versionInt!The current version number of this execution. Previous versions are tracked in history. Advances on every status change.
_recordIdUUID!Internal Twisp record identifier.
history(first, after)ActionExecutionConnection!History of changes to this execution, newest first.

ActionExecutionStart

FieldTypeMeaning
executionIdUUIDThe execution that started this one. Null for a client's start.
executionActionExecutionThat execution, subject to your read permissions.
invocationNumberIntThe starting execution's invocation that made the start. Null for a client's start.
sequenceIntThe start's position among the calls and starts that invocation made, from 1. Null for a client's start.
notStartedBoolean!True when a replay made the start. A replay starts nothing: the start is recorded, CANCELLED and preview, with its effective input, and was never queued.
actionIdUUIDThe action of the execution that started this one. Null for a client's start.
sessionIdStringThe session the starting execution ran in, such as the MCP session of the request that created a task. Null for a client's start and for a start made outside a session.
invokedBoolean!True when an MCP server's invoke tool made the start: at each invocation, the caller must still be allowed to invoke the started action.

ActionInvocation

Each new invocation replaces the previous one. Read earlier invocations through history.

FieldTypeMeaning
numberInt!Invocation number, counting from 1.
startedAtTimestamp!When the invocation started.
responseDeadlineTimestamp!When the handler must answer, from the action's responseTimeout setting.
completionDeadlineTimestampWhen an accepted invocation must report its outcome, from the action's completionTimeout setting. Null until acceptance.
externalIdStringCorrelation identifier supplied by a handler that accepted the work. Null otherwise.
outputValueThe handler's result. Null until the invocation succeeds or is accepted, and null for a session turn that ended with the session WAITING for approval.
errorActionErrorNull until the invocation fails.
dryRunBoolean!True when the invocation succeeded with its business effects rolled back, such as an invokeAction call in a @dryRun mutation.
uses[ActionOutputUse!]!Tool answers and skill guidance the handler reported supplying to this invocation. See Output uses.
tools[ActionToolReference!]!The action's tool references, each with the version it resolved to when the invocation started in actionVersion, followed by the tools skill loads made available to it. For a skill load, the tools the load made available. See Versions.
skills[ActionSkillReference!]!The action's skill references, each with the version it resolved to when the invocation started in actionVersion.
approvals[ActionApprovalRequest!]!The calls the invocation paused before, each waiting for a person's approval, when its session turn ended WAITING. For an agent in preview mode, the calls that would have waited, each marked grantedInPreview. See Approvals.
holdActionSessionHoldThe session turn the invocation held for a person's answer, when it failed with agent_refused or agent_unfinished. See Held turns.
transcriptLastSequenceInt!The number of the invocation's last transcript event, recorded with its outcome. See Transcripts.
transcript(first, after)ActionTranscriptEventConnection!The invocation's transcript in sequence order. See Transcripts. A call to a built-in model records its exchange with the provider here; see The call transcript.

ActionError

FieldTypeMeaning
codeString!Failure code, one of action_failure, action_response_timeout, action_completion_failure, action_interrupted, action_output_too_large, action_output_invalid (including output that fails the tool's output schema), action_transaction_cannot_wait, action_input_invalid, action_access_denied, action_graphql_error, action_cel_error, action_call_limit_exceeded, action_not_callable (including a call to a retired model version), for models, model_not_served, model_credential_missing, model_unauthorized, model_request_invalid, model_unavailable, for agents, agent_unfinished, agent_limit_exceeded, agent_refused, agent_runtime_unavailable, agent_approval_required, fixture_exhausted, model_request_rejected, model_access_denied, model_failed, or, on an intake record, session_intake_failed.
messageString!Description of the failure.
retryableBoolean!Whether the failure was classed as retryable. Stays true on an EXHAUSTED execution.
hintStringWhat the caller should change for a failed call to succeed, such as the path and expected shape of a schema violation or the hint passed to fail(). Null when there is none.
detailsValueStructured detail of the failure, such as the errors of a failed graphql call. Null when there is none. When the failure is an error of a tool call the execution made and did not handle, whose own record is not kept, details.cause describes that call: tool, actionId, actionVersion, callSequence, callPath (the call's position in its call tree, as a list of frames like callPath on the execution), and its error's code, message, hint, and details. A call that failed the same way names its own cause in its details.

Execution status

StatusTerminalMeaning
QUEUEDnoWaiting for its first invocation.
RUNNINGnoAn invocation has started and the handler has not answered.
ACCEPTEDnoThe handler accepted the work and will report its outcome asynchronously.
RETRY_SCHEDULEDnoThe last invocation failed with a retryable error or passed its deadline without an outcome. nextInvocationAt is set.
SUCCEEDEDyesThe handler returned a result, or no trigger failed in the rerun of an intake record.
FAILEDyesThe handler returned a non-retryable error, or an intake record's event failed to start or resume a session.
EXHAUSTEDyesThe invocation budget or deadline ran out.
CANCELLEDyesCancelled through cancelExecution, or a start a replay recorded without starting it.

Terminal executions never change status again. Only terminal executions can be rerun.

Listing and filtering

executions(where: {...}) accepts any combination of the following. Every supplied field must match. Results are ordered by creation time, newest first; pass sort: ASC for oldest first. Every list of executions, sessions, and memory records is newest first by default, as the rest of the API's time-ordered lists are; an execution's children stay in the order the calls were made, and a session's inputs in the order they were delivered. An action's executions takes the same filters without actionId, a session's turns are its turns, and the calls one execution made are its children.

FilterTypeMeaning
actionIdUUIDExecutions of one action.
eventIdUUIDExecutions created for one event.
sessionIdStringExecutions of one session: those that run its turns, or for an MCP server, those of the requests recorded in it. Requires actionId.
statusActionExecutionStatusExecutions in one status.
createdAfter, createdBeforeTimestampExclusive bounds on execution creation time, not on event time.
intakeBooleanWith true, only intake records. With false, none.
calledBooleanWith true, only executions another execution called, which have a parentExecutionId. With false, only those no execution called, such as calls of invokeAction, starts, and triggered runs.
previewBooleanWith true, only executions that committed nothing, which have preview set: previews, and those of a server or agent in preview mode, or of a replay. With false, only those that committed.
startedByExecutionIdUUIDExecutions one execution started, such as an MCP server's tasks. Cannot be combined with sessionId.
startedByInvocationNumberIntExecutions one invocation of the starting execution started. Requires startedByExecutionId.
query FailedExecutions($actionId: UUID!, $after: String) {
  actions {
    executions(where: { actionId: $actionId, status: FAILED }, first: 20, after: $after) {
      nodes {
        executionId
        created
        event {
          eventId
          occurredAt
        }
        invocation {
          number
          error {
            code
            message
          }
        }
      }
      pageInfo {
        endCursor
        hasNextPage
      }
    }
  }
}

Connections follow the pagination conventions. first is required and accepts 1 to 100. A filtered page can contain fewer nodes than first, or none, while hasNextPage is still true, so continue from pageInfo.endCursor until hasNextPage is false. Cursors are valid only with the same filter they were issued for.

History and exact versions

Every status change writes a new record version. history on an action or execution lists those versions newest first, and action(id, version) or execution(id, version) reads one exactly. Several history entries can share an invocation number, for example the start of an invocation and its outcome. event and invocation.output on a history node are the payloads as they were at that version.

Lineage

lineage on an execution walks every execution it caused, or that caused it, from any execution in a call tree. Every call is an edge, whatever the caller and the called tool, skill, or native tool, so the walk mirrors exactly what ran. So is every start an execution made, such as an MCP server's request that created a task: a START edge leads from the starting execution to the execution it started, which runs on its own, with calls of its own.

ArgumentTypeMeaning
directionActionLineageDirectionDOWNSTREAM (the default) lists every call and start beneath the execution at any depth. UPSTREAM lists its callers to the top, and on through the execution that started the top, if any.
firstInt!Number of nodes to return, 1 to 100.
afterStringCursor to continue from.

Nodes are listed depth first: downstream, each call follows its caller, and a caller's calls and starts are in the order it made them, each followed by its own calls. Upstream, the last node is the top of the call tree, or of the tree of the execution that started it. A client's start has no starting execution, so its upstream walk ends at the top of its own tree. Each execution appears once, and the execution you start from is not listed.

FieldTypeMeaning
executionIdUUID!Identifier of the execution.
executionActionExecutionThe execution, or null when you may not read it.
depthInt!Edges between the starting execution and this one, from 1.
viaActionLineageEdgeKind!CALL or START. CAUSE is reserved for executions started by changes another execution wrote, and is not yet returned.
viaExecutionIdUUID!The execution this one was reached from: downstream, its caller or the execution that started it; upstream, the call it made or the execution it started.
viaExecutionActionExecutionThe execution identified by viaExecutionId, subject to your read permissions.

You must be able to read the execution you start from. The walk continues through executions you may not read: each is listed with its identifier and edge, and a null execution, so the shape of the tree is complete without disclosing their content. Each page reflects the executions committed when it is read. Cursors are valid only for the walk they were issued for.

query CallTree($id: UUID!, $after: String) {
  actions {
    execution(id: $id) {
      lineage(first: 50, after: $after) {
        nodes {
          executionId
          depth
          viaExecutionId
          execution {
            actionId
            status
            invocation {
              output
            }
          }
        }
        pageInfo {
          endCursor
          hasNextPage
        }
      }
    }
  }
}

Call paths

callPath on an execution gives its position in its call tree: one ActionCallFrame for each call on the path from the top of the tree down to the execution, which is the last frame. The number of frames is the execution's depth below the top, and a call's path is its caller's path followed by its own frame. A downstream walk lists calls in call path order.

FieldTypeMeaning
invocationNumberInt!The caller's invocation that made the call.
callSequenceIntThe call's position among the calls that invocation made, from 1. Null when the caller did not number its calls.
createdTimestamp!When the call was made. A caller's calls that it did not number are ordered by created, then by executionId.
executionIdUUID!Identifier of the call's execution.
executionActionExecutionThe call's execution, subject to your read permissions.

Preview calls

An agent whose config.mode is preview runs on real events and does everything it would do, but changes nothing. Each of its turns runs every call as a live turn would, and each call sees the writes of the calls before it, but the turn's writes are discarded when it ends. The agent's own execution, transcript, and session still commit, so a preview session evolves as a live one would; the execution is marked preview, as its calls are. To let the agent act, update its definition's config.mode to live.

The memory and session history a preview turn writes are kept for audit, marked preview. Later turns in preview mode read them, as live turns read their own, but a live turn never does: once the agent goes live, it does not act on what it learned of effects that never happened. A preview turn never changes a memory record a live turn wrote. It replaces one with a record of its own whose replaces names it, and deletes one with a record of its own of status DELETED whose replaces names it. Turns in preview mode no longer recall the record it names; live turns still recall it, unchanged. Action.memories lists these records with preview: true, so you see exactly what the preview turn would have changed.

Each call a preview turn made, and each call beneath one, is recorded with preview: true. A call that succeeded lists in writeSet every record version it would have written, including those of the tool session it started and of the calls beneath it. A call that failed leaves no writes of its own, as in a live run; it lists only the writes of the tool session it started, which stay in the turn. The calls beneath it are still recorded, each listing what it wrote and marked with rolledBackByExecutionId. A programmatic_tool_caller or subagent call is such a call: it lists the writes of the calls made under it, or none when it failed. A writeSet lists only the records you may read, each as a read of it by its ID would be authorized; writeSetUnreadable counts the rest, so a write set never shows you a record you could not read.

A call to a tool that requires approval does not wait in preview mode: it runs at once, with the agent's input, as if a person had approved it. The execution's invocation.approvals lists the request a live turn would have made, with grantedInPreview: true and an empty interruptId, so the record shows that no person approved the call. The session does not wait, and the request cannot be answered.

A tool session that a preview call starts is discarded with the turn's other writes. Later calls in the same turn still find it. A preview call can read other tenants, as admin queries do, but it cannot write to them, as admin mutations and branch do, because the turn could not discard what they write. Nor can it run an operation that acts outside the ledger, such as a warehouse statement or export. Such a call fails with a call error that the tool can handle.

FieldTypeMeaning
recordTypeString!Record name, for example Journal.
recordIdUUID!Row identifier of the record.
recordVersionInt!Version of the record the write created, or, for a deletion, the version it removed.
deletedBoolean!True when the write deleted the record.
dataValueThe record after the write, in the form an event's data takes. Null for a deletion.
query PreviewCalls($id: UUID!) {
  actions {
    execution(id: $id) {
      children(first: 50) {
        nodes {
          actionId
          preview
          invocation {
            output
          }
          writeSet {
            recordType
            recordId
            recordVersion
            deleted
            data
          }
          writeSetOmitted
        }
      }
    }
  }
}

Payloads

event is the event as an ActionEvent object, and invocation.output is whatever the handler returned as a Value. Records and outputs are stored with their types; the Value scalar renders them as JSON only when you read them.

KindJSON rendering
Integers, unsigned integersExact numbers, including values above 2^53.
UUIDCanonical lowercase string.
Money"<amount> <currency>", for example "9.99 USD".
DecimalExact decimal string, for example "0.125".
TimestampRFC 3339 UTC string.
DateYYYY-MM-DD.
DurationGo duration string, for example "1h30m0s".
BytesStandard base64 string.
Strings, booleans, null, lists, objectsAs themselves. Object keys are sorted.

Integers arrive with their exact digits. A JSON parser that reads every number as a double, as JavaScript's JSON.parse does, still rounds integers above 2^53; use a parser that preserves big integers when you rely on such values.

query ExecutionPayloads($id: UUID!, $version: Int) {
  actions {
    execution(id: $id, version: $version) {
      status
      event {
        eventType
        recordType
        recordVersion
        data
        previous
      }
      invocation {
        output
      }
    }
  }
}

The event

The execution's event, delivered to the handler as its input and bound to event in your triggers, is this event. In a trigger, event exposes the same fields with their native types: event.id is a UUID, event.type a string, event.occurred_at a timestamp, and event.record_version an unsigned integer.

Field (event key)TypeMeaning
eventId (id)UUID!Stable identifier of the committed change, the tool call's executionId, or an identifier derived from a schedule's firing.
eventType (type)String!<record>.inserted, <record>.updated, or <record>.deleted, for example journal.updated; action.invoked for a tool call; schedule.fired for a firing of a schedule.
occurredAt (occurred_at)Timestamp!Commit time of the change, the start of the calling transaction, or a firing's scheduled time.
recordType (record_type)StringRecord name, for example Journal or action_session. Null for a tool call or a firing.
recordId (record_id)UUIDRow identifier of the changed record. Null for a tool call or a firing.
recordVersion (record_version)IntVersion of the changed record. Null for a tool call or a firing.
dataValueThe record after the change, the tool call's effective input, or a firing's {trigger, scheduled_at} or, for the session's clock, {schedule, scheduled_at, missed}. Null for a physical deletion.
previousValueThe record before the change. Null for an insert or a firing.
recordDatabaseEntityNot a trigger key. The changed record at recordVersion, for an Account, AccountSet, Balance, Entry, Journal, KVValue, Transaction, or TranCode. Null for other records, a tool call, and a firing.

Records inside data and previous use snake_case field names such as journal_id and dr_balance, and every field keeps its type: UUIDs are UUIDs, money is money, timestamps and dates keep their kinds, enums are their numeric values, and a KV record's value is the same typed document that was written. Each record also carries version, its record version. A structured field that was never set, such as a balance with no encumbrance or a record with no entry_id, is absent rather than zero-valued, so guard it in a trigger with has(event.data.encumbrance) or read it with event.data.?encumbrance.dr_balance.orValue(money("0", "USD")); plain strings, numbers, and booleans that were never set read as "", 0, and false. Soft deletes such as deleteJournal produce an updated event.

Actions records

Changes to actions, executions, transcripts, sessions, session inputs, and agent memory are events too, so an action can respond to them. Their record types are snake_case names, and the event type is the record type and the change, such as action_execution.updated. data and previous hold the record as the API returns it:

record_typeEvent typesdata and previous
actionaction.inserted, action.updated, action.deletedThe action. Deleting an action is an action.updated event.
action_executionaction_execution.inserted, .updated, .deletedThe execution, without its event, the event of each of its session.inputs, or its write set (write_set, write_set_omitted, and write_set_unreadable). Read the execution, or the session's inputs, for them.
action_execution_eventaction_execution_event.inserted, .updated, .deletedA transcript event's keys only: action_id, execution_id, invocation_number, sequence, kind, call_execution_id, and session_id. Read the transcript for its content.
action_sessionaction_session.inserted, .updated, .deletedThe session. A large state is left out; read the session for it.
action_session_inputaction_session_input.inserted, .updated, .deletedThe session input, without its event. Read the session's inputs for it.
agent_memoryagent_memory.inserted, .updated, .deletedThe memory record.

An answer to an approval request is an action_session.responded event; see Approvals.

An update that changes nothing these records show but version, modified, and modified_by, such as a session taking its next inputs, is not an event. Triggers on these records cannot loop; see Loops.

Loops

Triggers do not loop. A chain of actions that trigger one another stops before it repeats an action, and after 8 links:

  • A change to an action's own execution never triggers it.
  • Chains. A change an execution made, itself or through the tools it called, has a chain: the executions whose changes led to it, oldest first. Each one led to the next because a change it made matched a trigger of the next one's action, or because it started the next one. The last one made the change. A change to a session has the chain of the event that started the session, followed by a link for the session's action, so a change to an action's own session never triggers it either.
  • No repeats. A trigger never runs its action for a change whose chain includes that action. When A's change triggers B and B's change matches a trigger of A, A does not run, and nothing is recorded.
  • Depth limit. A change whose chain holds 8 links runs no action. Each action whose triggers match it gets an intake record with stage DEPTH_LIMIT instead.
  • What has a chain. Changes to executions, transcript events, sessions, session inputs, and agent memory have one. Changes to actions and to your own records do not, so a loop through them, such as an action that writes journals and triggers on journals, is not stopped. Write such a trigger so that it does not match the action's own writes.

Intake records

An event that matches a session trigger starts or resumes a session. When that fails on the event's data, for example because a start.input expression reads a field the event lacks, the mapped input fails inputSchema, or session.id evaluates to an empty string, the event is recorded as an intake record: a FAILED execution of the action with intake set. So is an event that matches any of an action's triggers at the end of a chain too long to run (see Loops). No handler runs, and the record is not retried: the failure is a fact about the event and the definition. Intake records are listed, filtered, and alarmed on as any failed execution is, and a record can be rerun once the definition accepts its event.

  • One record per event and action version. The record lists every trigger of that version that failed for the event, in intake.failures, and their positions in matchedTriggers. event is the event. invocation is invocation 1, whose error has the code session_intake_failed, is not retryable, and summarizes the failures in its message. A repeated delivery of the event finds the same record.
  • Partial failures are recorded. A record is written when any of the action's triggers failed for the event, even when another of its triggers started or resumed a session with it. intake.sessionId then names the session a start or resume.id trigger reached, including one the event is held for until it starts. A resume.input delivery, which can reach many sessions, is not named.
  • Not failures. A trigger predicate that fails to evaluate is a non-match, as it is for any trigger. A resume key that fails is a failure only for an event the trigger's predicate can select: one it selects, or cannot decide without the session. An event the predicate rejects, or fails on because the event lacks a field the predicate reads, is not the trigger's event. A resume event that the triggers of the version a session keeps do not match is not delivered to that session. An event for a closed session is not delivered. A resume event for a session that does not exist yet waits for it for 24 hours, and is delivered if the session starts in that time. None of these writes an intake record.

ActionIntake describes what failed:

FieldTypeMeaning
failures[ActionIntakeFailure!]!Each trigger that failed, in trigger order. Empty on a SUCCEEDED rerun.
sessionIdStringThe session a failure names, such as the open session of an INPUT_CONFLICT, or else the session another of the action's triggers reached with the event by a start or resume.id: started, resumed, or held the event for until it starts. A resume.input delivery, which can reach many sessions, is not named. On a SUCCEEDED rerun, the session the rerun reached. Null when there is none.

Each ActionIntakeFailure is one trigger's failure:

FieldTypeMeaning
triggerInt!Position in the action version's definition.triggers.
triggerNameStringThe trigger's name, or null if it has none.
stageActionIntakeStage!The step that failed.
messageString!What failed.

ActionIntakeStage names the step that failed:

StageMeaning
START_MAPPINGA start.input expression failed, such as on a field the event lacks.
INPUT_SCHEMAThe mapped input does not satisfy session.inputSchema, or is too large.
SESSION_IDsession.id failed, or is not a nonempty string within the size limit.
SESSION_ENDSsession.ends failed, or did not produce a timestamp, for the new session.
INPUT_CONFLICTTwo start triggers map the session to different input, or a start's input differs from that of the open session it names.
PINNED_VERSIONA start names an open session that keeps another version of the action, and matches neither a start nor a resume trigger of that version.
RESUME_KEYA resume trigger's resume.id or resume.input value failed or is missing for an event its predicate selects, or cannot decide without the session.
DEPTH_LIMITThe change was made at the end of a chain of 8 executions, the most a chain of triggers runs; see Loops.
query IntakeRecords($actionId: UUID!, $after: String) {
  actions {
    action(id: $actionId) {
      executions(where: { intake: true, status: FAILED }, first: 20, after: $after) {
        nodes {
          executionId
          actionVersion
          event {
            eventId
            eventType
            occurredAt
          }
          intake {
            sessionId
            failures {
              trigger
              triggerName
              stage
              message
            }
          }
        }
        pageInfo {
          endCursor
          hasNextPage
        }
      }
    }
  }
}

mutation RerunIntake($id: UUID!, $executionId: UUID!) {
  actions {
    rerunExecution(id: $id, input: { executionId: $executionId }) {
      executionId
      status
      actionVersion
      intake {
        sessionId
      }
    }
  }
}

Manual reruns

rerunExecution creates a new execution for the same event from a source that is SUCCEEDED, FAILED, EXHAUSTED, or CANCELLED. A replay is not rerun; replay its original again instead. Nor is a draft's preview, an execution with a draft: preview the draft again. The new execution starts QUEUED with a fresh invocation and time budget and reuses the source's event unchanged. Triggers are not re-evaluated, so the rerun's matchedTriggers is empty. An intake record is the exception; see Rerunning an intake record.

A rerun runs the action again, so it needs the INVOKE permission on the action, besides SELECT and INSERT on its execution records, and it cannot be made from inside an execution. A rerun of an execution that has no caller, such as a triggered execution, runs under the action's own policies as the original did, whether or not the action is still active. It reads those policies afresh when it starts, so after an update that adds an operation the original was denied, rerunning the failed execution runs under the update's policies at once. A session turn and an MCP request are not rerun: a turn runs only from its session, and a request only when its endpoint serves it; replay them instead.

Input fieldTypeMeaning
executionIdUUID!Identifier for the new execution, chosen by you. Repeating a request with the same executionId, source, and actionVersion returns the same rerun. Reusing it with different values is an error.
actionVersionIntAction version to pin. Omit to reuse the source's pinned version or, for an intake record, to use the action's current version. An intake record can be rerun only at a version whose triggers are still published; see Rerunning an intake record.
mutation Rerun($id: UUID!, $executionId: UUID!) {
  actions {
    rerunExecution(id: $id, input: { executionId: $executionId }) {
      executionId
      status
      actionVersion
      rerunOf {
        executionId
        status
      }
    }
  }
}

Automatic retries keep the same executionId; a rerun always gets the one you supplied, so downstream deduplication on executionId distinguishes deliberate reruns from retries.

A rerun of a call, whoever made it, or of a started execution is a new start by you: it runs under your authority, in preview mode when the original was a preview, its caller is you, and its startedBy names no starting execution. As for startAction, the action must be callable at the rerun's version: published, enabled, with a tool block, and the input must satisfy its schema. A call to a tool with a session is not rerun, since a start has no call for the session to record; call the tool again instead.

Rerunning an intake record

A rerun of an intake record tries the recorded event again: it evaluates every trigger of this action, and no other action's, at actionVersion, and starts or resumes sessions as the event would have. No handler runs. The rerun is itself an intake record. It starts QUEUED, and is RUNNING while the event is tried, when it cannot be cancelled. It then becomes SUCCEEDED when no trigger failed, with invocation 1 carrying no error and no output, or FAILED, with its own intake.failures, matchedTriggers, and session_intake_failed error.

  • The version. actionVersion defaults to the action's current version, since the fix is usually a new version. An explicit actionVersion must be the current version or a replaced version whose triggers are still published: one that sessions still keep, or one replaced less than 24 hours ago. Any other version is refused. Repeating the request with the same executionId returns the rerun it made, at the version it resolved then, even if the action's current version has changed since.
  • The session reached. A SUCCEEDED rerun's intake.sessionId names the session it reached, as a record does. It is null when the event reached no session: no trigger of the version selects it, its session is closed, or the version the session keeps does not match it. A session keeps its version's triggers, so a resume key fixed in a new version does not reach sessions that keep the old one.
  • Nothing is delivered twice. An event a session already received, from the original delivery or an earlier rerun, is not delivered to it again.
  • A session's start does not move. A session a rerun starts keeps the event's commit time as startedAt, so its endsAt may already have passed; its first turn is then final. A rerun never moves the start or end of a session another event already started: its start is delivered to that session as an input.
  • The rerun runs the recorded event. Fixing the data the event carried, such as an account's metadata, does not by itself make a rerun succeed; that needs a definition that accepts the event as it was recorded.
  • Waiting resume events expire. Resume events that arrived before their session existed wait for it for 24 hours. A rerun later than that starts the session without them.
  • Enabled session actions only. The action must be enabled and not deleted, and the version must have a session. A rerun queued before its action was disabled or deleted ends FAILED without running, with the code session_intake_failed and a message saying it did not run. An intake record cannot be replayed.

Replays

replayExecution reruns a recorded agent turn with another version of the agent, or with an unpublished definition of it, and records the result as a new execution beside the original, so you can compare the two before you rely on the change. The source must be a finished execution of a kind agent action, or a request's execution of an MCP server. It cannot be a call that another execution made, including a tool's or an agent's call to an agent, another replay, or a draft's preview, an execution with a draft. An agent that a client called directly with invokeAction or started with startAction can be replayed, and the replay receives the call's input.

A replay needs the INVOKE permission on the action it replays, whatever its source. A replay of an execution that had a caller, a client's call or start, or an MCP request, runs under both you and the principal it replays, whose policies are evaluated with the claims it recorded, then the action's policies: it can do nothing either of you could not. A replay of a triggered turn or a session turn runs under the agent's own policies, as the turn did. A replay runs as you, so your principal needs a client in the tenant; without one the replay fails with action_access_denied.

A tool can request a replay too, through the graphql tool. The replay then also runs under every action on the requesting tool's call path, such as the MCP server that called the tool and the tool itself, before the principal it replays: it can do nothing the requesting tool could not, even when you could. Each invocation checks again that you are still authorized, that the credential you requested it with has not expired, and that the chain may still invoke the replayed action. replay.requestedBy names the requesting execution. A program, such as an MCP server's program tool or programmatic tool calling, cannot request a replay.

The replay receives what the original turn received: its event or, for a session turn, the same inputs and the session checkpoint the turn started from, even if later turns have moved the session on. Its tools run as they would live, and each call sees the writes of the calls before it, but every write is discarded when the turn ends, as for a preview agent. Each call is recorded with preview: true and lists in writeSet the records it would have written. The replay has its own transcript, calls, and output uses, which you compare with the original's.

A replay is never part of the live session. It admits no inputs, leaves the session and the agent's memory unchanged, and commits no business writes. For a session turn, replay.session shows how the turn would have changed the session, to compare with the session version the original turn committed.

A replay calls the tool and skill versions the original turn used: a reference without a version resolves to the version the original invocation recorded in its tools and skills, and to the latest version only for a tool or skill the original did not use. A pinned reference, in the replayed version or in definition, uses its version. A replay's tools read the data current when it runs, not the data the original turn saw, and replay.data is CURRENT to say so. Records the original turn read may have changed since. The replay's invocation.created is the snapshot it read. While it runs, the replay's writes conflict with other writes to the same records as any transaction's writes do, although they never commit. A write that meets a record the replay has written fails with a write conflict and can be retried once the replay's turn ends. A replay that meets another transaction's write, uncommitted or committed after the replay's invocation started, fails that invocation with a write conflict, and the invocation is retried with the replayed version's execution settings.

The replay starts QUEUED and runs as any execution does, with the replayed version's execution settings. Its triggers are not evaluated, so matchedTriggers is empty.

Input fieldTypeMeaning
executionIdUUID!Identifier for the new execution, chosen by you. Repeating a request with the same executionId, source, and other fields returns the same replay. Reusing it with different values is an error.
actionVersionIntVersion of the agent to run. Omit to run the source's pinned version. A session turn is replayed only by a version with a session, and a turn without one only by a version without one.
definitionActionDefinitionInputAn unpublished definition of the agent to run instead of a stored version: replay a change before you write it. It is a whole definition, with kind and config, not a patch. It is validated as createAction validates a definition, and its tools and skills resolve as that write would resolve them. Running one requires permission to update the action, and the replay's actionVersion is the agent's version when you requested it. Cannot be combined with actionVersion.
invocationNumberIntThe source invocation whose turn is replayed. Omit for its last invocation.
modelActionReplayModelLIVE (default): the model the replayed version configures. RECORDED: the model outputs the source invocation recorded, in order for each purpose, so a change of tools or skills is compared without the model's variability. A RECORDED replay that needs more outputs of a purpose than were recorded fails with fixture_exhausted.

ActionReplay describes how a replay ran:

FieldTypeMeaning
invocationNumberInt!The source invocation whose turn was replayed.
modelActionReplayModel!What answered the replay's model calls.
dataActionReplayData!CURRENT: the replay's tools read the data current when it ran.
sessionActionReplaySessionFor a replay of a session turn that succeeded: the status, reason, and state the turn would have given the session. It was not applied.
definitionActionDefinitionFor a replay of an unpublished definition: the definition the replay ran. Its invocation's tools and skills name the versions it called. Null when the replay ran a stored version.
requestedByActionReplayRequesterFor a replay a tool requested: the requesting execution's executionId and execution, its invocationNumber, and its actionId. Null for a replay a client requested.
mutation Replay($id: UUID!, $executionId: UUID!, $version: Int!) {
  actions {
    replayExecution(
      id: $id
      input: { executionId: $executionId, actionVersion: $version, model: RECORDED }
    ) {
      executionId
      status
      replayOfId
    }
  }
}

query Compare($id: UUID!) {
  actions {
    execution(id: $id) {
      status
      invocation {
        output
      }
      replay {
        model
        data
        session {
          status
          state
        }
      }
      children(first: 50) {
        nodes {
          actionId
          invocation {
            output
          }
          writeSet {
            recordType
            recordId
            data
          }
        }
      }
      replayOf {
        invocation {
          output
        }
      }
    }
  }
}

A fixture model can also replay recorded outputs outside a replay: {"provider": "fixture", "replay": {"execution_id": "...", "invocation_number": 1}} in an agent's config answers its model calls with the outputs recorded in that invocation of an execution of the same agent.

Cancellation

cancelExecution cancels an execution that has not finished, and records you in its cancelledBy. When an execution cancels it, such as an MCP server's tasks/cancel request, cancelledBy names that execution too. You need permission to update the execution.

  • QUEUED, RETRY_SCHEDULED, or ACCEPTED. The execution is CANCELLED at once. It runs no further invocation, and a later completion report for an accepted invocation is ignored.
  • RUNNING. An invocation that has only just begun is cancelled at once: the execution is returned CANCELLED, and the invocation's outcome cannot commit. Otherwise the execution is returned still RUNNING, with its cancellation requested. Once the cancellation commits, the running invocation's outcome never commits, so its writes roll back; its handler is stopped, and an agent stops at its next step. The execution becomes CANCELLED, with cancelledBy set, when the invocation ends, within its responseTimeout. Read it again to see it CANCELLED. Work an invocation started outside Twisp, such as a request to another system, is not undone.
  • Finishing. If the invocation's outcome commits at the same moment, the cancellation fails with a retriable conflict, and retrying it returns the execution with its outcome. A cancellation that commits is always honoured: either the outcome or the cancellation wins, never both.
  • Finished. A terminal execution is returned unchanged.

An execution processing a session turn cannot be cancelled; close its session instead. Cancelling does not stop new matches; disable the action for that.

mutation Cancel($id: UUID!) {
  actions {
    cancelExecution(id: $id) {
      executionId
      status
    }
  }
}
Previous
Memory and History