CoDNA Request access
Contents

Docs Decisions

0030 · A call's answer runs the next step: the same automation, given the answer and what it asked to be given back

Status: Accepted · 2026-11-02. It extends the calls of 0028, whose answers reach no script today. The sandbox (0007) does not change: a script is still synchronous and runs in 100 ms, and the next step is a run of its own.

Context

With 0027, 0028 and 0029, an automation can be started by another app, read what it needs, write back to an API and remember what it did. One kind of flow still can't be written: one whose next step needs what the API answered.

  • A form is filled in. Make the contact in the CRM, then add it to the list for its plan. The second call needs the id the first one answered.
  • A deal is won. Make the invoice in the billing system, then post its link to Slack.
  • An issue is opened in Linear. If it was made, attach the customer's page to it; if the API said the team doesn't exist, tell the owner.
  • A page is created in Notion. Then add its blocks, which need the new page's id.

What exists can't do it:

  • The answer is recorded and reaches no script (0028).
  • A read happens before the run (0025), so a script can't read something made by a call in the same run.
  • Two automations, one making the call and one reading the record, would have to find each other's calls. Nothing fires on a call's answer.

n8n's answer is the next node on the canvas. Here, the script stays where the flow is written.

Decision

A call can ask for its answer

actions.call takes a third argument. With then, the call's answer comes back to the same automation, in a new run, with whatever then holds:

export default function ({ event, actions }) {
  if (event.type === 'call.answered') {
    const { answer, then } = event
    if (then.step === 'add-to-list' && answer.status === 201) {
      actions.call('crm', { method: 'POST', path: '/lists/' + then.list + '/members', body: { id: answer.data.id } })
    } else if (answer.error) {
      actions.notify('me', 'The contact for ' + then.email + ' was not made: ' + answer.error)
    }
    return
  }
  const form = event.request.body
  actions.call('crm', { method: 'POST', path: '/contacts', body: { email: form.email } },
    { then: { step: 'add-to-list', list: form.plan, email: form.email } })
}
  • then is any JSON, at most 16 KB, handed back as it was given. It carries what the next step needs from this one, so the script needn't keep it in its state (0029).
  • The next run is told event.type 'call.answered', and event.answer:
    • status, and data (an answer in JSON) or text (anything else), at most 1 MB;
    • error when there is no answer: the call failed for good, or was never made because a call before it failed. The call is in method, path and connection.
  • The run's inputs export is given the answer too, so a step can read a source about what was just made.
  • It is the same automation, as the same owner, with its current script and state. It is a run like any other: vetted, recorded, counted towards being switched off for failing, and able to call again with then.

When the next step runs

  • As soon as the call is finished, through the outbox. A run's calls to one connection still go in order (0028); each call's next step runs when that call is done, without waiting for the rest.
  • Every call that asked for its answer gets a next step, whether it was answered, failed, or never made. A flow that needs to clean up, or to tell someone, always has its chance.
  • A switched-off automation's next steps don't run. The call's record says its answer was not handed back, and why.
  • At most 10 steps in a chain. Each next step is one step further on, and event.step says how far. A call made at the tenth is refused, saying so. That bounds a script that asks for answers in a loop, as the cascade depth bounds one that writes the pages it fires on.

Trying it

  • A dry run of the first step shows each call with its then, and says its answer would run the next step.
  • The next step is tried with an answer given: run_automation with answer (such as { "status": 201, "data": { "id": 812 } }) and then. A dry run or a preview of it calls nothing, as any does. On the automation's page, Try it on an answer… takes them.
  • A real run by hand makes its calls, and their next steps run, as they would on any run.

What is kept

  • The next step's run record keeps the answer it was given, as event.answer, cut at 64 KB, for 30 days, as it keeps a request (0027). It also says which run's call it answers. A chain can be followed run to run on the automation's page.
  • The call's record says it ran a next step, and links to that run.

Consequences

  • The flows that need one call's answer for the next are one automation, and their steps read as one script. n8n remains for flows that branch across many systems, wait on people, or need a canvas to be understood.
  • A script now has several entry points. It branches on event.type and then.step, and a step it forgot to handle does nothing, saying nothing. The skill tells agents to write each step, and to try each with an answer before switching the automation on.
  • An API's answer drives what a script does next. The answer is data from a system its owner connected, and code acts on it, as code acts on a request (0027). A field such as a contact's name can hold anything the system holds, and a script that puts it in a message passes it on.
  • More runs: one per step. The 10-step limit, the 20 calls a run, and switching an automation off after repeated failures bound how many.

Alternatives rejected

  • await on a call inside the run: a run is 100 ms and synchronous, so a dry run can be safe and secrets stay out of the sandbox. What 0023 and 0025 rejected fetch for holds here too.
  • A second automation fired by answers, such as a trigger call.answered on a connection: the flow would be split over two scripts, which have to agree on what each answer is for. then keeps the flow in one script, and says what each answer is for.
  • Answers kept in the state (0029) for the next scheduled run: the flow would wait for a tick, and the state isn't meant to hold answers of up to 1 MB.
  • Next steps only for answers that succeeded: a flow could not tell anyone that its first step failed.
  • A graph of steps, as n8n draws: a workflow engine, which 2026-10-25's direction keeps CoDNA from becoming.

Settled when it was accepted

Tornike accepted it as proposed on 2026-11-02, settling:

  1. then is actions.call's third argument, and the same automation runs again with event.type 'call.answered'.
  2. The limits: then 16 KB, an answer handed back up to 1 MB, 10 steps in a chain.
  3. Every call that asked gets its next step, answered, failed or never made.
  4. A switched-off automation's next steps are dropped.

Still open

  • Waiting: "if the invoice isn't paid in 3 days, remind them", as a next step that runs later. It could be a then with a time.
  • Answers from reads in the same shape, for a read that should be made only after a call.

Building it

  1. The next step:
    • then on a call, kept with it;
    • the event when the call is finished, carrying its answer;
    • the run with event.answer, event.then and event.step, in inputs too, and the step limit;
    • the records linking a call and its next step;
    • trying a step with an answer, over MCP and on the automation's page;
    • the contract.
  2. Docs: automations.md, and the set-up-an-automation skill, with the form-to-CRM flow as its example.