Integration: Custom Workflow Behavior

Overview

ID Dataweb workflows are configurable — you can add steps, remove steps, and attach additional checkers to any step. For Gateway integrations, customization is largely transparent to your application. For API integrations, adding or changing steps means your application needs to handle the new interactions.

This page covers what changes (and what doesn't) when you customize a workflow.

Gateway (OIDC): Nothing Changes

Gateway integrations are based on OpenID Connect. Your application sends an /authorize request and receives an authorization code after the user completes the flow. The hosted UI handles all step sequencing — your application is not involved in the step-by-step logic.

When you add or change a step in a Gateway workflow:

  • The user sees the additional interaction in the hosted UI
  • Your application sends the same /authorize request, unchanged
  • Your application receives the same authorization code and performs the same token exchange

The response will reflect what actually ran — including any additional steps. You do not need to change your backend code to handle a new step.

The only thing to plan for on the Gateway side is the user experience: adding a document capture step, for example, means users will complete a mobile document capture flow that wasn't there before. No code changes required, but worth communicating to users.

API: Handle Steps Dynamically with forwardApiKey

API integrations require your application to call each step explicitly. When you add a step to a workflow, your application needs to handle it.

How step chaining works

Every /slverify response includes a policyDecision field:

policyDecisionMeaningWhat your app does next
obligationStep passed — another step is requiredCall the next step using forwardApiKey from this response
approveWorkflow complete — user verifiedGrant access
denyWorkflow complete — user not verifiedDecline or route to fallback

When policyDecision = obligation, the response includes forwardApiKey — the API key for the next step. Pass it as apikey in the next /slverify call.

Hardcoded step sequence

For a fixed workflow where you know the sequence in advance, call steps in order:

// Step 1 — Session Risk
const step1 = await slverify(SESSION_RISK_KEY, { country: 'US' });
if (step1.policyDecision === 'deny') return deny();

// Step 2 — PII Validation (key comes from step 1 forwardApiKey)
const step2 = await slverify(step1.forwardApiKey, piiAttributes);
if (step2.policyDecision === 'deny') return deny();

// Step 3 — SMS Link (key comes from step 2 forwardApiKey)
const step3 = await slverify(step2.forwardApiKey, fastTapAttributes);
return step3.policyDecision === 'approve' ? approve() : deny();

This is straightforward when the workflow is fixed and you control the step sequence.

Dynamic step sequence

When you add steps, support multiple workflow configurations, or want your integration to handle workflow changes without code deploys, drive the flow from policyDecision and forwardApiKey rather than hardcoding the step order:

async function runWorkflow(initialKey, initialAttributes) {
  let apiKey = initialKey;
  let attributes = initialAttributes;

  while (true) {
    const res = await slverify(apiKey, attributes);

    if (res.policyDecision === 'approve') return { result: 'approve', data: res };
    if (res.policyDecision === 'deny')    return { result: 'deny',    data: res };

    // obligation — another step required
    apiKey = res.forwardApiKey;
    attributes = await collectAttributesForNextStep(apiKey);
    // collectAttributesForNextStep presents the appropriate UI for the next step
    // and returns the user's input as userAttributes
  }
}

With this pattern, adding a step to the workflow in the Admin Console does not require a code change — your application handles the new step automatically, as long as it knows how to collect input for that step type.

What to handle when adding a step

New user input. Each step type requires specific userAttributes. Adding a Government ID and Selfie Match step requires calling /async-ui/send-link and handling the capture redirect. Adding a Dynamic KBA step requires displaying the questions and submitting answers. Plan the UI and input handling for any new step type before deploying.

New deny points. Each step is a point where policyDecision = deny can be returned. Confirm that your application handles deny at any point in the chain, not just at the final step.

No change to approval. policyDecision = approve means the full workflow has passed — the same regardless of how many steps ran to get there.

Session continuity across steps

Link all steps of a user session using the asi (Application Session Identifier). Pass the transaction_id returned by the first /slverify call as asi in all subsequent calls for that session. This is required for accurate reporting in the Admin Console and is important for debugging.

// First call — save transaction_id
const step1 = await slverify(SESSION_RISK_KEY, attrs);
const asi = step1.transaction_id;

// All subsequent calls — include asi
const step2 = await slverify(step1.forwardApiKey, step2Attrs, { asi });
const step3 = await slverify(step2.forwardApiKey, step3Attrs, { asi });

See ID Dataweb API for full session tracking documentation.

Related Resources

Customizing Workflows | → ID Dataweb API | → API Authentication | → Interpreting Results — API | → Gateway (OIDC) Integration