---
title: Stepping through DaVinci flows
description: Explains how to navigate DaVinci flows in a JavaScript application by handling different node types (`continue`, `success`, `error`, `failure`) and their collectors.
component: orchsdks
page_id: orchsdks:davinci:usage/javascript/03-stepping-through-davinci-flows
canonical_url: https://developer.pingidentity.com/orchsdks/davinci/usage/javascript/03-stepping-through-davinci-flows.html
revdate: Fri, 9 Jan 2026 17:23:51 +0000
keywords: ["DaVinci", "JavaScript", "Flows", "Nodes", "Collectors", "Navigation"]
section_ids:
  starting_a_davinci_flow: Starting a DaVinci flow
  adding_custom_parameters: Adding custom parameters
  determining_davinci_flow_node_type: Determining DaVinci flow node type
  continue_node_type_js: Handling DaVinci flow collectors in continue nodes
  example_1_handling_textcollector_with_a_component: Example 1. Handling TextCollector with a component
  example_2_handling_flowcollector_with_a_component: Example 2. Handling FlowCollector with a component
  example_3_handling_submitcollector_with_a_component: Example 3. Handling SubmitCollector with a component
  continuing_a_davinci_flow: Continuing a DaVinci flow
  error_node_type_js: Handling DaVinci flow error nodes
  failure_node_type_js: Handling DaVinci flow failure nodes
  success_node_type_js: Handling DaVinci flow success nodes
---

# Stepping through DaVinci flows

[icon: circle-check, set=far]PingOne [icon: js, set=fab]JavaScript

* [Install](01-installing-the-davinci-module.html)

* [Configure](02-configuring-the-davinci-module.html)

* **Navigate**

To authenticate your users the Orchestration SDK for JavaScript DaVinci module must start the flow, and step through each node.

|   |                                                                                                                                 |
| - | ------------------------------------------------------------------------------------------------------------------------------- |
|   | For information on which connectors and fields the DaVinci module supports, refer to [Compatibility](../../compatibility.html). |

## Starting a DaVinci flow

To start a DaVinci flow, call the `start()` method on your new client object:

Start a DaVinci flow

```javascript
let node = await davinciClient.start();
```

### Adding custom parameters

When starting a DaVinci module you can add additional key-pair parameters. The DaVinci module will append these parameters as query strings to the initial OAuth 2.0 call to the `/authorize` endpoint.

|   |                                                                                                                                                                                                                                                                                                                                                   |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | You can access these additional OAuth 2.0 parameters in your DaVinci flows by using the `authorizationRequest.<customParameter>` property.Learn more in [Referencing PingOne data in the flow](https://docs.pingidentity.com/davinci/integrating_flows_into_applications/davinci_launch_flow_redirect.html#referencing-pingone-data-in-the-flow). |

To add parameters when starting the client, create an object of the key-value pairs and pass it as a `query` parameter to the `start()` function:

```js
const query = {
  customQueryParameter: 'customValue'
}

let node = await davinciClient.start({query});
```

|   |                                                                                                                                             |
| - | ------------------------------------------------------------------------------------------------------------------------------------------- |
|   | You can add any parameters to the request as required. For example, you could add `acr_values` to the request to the `/authorize` endpoint. |

## Determining DaVinci flow node type

Each step of the flow returns one of four node types:

* `continue`

  This type indicates there is input required from the client. The `node` object for this type contains a list of `collector` objects, which describe the information it requires from the client.

  Learn more in [Handling DaVinci flow collectors in continue nodes](#continue_node_type_js).

* `success`

  This type indicates the flow is complete, and authentication was successful.

  Learn more in [Handling DaVinci flow success nodes](#success_node_type_js).

* `error`

  This type indicates an error in the data sent to the server. For example, an email address in an incorrect format, or a password that does not meet complexity requirements.

  You can correct the error and resubmit to continue the flow.

  Learn more in [Handling DaVinci flow error nodes](#error_node_type_js).

* `failure`

  This type indicates that the flow could not be completed and must be restarted. This can be caused by a server error, or a timeout.

  Learn more in [Handling DaVinci flow failure nodes](#failure_node_type_js).

Use `node.status` to determine which node type the server has returned:

Determine node type using the `node.status` property

```javascript
let node = await davinciClient.start();

switch (node.status) {
  case 'continue':
    return renderContinue();
  case 'success':
    return renderSuccess();
  case 'error':
    return renderError();
  default: // Handle 'failure' node type
    return renderFailure();
}
```

## Handling DaVinci flow collectors in continue nodes

The `continue` node type contains a list of `collector` objects. These collectors define what information or action to request from the user, or browser.

|   |                                                                                                                                            |
| - | ------------------------------------------------------------------------------------------------------------------------------------------ |
|   | For a list of supported collectors, refer to [Supported PingOne fields and collectors](../../compatibility.html#supported-davinci-fields). |

The Orchestration SDK for JavaScript groups collectors that have similar traits together into categories. For example, collectors that only require a single primitive value to be returned to the server, such as a username or password string, or a single value from a drop-down list are grouped together in a single value collectors category.

To complete a DaVinci flow, we recommend that you either implement a component for a *category* of collectors, or implement a component for each collector *type* that you will encounter in the flow.

Your app iterates through the flow and handles each collector as you encounter it.

Example of iterating collectors and using components

```javascript
const collectors = davinciClient.getCollectors();
collectors.forEach((collector) => {
  if (collector.type === 'TextCollector') {
    textComponent(
      collector, // Object with the collector details
      davinciClient.update(collector), // Returns an update function for this collector
      davinciClient.validate(collector), // Returns a validate function for this collector
    );
  } else if (collector.type === 'PasswordCollector') {
    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
    collector;
    passwordComponent(
      collector, // Object with the collector details
      davinciClient.update(collector), // Returns an update function for this collector
    );
  } else if (collector.type === 'SubmitCollector') {
    submitButtonComponent(
      collector, // Object with the collector details
    );
  } else if (collector.type === 'FlowCollector') {
    flowLinkComponent(
      collector, // Object with the collector details
      davinciClient.flow({
        // Returns a function to call the flow from within component
        action: collector.output.key,
      }),
      renderForm, // Enable re-rendering the form
    );
  }
});
```

### Example 1. Handling `TextCollector` with a component

This example shows how to update a collector with a value gathered from your user.

Pass both a `collector` and `updater` object into a component that renders the appropriate user interface, captures the user's input, and then updates the collector, ready to return to the server.

Example `TextCollector` mapping

```javascript
const collectors = davinciClient.getCollectors();
collectors.map((collector) => {
  if (collector.type === 'TextCollector') {
    renderTextCollector(collector, davinciClient.update(collector));
  }
});
```

|   |                                                                                                                                                                                                                                                                        |
| - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | Mutating the `node` object, the `collectors` array, or any other properties does not alter the internal state of the DaVinci module.The internal data the client stores is immutable and can only be updated using the provided APIs, not through property assignment. |

Your `renderTextCollector` would resemble the following:

Example `TextCollector` updater component

```javascript
function renderTextCollector(collector, updater) {
  // ... component logic

  function onClick(event) {
    updater(event.target.value);
  }

  // render code
}
```

### Example 2. Handling `FlowCollector` with a component

This example shows how change from the current flow to an alternate flow, such as a reset password or registration flow.

To switch flows, call the `flow` method on the `davinciClient` passing the `key` property to identify the new flow.

Example `FlowCollector` mapping

```javascript
const collectors = davinciClient.getCollectors();
collectors.map((collector) => {
  if (collector.type === 'FlowCollector') {
    renderFlowCollector(collector, davinciClient.flow(collector));
  }
});
```

This returns a function you can call when the user interacts with it.

Example `flowCollector` component

```javascript
function renderFlowCollector(collector, startFlow) {
  // ... component logic

  function onClick(event) {
    startFlow();
  }

  // render code
}
```

### Example 3. Handling `SubmitCollector` with a component

This example shows how submit the current node and its collected values back to the server. The collection of the data is already complete so an updater component is not required. This collector only renders the button for the user to submit the collected data.

Example `SubmitCollector` mapping

```javascript
const collectors = davinciClient.getCollectors();
collectors.map((collector) => {
  if (collector.type === 'SubmitCollector') {
    renderSubmitCollector(
      collector, // This is the only argument you will need to pass
    );
  }
});
```

## Continuing a DaVinci flow

After collecting the data for a node you can proceed to the next node in the flow by calling the `next()` method on your DaVinci module object.

This can be the result of a user clicking on the button rendered from the `SubmitCollector`, from the `submit` event of an HTML form, or by programmatically triggering the submission in the application layer.

Continue a DaVinci flow using `next()`

```javascript
let nextStep = davinciClient.next();
```

|   |                                                                                                                                                                  |
| - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | You do not need to pass any parameters into the `next` method as the DaVinci module internally stores the updated object, ready to return to the PingOne server. |

The server responds with a new `node` object, just like when starting a flow initially.

Loop again through conditional checks on the new node's type to render the appropriate UI or take the appropriate action.

## Handling DaVinci flow error nodes

DaVinci flows return the `error` node type when it receives data that is incorrect, but you can fix the data and resubmit. For example, an email value submitted in an invalid format or a new password that is too short.

This is different than a `failure` node type which you cannot resubmit and instead you must restart the entire flow.

You can retain a reference to the `node` you submit in case the next `node` you receive is an `error` type. If so, you can re-render the previous form, and inject the error information from the new `error` node.

After the user revises the data call `next()` as you did before.

## Handling DaVinci flow failure nodes

DaVinci flows return the `failure` node type if there has been an issue that prevents the flow from continuing. For example, the flow times out or suffers an HTTP 500 server error.

You should offer to restart the flow on receipt of a `failure` node type.

Restart a DaVinci flow on receipt of a `failure` node type

```javascript
const node = await davinciClient.next();

if (node.status === 'failure') {
  const error = davinciClient.getError();
  renderError(error);

  // ... user clicks button to restart flow
  const freshNode = davinciClient.start();
}
```

## Handling DaVinci flow success nodes

DaVinci flows return the `success` node type when the user completes the flow and PingOne issues them a session.

On receipt of a `success` node type you should use the OAuth 2.0 authorization Code and state properties from the node and use them to obtain an access token on behalf of the user.

To obtain an access token, leverage the Orchestration SDK for JavaScript.

Example of obtaining an access token using the Orchestration SDK for JavaScript

```javascript
// ... other imports
import { davinci } from '@forgerock/davinci-client';
import { Config, TokenManager } from '@forgerock/javascript-sdk';

// ... other config or initialization code

// This Config.set accepts the same config schema as the davinci function
Config.set(config);

const node = await davinciClient.next();

if (node.status === 'success') {
  const clientInfo = davinciClient.getClient();

  const code = clientInfo.authorization?.code || '';
  const state = clientInfo.authorization?.state || '';

  const tokens = await TokenManager.getTokens({
    query: {
      code, state
    }
  });

  // user now has session and OIDC tokens
}
```
