Accepted design · Digital onboarding

Customer update journeys

How authenticated customers discover, resume, and complete profile-enrichment journeys from current NewGen requirements and configured forms.

NewGen authoritative Authenticated Resumable Core-confirmed
Sequence from status assessment through configured form resolution, local step capture, and core-confirmed journey completion.
Figure 1. Customer update resolution and submission.

1. Overview

A customer update is a post-onboarding journey for a digitally onboarded customer. The service returns only journeys that match the customer's current profile requirements. Customer Information and Biometrics are separate journeys.

NewGen decides which profile requirements remain. Form Engine selects existing journey steps and form properties that can satisfy those requirements. Step submissions remain local until the customer submits the confirmation step. That final step sends one journey payload to NewGen and waits for the system of record.

Authoritative design. This document replaces the earlier one-form and per-step core-submission design. The accepted decisions live under docs/adr/cif-update.

2. Ownership and eligibility

ConcernOwner
Authentication and digital-onboarding claimProfile Management
Current profile requirement values, required state, and editabilityNewGen
Journey state, progress, locking, and reconciliationOnboarding Service
Journey templates, forms, validation, and customer copyForm Engine
CIF dataFinacle
Biometric enrollmentBio Server

All customer-update endpoints require a bearer token. The service reads `CustomerId` as the CIF, `bankId` as the bank identifier, and `onboardingType` as the onboarding channel enum. Only onboardingType: 5 is eligible. The service never accepts these values from a request body.

Every instance lookup verifies ownership by (bankId, CustomerId). A request for another customer's instance returns 404.

3. NewGen assessment contract

The status endpoint asks NewGen for current requirement groups. NewGen returns values and states, not UI controls.

{
  "preferredAddress": {
    "streetBuilding": { "editable": true,  "value": "",    "required": false },
    "town":           { "editable": true,  "value": "",    "required": true  },
    "countryCode":    { "editable": false, "value": "KE",  "required": true  },
    "cityCode":       { "editable": true,  "value": "002", "required": true  },
    "villageEstate":  { "editable": true,  "value": "",    "required": false },
    "stateCode":      { "editable": true,  "value": "NAI", "required": false }
  },
  "workitemName": "DP-TEST-00001-Update",
  "cifInfo": { "cifNo": "00000000001" },
  "sourceOfIncome": {
    "occupation":       { "editable": true, "value": "", "required": false },
    "employmentStatus": { "editable": true, "value": "", "required": false }
  },
  "contacts": {
    "emailAddress": { "editable": true, "value": "", "required": true }
  },
  "successful": true,
  "statusCode": "00"
}

This confirmed response has no missing property. In this contract version, the service requests a parameter when it is required, editable, and its string value is empty. The full fixture is docs/newgen-assessment-v1.example.json; its identifying values are synthetic.

Contract version. NewGen may replace this response with a more consistently nested shape. Treat the checked-in fixture as version 1. Change the fixture, DTO, and projection tests together when the upstream contract changes.

4. Resolve configured forms

  1. Resolve each NewGen group through an internal Form Engine sourceReference.
  2. Load the configured journey and form versions for the customer's country and channel.
  3. Keep a form property only when required && editable && value is empty.
  4. Require NewGen child names to match schema property names.
  5. Omit a step when no properties remain. Omit a journey when no steps remain.
  6. Fail the complete catalog when a required mapping or configuration is invalid.

For the example above, the resolved forms contain town and emailAddress. They omit populated cityCode, read-only countryCode, and optional empty fields. A required, non-editable empty parameter produces actionRequired.

CIF-update forms in this delivery do not contain cross-field rules. Existing onboarding forms keep their current cross-field validation. Supporting values may appear in data, but only properties in the resolved schema may be submitted. The backend rejects additional properties.

5. Status catalog

GET /customer-updates/status queries NewGen on every request and resolves the catalog in memory. An unchanged resolution reuses local journey instances and causes no database write.

{
  "overallStatus": "incomplete",
  "progress": { "completedSteps": 7, "totalSteps": 10, "percentage": 0.7 },
  "journeys": [
    {
      "journeyId": "customer_information_update",
      "displayOrder": 1,
      "title": "Customer information",
      "subtitle": "Fill in your physical address and occupation information.",
      "lifecycle": "not_started",
      "availability": "available",
      "customerUpdateId": null,
      "startToken": "opaque-short-lived-token"
    },
    {
      "journeyId": "biometric_fingerprints_update",
      "displayOrder": 2,
      "title": "Capture your biometrics",
      "subtitle": "Share your biometric information.",
      "lifecycle": "not_started",
      "availability": "available",
      "customerUpdateId": null,
      "startToken": "opaque-short-lived-token"
    }
  ]
}

Lifecycle values are not_started, active, and processing. Availability values are available, locked, and temporarily_unavailable. The API supplies displayOrder; the client does not sort by state.

Customer Information and Biometrics are independent. Both can be available before the customer starts either one. After a journey starts, that item becomes active and every other item becomes locked until core-confirmed completion.

An active catalog item contains its customerUpdateId. The client calls current-step after selection. The catalog does not contain forms or current-step data. If NewGen reports only non-editable gaps, the response uses actionRequired and configured support guidance without exposing internal parameter names.

An ineligible customer receives notEligible, no progress, and an empty journey list. A customer with no remaining gaps receives complete, progress 1.0, and an empty journey list.

6. Authenticated client API

MethodRoutePurpose
GET/customer-updates/statusResolve overall progress and the journey catalog.
POST/customer-updatesStart or return the active instance from a protected start token.
GET/customer-updates/{customerUpdateId}/journey-configReturn resolved steps and journey progress.
GET/customer-updates/{customerUpdateId}/current-stepReturn the current step using the onboarding response shape.
GET/customer-updates/{customerUpdateId}/steps/{stepId}Read a resolved step without changing journey state.
POST/customer-updates/{customerUpdateId}/steps/{stepId}/submitSave a draft or validate and submit a step.
POST/customer-updates/{customerUpdateId}/otp/verifyVerify the OTP for the instance.
POST/customer-updates/{customerUpdateId}/otp/regenerateRegenerate the OTP for the instance.

The executable examples are in the Customer updates Postman collection.

Start request

{ "startToken": "opaque-short-lived-token" }

The protected token contains the customer key, journey and form versions, resolved field set, assessment fingerprint, and expiry. It creates no database record until used. An expired token requires another status request. A replay returns the active instance.

Start response

{
  "customerUpdateId": "019c...",
  "journeyId": "customer_information_update",
  "lifecycle": "active",
  "currentStepId": "email_address"
}

Journey configuration response

{
  "customerUpdateId": "019c...",
  "journeyId": "customer_information_update",
  "journeyVersion": 2,
  "progress": { "completedSteps": 1, "totalSteps": 4, "percentage": 0.25 },
  "steps": [
    {
      "stepId": "email_address",
      "sequence": 1,
      "formKey": "profile_email_validation",
      "formVersion": 2,
      "title": "Email address",
      "type": "Form",
      "status": "Validated"
    },
    {
      "stepId": "preferred_address",
      "sequence": 2,
      "formKey": "profile_preferred_address",
      "formVersion": 1,
      "title": "Preferred address",
      "type": "Form",
      "status": "NotStarted"
    },
    {
      "stepId": "review_information",
      "sequence": 3,
      "formKey": "review_information",
      "formVersion": 1,
      "title": "Review information",
      "type": "ReviewForm",
      "status": "NotStarted"
    },
    {
      "stepId": "confirm",
      "sequence": 4,
      "formKey": "confirm",
      "formVersion": 1,
      "title": "Confirm",
      "type": "ConfirmForm",
      "status": "NotStarted"
    }
  ],
  "branches": []
}

The response keeps the configured-journey structure. It adds instance progress and step status, but does not include form schemas.

7. Steps, drafts, OTP, and review

current-step and get-step use the same response structure as onboarding. The response contains data and a nested form with formKey, formVersion, schema, uiSchema, hash, and context.

{
  "customerUpdateId": "019c...",
  "currentStepId": "preferred_address",
  "stepType": "Form",
  "status": "NotStarted",
  "onboardingStatus": "InProgress",
  "countryCode": "KE",
  "data": { "country": "KE", "city": "NBO" },
  "form": {
    "formKey": "profile_preferred_address",
    "formVersion": 1,
    "schema": {
      "type": "object",
      "properties": {
        "town": { "type": "string" },
        "building": { "type": "string" }
      },
      "required": ["town", "building"]
    },
    "uiSchema": {},
    "statusPage": null,
    "hash": "sha256:...",
    "context": {}
  },
  "documentType": null,
  "documentNumber": null,
  "resolvedBranches": {}
}

The customer-update response renames onboardingId to customerUpdateId. All other fields retain the onboarding endpoint structure.

{
  "requestId": "client-generated-id",
  "isDraft": false,
  "payload": { "town": "Westlands" }
}

The step submission contract remains compatible with onboarding. While review is current, the client may submit a corrected payload for an earlier reviewable step. The backend validates and replaces that step without moving away from review.

Email is a step in Customer Information. A missing email follows the existing onboarding OTP behavior. The customer may correct the email before OTP validation, but cannot reopen it afterward. OTP routes use the customerUpdateId.

8. Submit one journey

Normal form submissions do not call NewGen. They validate and persist customer data locally. Submitting the configured confirmation step performs these actions:

  1. Validate the confirmation and all selected fields.
  2. Atomically move the journey to processing.
  3. Build one flat object that contains the values collected by the journey.
  4. Call NewGen once for the journey. NewGen decides whether to reuse or create a WorkItem.
  5. Wait for Finacle or Bio Server confirmation through NewGen.
  6. Complete the journey and resolve status again.
{
  "town": "Westlands",
  "emailAddress": "customer@example.test"
}

The flat object contains only selected properties. The service rejects duplicate selected parameter names across groups because the object cannot represent them safely. Onboarding does not echo unchanged profile values.

9. Resumability and reconciliation

Every successful status request compares current NewGen requirements with local customer-update state.

ConditionResult
Requirements are unchangedReuse the existing instance and avoid a write.
A requirement is satisfied elsewhereRemove its field. If no fields remain, mark the journey superseded.
A requirement is added to an active journeyAdd its configured field and invalidate review.
A draft conflicts with NewGenNewGen wins. Preserve drafts only for required and editable parameters whose current value remains empty.
A completed requirement appears againCreate a new instance and retain the earlier completion history.
The journey is processingFreeze its submitted payload. New requirements belong to a later instance.

Journey and form versions freeze when the customer starts. The selected fields can change within those versions after re-resolution. Another channel receives the same catalog and resumes the active instance through current-step.

10. Progress

(baseline onboarding steps + confirmed enrichment steps) / (baseline onboarding steps + all current enrichment steps)

The API returns a fractional percentage such as 0.5. Baseline steps are the steps the customer traversed in the persisted onboarding instance. Every journey step counts except a status page. Review, OTP, and confirmation count.

Re-resolution can add required steps and reduce the percentage. A requirement satisfied through another channel counts as externally completed. When no gaps remain, status returns complete, progress 1.0, and an empty journey list.

11. Failures and HTTP behavior

StatusUse
200Successful read, local step operation, or core-confirmed submission.
202Submission still processing, including a duplicate request.
400Malformed request.
401Missing or invalid token.
404Unknown or unowned journey or step.
409Invalid lifecycle transition, including an attempt to edit verified email.
422Form validation failure.
500Unexpected server or configuration failure.
503NewGen or a core system is unavailable.

If NewGen is unavailable, the API may return the previous catalog with overall status temporarilyUnavailable. Its items use availability temporarily_unavailable. The customer cannot start, resume, edit, save, or submit until the authoritative systems recover. If no previous catalog exists, status fails.

A normal final submission waits for core confirmation. If the request window closes first, the API returns 202 with a stable submission reference and status URL. Polling and NewGen callbacks update the same durable state. Core-confirmed success is final.

12. Storage and scale

Status calls do not create records when no local state changes. The service does not retain full NewGen responses. It stores only data needed for ownership, resumability, validation, progress, and submission reconciliation:

Completed cycles may be compacted to counters and audit references. The retention period for a previous catalog during outages is configurable.

13. Current implementation gaps

The ADRs remain the source of truth for the target design. Milestone 1 is complete with a versioned NewGen fixture, contract checks, form mappings, journey configuration, and clean-database version retention. Implementation work must still address these differences:

Implementation boundary. The checked-in contract and configuration are complete. Persistence starts in Milestone 2, and runtime catalog resolution starts in Milestone 3. Follow the delivery plan for milestone status and proof.