> ## Documentation Index
> Fetch the complete documentation index at: https://docs.go.gbgplc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# KYB journey recipe

> Verify a business with the Business Insights module using the GBG Go Core SDKs or the API, from starting the journey to reading the outcome.

This recipe runs a Know Your Business (KYB) journey end to end with the GBG Go Core SDK for TypeScript or Java. You start a journey, submit a business name and registered address, and read back the Business Insights result. Each step also shows the API request, if you call the API directly with cURL.

The recipe uses the **Global Business Entity Verification** variant of the [Business Insights module](/docs/go-v2/platform/modules/others/business-insights.md). The code and responses on this page come from a live journey, trimmed for length.

> [!WARNING]
>
> Always send a `participants` array when you submit business details, whether you use a Core SDK or the API. Without it, the submit call still returns `"status": "success"`, but the journey doesn't store the data and stays on the same interaction. For details, see [Troubleshooting](#troubleshooting).

## Before you begin

You need a GBG Go access token. For details, see [Authenticate](/docs/go-v2/developer-integration/execute-customer-journeys/authenticate.md).

### Set up a Core SDK

Skip this section if you call the API directly.

Install the Core SDK for your language. KYB needs TypeScript Core SDK `0.2.0-alpha.0` or later, or Java Core SDK `0.2.0-alpha01` or later. Earlier TypeScript versions, including `0.1.3`, drop the `entities` field that holds the business details.

```bash TypeScript Core SDK theme={null}
npm install @gbg/go-core@0.2.0-alpha.0
```

```xml Java Core SDK theme={null}
<dependency>
  <groupId>com.gbg</groupId>
  <artifactId>go-core-sdk</artifactId>
  <version>0.2.0-alpha01</version>
</dependency>
```

Create a client with your access token. The steps that follow use this `go` client and the `accessToken` variable:

```typescript TypeScript Core SDK theme={null}
import { Go } from "@gbg/go-core";

const go = new Go({
  serverURL: "https://eu.platform.go.gbgplc.com",
  customerAccess: accessToken,
});
```

```java Java Core SDK theme={null}
import com.gbg.gocore.Go;
import com.gbg.gocore.models.StateFetchResponse;
import com.gbg.gocore.models.operations.*;
import java.util.List;
import java.util.Map;

Go go = Go.builder()
    .serverURL("https://eu.platform.go.gbgplc.com")
    .customerAccess(accessToken)
    .build();
```

> [!NOTE]
>
> In these SDK versions, set `serverURL` to the host only, without `/v2/captain`. The SDK adds the path itself. Use the `us` or `au` host if your account is in another region.

## Step 1: Build the journey

The journey for this recipe has one interaction that collects the business details, followed by the Business Insights module.

1. Create a new journey or open an existing one in the Journey builder.
2. In the **Browse and add modules** panel, click **Search all modules** and enter `Business Insights`.
3. Select the **Business Insights** module.
4. Choose the **Global Business Entity Verification** variant.
5. Click **Add to journey**.
6. Click the **Business Insights** module, and then click **Settings**.

The settings window for the Global Business Entity Verification variant has three settings:

![Business Insights settings window for the Global Business Entity Verification variant](https://mintcdn.com/gbg-loqate/tIKXPKW1MAFiFKd5/images/business-insights-global-settings.png?fit=max\&auto=format\&n=tIKXPKW1MAFiFKd5\&q=85\&s=7f558f9e0742dec31cf7ba972e5b58a7)

| Setting | Description | Value in this recipe |
| - | - | - |
| **How many records to retrieve for check (max 100)** | The number of registry records the module retrieves to check the business against. The maximum is 100. | `15` |
| **Use state / provence as an additional filter (US and CA only)** | Also filters the registry records by the state or province in the submitted address. Applies to US and Canadian businesses only. | Selected |
| **Country code to use if not found on address (2 characters)** | The country the module uses when the submitted address has no `country`. Enter a 2-character country code. | `US` |

> [!NOTE]
>
> Each Business Insights variant has its own settings. If you choose the **US Taxpayer Verification** variant, then its settings window shows a different set. For more on module settings, see [How to configure module settings in Go](/docs/go-v2/guides/product-guides/configure-module-settings.md).

7. Click **Save all settings**, and then click **Close**.
8. Click **Publish to Preview**.
9. Go to the GBG Go Dashboard and copy the journey's Resource ID and Version.

The module's default outcomes are `Business Match`, `Business Partial Match`, `Business Mismatch` and `Error`. To route on them, see [Configure outcome decisions](/docs/go-v2/guides/product-guides/configure-outcome-decisions.md).

## Step 2: Start the journey

Start a journey with your Resource ID and Version. With the API, send a POST request to [`/journey/start`](/docs/go-v2/api-reference/endpoint/start-journey):

```typescript TypeScript Core SDK theme={null}
const started = await go.journeys.start({
  resourceId: "88ffd900ce3454feb0178e33cc6154c2464030a389b14d408528d51cdbe2c976@5sm7vtls",
  context: {
    config: { delivery: "api" },
    subject: {},
  },
});
const instanceId = started.instanceId;
```

```java Java Core SDK theme={null}
StartJourneyResponse started = go.journeys().start(StartJourneyRequest.builder()
    .resourceId("88ffd900ce3454feb0178e33cc6154c2464030a389b14d408528d51cdbe2c976@5sm7vtls")
    .context(StartJourneyContext.builder()
        .config(Config.builder().delivery("api").build())
        .subject(StartJourneySubject.builder().build())
        .build())
    .build());
String instanceId = started.journeyStartResponse().orElseThrow().instanceId();
```

```bash cURL theme={null}
curl --request POST \
  --url https://eu.platform.go.gbgplc.com/v2/captain/journey/start \
  --header 'Authorization: Bearer your_access_token' \
  --header 'Content-Type: application/json' \
  --data '{
    "resourceId": "88ffd900ce3454feb0178e33cc6154c2464030a389b14d408528d51cdbe2c976@5sm7vtls",
    "context": {
      "config": { "delivery": "api" },
      "subject": {}
    }
  }'
```

Replace the `resourceId` value with your own Resource ID and Version. In the cURL request, also replace `your_access_token` with your access token.

**Response:**

```json JSON theme={null}
{
  "instanceId": "CiRmYhD8fR9jo4GBqmu1Q6",
  "status": "started",
  "message": "Journey CiRmYhD8fR9jo4GBqmu1Q6 started successfully"
}
```

Keep the `instanceId`. Every following call uses it.

## Step 3: Fetch the interaction

Fetch the interaction to find out what the journey needs. With the API, send a POST request to [`/journey/interaction/fetch`](/docs/go-v2/api-reference/endpoint/fetch-interaction):

```typescript TypeScript Core SDK theme={null}
const interaction = await go.interactions.fetch(
  { interactionAccess: accessToken },
  { body: { instanceId } },
);
const interactionId = interaction.interactionId;
if (!interactionId) throw new Error("The journey has no interaction to submit");
```

```java Java Core SDK theme={null}
FetchInteractionResponse interaction = go.interactions().fetch(
    FetchInteractionSecurity.builder().interactionAccess(accessToken).build(),
    null,
    FetchInteractionRequestBody.builder().instanceId(instanceId).build());
FetchInteractionResponseBody body = interaction.oneOf().orElseThrow();
String interactionId = body.interactionFetchResponse().flatMap(r -> r.interactionId())
    .or(() -> body.slimInteractionFetchResponse().flatMap(r -> r.interactionId()))
    .orElseThrow();
```

```bash cURL theme={null}
curl --request POST \
  --url https://eu.platform.go.gbgplc.com/v2/captain/journey/interaction/fetch \
  --header 'Authorization: Bearer your_access_token' \
  --header 'Content-Type: application/json' \
  --data '{
    "instanceId": "CiRmYhD8fR9jo4GBqmu1Q6"
  }'
```

In both SDKs, the fetch call takes the access token again as `interactionAccess`. In Java, the `null` argument asks for the full response. The SDK returns the response as one of two shapes, so the code reads `interactionId` from whichever one arrives.

**Response:**

```json JSON expandable theme={null}
{
  "instanceId": "CiRmYhD8fR9jo4GBqmu1Q6",
  "interactionId": "grn:::gbg:design:interaction:segment1@latest",
  "journey": {
    "status": "InProgress"
  },
  "interaction": {
    "nodeId": "segment1",
    "grId": "grn:::gbg:design:interaction:segment1@latest",
    "resource": {
      "id": "segment1",
      "name": "segment1",
      "version": "latest",
      "type": "interaction",
      "data": {
        "pages": [
          {
            "id": "business_details",
            "label": "Business details",
            "cards": [
              { "id": "PrimaryEntityCard" },
              { "id": "EntityAddressCard" },
              { "id": "ControlCard" }
            ]
          }
        ]
      }
    },
    "collects": [
      { "ref": "PrimaryEntity/name", "spec": "required", "parentSpec": "required" },
      { "ref": "PrimaryEntity/aliases", "spec": "optional", "parentSpec": "required" },
      { "ref": "PrimaryEntity/companyNumber", "spec": "optional", "parentSpec": "required" },
      { "ref": "EntityAddress/purpose", "spec": "optional", "parentSpec": "required" },
      { "ref": "EntityAddress/premise", "spec": "optional", "parentSpec": "required" },
      { "ref": "EntityAddress/thoroughfare", "spec": "optional", "parentSpec": "required" },
      { "ref": "EntityAddress/locality", "spec": "optional", "parentSpec": "required" },
      { "ref": "EntityAddress/administrativeArea", "spec": "optional", "parentSpec": "required" },
      { "ref": "EntityAddress/postalCode", "spec": "optional", "parentSpec": "required" },
      { "ref": "EntityAddress/country", "spec": "optional", "parentSpec": "required" }
    ],
    "consumes": []
  },
  "outstanding": [
    "PrimaryEntity/name",
    "EntityAddress/purpose",
    "EntityAddress/premise",
    "EntityAddress/thoroughfare",
    "EntityAddress/locality",
    "EntityAddress/administrativeArea",
    "EntityAddress/postalCode",
    "EntityAddress/country"
  ],
  "outstandingGroups": [
    {
      "elementId": "EntityAddress",
      "cardinality": "at-least-one",
      "refs": [
        "EntityAddress/purpose",
        "EntityAddress/premise",
        "EntityAddress/thoroughfare",
        "EntityAddress/locality",
        "EntityAddress/administrativeArea",
        "EntityAddress/postalCode",
        "EntityAddress/country"
      ]
    }
  ]
}
```

The response tells you three things:

* **`interactionId`**: Pass this back unchanged when you submit. The submit endpoint rejects the short form `segment1`.
* **`PrimaryEntity/name`**: The only required field. It's the business's registered name.
* **`outstandingGroups`**: The `EntityAddress` group has `at-least-one` cardinality, so supply at least one address component. Send every component you have, because the module matches each one separately.

The live response lists more optional fields than shown here, such as `PrimaryEntity/registrations`, `PrimaryEntity/phones` and `EntityAddress/location`. To get the full JSON schema for every field, call `/journey/schema/fetch` with your `resourceId`.

## Step 4: Submit the business details

The journey refers to the business as the `PrimaryEntity` and `EntityAddress` domain elements. In the request body, they map to an entry in `context.subject.entities`:

| Domain element | Path in `context.subject` | Notes |
| - | - | - |
| `PrimaryEntity` | `entities[0]` | Set `type` to `primary`. Put the business name in `name`. |
| `EntityAddress` | `entities[0].addresses[0]` | Optional `purpose` is one of `registered`, `mailing`, `site`, `agent` or `headquarters`. `country` must be an ISO 2 or ISO 3 code in capitals. |

Submit the business details, and name both domain elements in `participants`. With the API, send a POST request to [`/journey/interaction/submit`](/docs/go-v2/api-reference/endpoint/submit-interaction):

```typescript TypeScript Core SDK theme={null}
const submitted = await go.interactions.submit(
  { interactionAccess: accessToken },
  {
    instanceId,
    interactionId,
    participants: [
      { domainElementId: "PrimaryEntity" },
      { domainElementId: "EntityAddress" },
    ],
    context: {
      subject: {
        entities: [
          {
            type: "primary",
            name: "Match Corporation",
            addresses: [
              {
                purpose: "registered",
                premise: "9159",
                thoroughfare: "Kelsey Court",
                locality: "Fishers",
                administrativeArea: "IN",
                postalCode: "46038",
                country: "US",
              },
            ],
          },
        ],
      },
    },
  },
);
```

```java Java Core SDK theme={null}
SubmitInteractionResponse submitted = go.interactions().submit(
    SubmitInteractionRequest.builder()
        .instanceId(instanceId)
        .interactionId(interactionId)
        .participants(List.of(
            Participant.builder().domainElementId("PrimaryEntity").build(),
            Participant.builder().domainElementId("EntityAddress").build()))
        .context(SubmitInteractionContext.builder()
            .subject(SubmitInteractionSubject.builder()
                .entities(List.of(SubmitInteractionEntity.builder()
                    .type(SubmitInteractionTypePrimary1.PRIMARY)
                    .name("Match Corporation")
                    .addresses(List.of(SubmitInteractionAddressPrimary.builder()
                        .purpose(SubmitInteractionPurpose.REGISTERED)
                        .premise("9159")
                        .thoroughfare("Kelsey Court")
                        .locality("Fishers")
                        .administrativeArea("IN")
                        .postalCode("46038")
                        .country("US")
                        .build()))
                    .build()))
                .build())
            .build())
        .build(),
    SubmitInteractionSecurity.builder().interactionAccess(accessToken).build());
```

```bash cURL theme={null}
curl --request POST \
  --url https://eu.platform.go.gbgplc.com/v2/captain/journey/interaction/submit \
  --header 'Authorization: Bearer your_access_token' \
  --header 'Content-Type: application/json' \
  --data '{
    "instanceId": "CiRmYhD8fR9jo4GBqmu1Q6",
    "interactionId": "grn:::gbg:design:interaction:segment1@latest",
    "participants": [
      { "domainElementId": "PrimaryEntity" },
      { "domainElementId": "EntityAddress" }
    ],
    "context": {
      "subject": {
        "entities": [
          {
            "type": "primary",
            "name": "Match Corporation",
            "addresses": [
              {
                "purpose": "registered",
                "premise": "9159",
                "thoroughfare": "Kelsey Court",
                "locality": "Fishers",
                "administrativeArea": "IN",
                "postalCode": "46038",
                "country": "US"
              }
            ]
          }
        ]
      }
    }
  }'
```

**Response:**

```json JSON theme={null}
{
  "status": "success"
}
```

The Business Insights module then runs in the background. In testing, the journey completed about four seconds after the submit.

## Step 5: Poll for the result

Fetch the journey state every second or two while `status` is `InProgress`. With the API, send a POST request to [`/journey/state/fetch`](/docs/go-v2/api-reference/endpoint/fetch-journey-state):

```typescript TypeScript Core SDK theme={null}
let state = await go.journeys.getState({ body: { instanceId } });
while (state.status === "InProgress") {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  state = await go.journeys.getState({ body: { instanceId } });
}
const step = "context" in state ? state.context?.process?.steps?.[0] : undefined;
console.log(state.status, step?.result?.outcome);
```

```java Java Core SDK theme={null}
StateFetchResponse state = fetchState(go, instanceId);
while ("InProgress".equals(state.status())) {
    Thread.sleep(2000);
    state = fetchState(go, instanceId);
}
System.out.println(state.status() + " " + outcome(state));

static StateFetchResponse fetchState(Go go, String instanceId) throws Exception {
    return go.journeys()
        .getState(null, GetJourneyStateRequestBody.builder().instanceId(instanceId).build())
        .oneOf().orElseThrow()
        .stateFetchResponse().orElseThrow();
}

@SuppressWarnings("unchecked")
static Object outcome(StateFetchResponse state) {
    Map<String, Object> process = (Map<String, Object>) state.context().orElseThrow().get("process");
    List<Map<String, Object>> steps = (List<Map<String, Object>>) process.get("steps");
    Map<String, Object> result = (Map<String, Object>) steps.get(0).get("result");
    return result.get("outcome");
}
```

```bash cURL theme={null}
curl --request POST \
  --url https://eu.platform.go.gbgplc.com/v2/captain/journey/state/fetch \
  --header 'Authorization: Bearer your_access_token' \
  --header 'Content-Type: application/json' \
  --data '{
    "instanceId": "CiRmYhD8fR9jo4GBqmu1Q6"
  }'
```

In the Java Core SDK, `context()` on the state is a map, so the `outcome` helper reads the result by key.

> [!NOTE]
>
> Poll the journey state, not the interaction. Straight after a submit, `/journey/interaction/fetch` can still return the business details interaction while the module runs.

**Response:**

```json JSON expandable theme={null}
{
  "instanceId": "CiRmYhD8fR9jo4GBqmu1Q6",
  "status": "Completed",
  "context": {
    "process": {
      "journey": {
        "id": "ee9cf0cd3688020b623fb0cfecf9eff9f9c39d655cacba01368ff8f06fc8907e",
        "version": "5sm3wbb8",
        "name": "Journey Name",
        "startedAt": "2026-10-20T09:27:03.703Z",
        "endedAt": "2026-10-20T09:27:07.871Z",
        "durationMilliSec": 4168
      },
      "steps": [
        {
          "nodeId": "mvggyds6m3mtfmjx64q",
          "name": "Business Insights",
          "result": {
            "status": "complete",
            "outcome": "Business Match",
            "sourceType": "commercial",
            "advice": {
              "businessMatch": true,
              "addressMatch": "partial match",
              "thoroughfare": "mismatch",
              "locality": "match",
              "administrativeArea": "match",
              "postalCode": "match",
              "gbgAddressQualityLevel": "1",
              "incorporationDate": "no result",
              "businessIsActive": "Y",
              "businessIsNonProfit": "N",
              "businessIsMultiStateOperation": "N"
            },
            "note": {
              "quality": {
                "AVC": "V44-I44-P5-096"
              },
              "customAttributes": {
                "businessNameMatches": [
                  {
                    "companyName": "MATCH CORPORATION",
                    "companyNumber": "2002050200116",
                    "companyType": "Domestic For-Profit Corporation",
                    "jurisdictionCode": "us_in",
                    "registeredAddressInFull": "9159 KELSEY COURT, FISHERS, IN 46038",
                    "registeredCountry": "United States",
                    "incorporationDate": "2002-05-01",
                    "dissolutionDate": "",
                    "source": "Indiana Secretary of State"
                  }
                ]
              }
            }
          },
          "process": {
            "step": {
              "moduleName": "Business Insights",
              "variantName": "Global Business Entity Verification",
              "startedAt": "2026-10-20T09:27:06.668337765Z",
              "endedAt": "2026-10-20T09:27:07.785523478Z",
              "durationMilliSec": 1117
            }
          }
        }
      ]
    },
    "result": {
      "status": "complete"
    }
  }
}
```

When `status` is `Completed`, read the Business Insights entry in `context.process.steps`:

* **`result.outcome`**: The module's outcome. Route on this value in the Journey builder or in your own code.
* **`result.advice`**: The individual checks behind the outcome. For what each value means, see [Business Insights](/docs/go-v2/platform/modules/others/business-insights.md).
* **`result.note.customAttributes.businessNameMatches`**: The registry records that matched the business name, including the source registry and company number.

## Read the outcome

The outcome depends on `businessMatch` and `addressMatch` in the advice. These three submissions ran against the same journey:

| Submitted business | `businessMatch` | `addressMatch` | Outcome |
| - | - | - | - |
| `Match Corporation`, with its registered address | `true` | `partial match` | `Business Match` |
| `Match Corp`, with an address that isn't on record | `true` | `mismatch` | `Business Partial Match` |
| A business name that isn't on record | `false` | `not available` | `Business Mismatch` |

A name match alone gives `Business Partial Match`. To return `Business Match`, the address must also match fully or partly.

## Optional: Send the business details at start

If you already hold the business details, then put them in `context.subject` when you start the journey. The journey skips the business details interaction and runs the module straight away.

```typescript TypeScript Core SDK theme={null}
const started = await go.journeys.start({
  resourceId: "88ffd900ce3454feb0178e33cc6154c2464030a389b14d408528d51cdbe2c976@5sm7vtls",
  context: {
    config: { delivery: "api" },
    subject: {
      entities: [
        {
          type: "primary",
          name: "Match Corporation",
          addresses: [
            {
              purpose: "registered",
              premise: "9159",
              thoroughfare: "Kelsey Court",
              locality: "Fishers",
              administrativeArea: "IN",
              postalCode: "46038",
              country: "US",
            },
          ],
        },
      ],
    },
  },
});
```

```java Java Core SDK theme={null}
StartJourneyResponse started = go.journeys().start(StartJourneyRequest.builder()
    .resourceId("88ffd900ce3454feb0178e33cc6154c2464030a389b14d408528d51cdbe2c976@5sm7vtls")
    .context(StartJourneyContext.builder()
        .config(Config.builder().delivery("api").build())
        .subject(StartJourneySubject.builder()
            .entities(List.of(StartJourneyEntity.builder()
                .type(StartJourneyTypePrimary1.PRIMARY)
                .name("Match Corporation")
                .addresses(List.of(StartJourneyAddressPrimary.builder()
                    .purpose(StartJourneyPurpose.REGISTERED)
                    .premise("9159")
                    .thoroughfare("Kelsey Court")
                    .locality("Fishers")
                    .administrativeArea("IN")
                    .postalCode("46038")
                    .country("US")
                    .build()))
                .build()))
            .build())
        .build())
    .build());
```

```bash cURL theme={null}
curl --request POST \
  --url https://eu.platform.go.gbgplc.com/v2/captain/journey/start \
  --header 'Authorization: Bearer your_access_token' \
  --header 'Content-Type: application/json' \
  --data '{
    "resourceId": "88ffd900ce3454feb0178e33cc6154c2464030a389b14d408528d51cdbe2c976@5sm7vtls",
    "context": {
      "config": { "delivery": "api" },
      "subject": {
        "entities": [
          {
            "type": "primary",
            "name": "Match Corporation",
            "addresses": [
              {
                "purpose": "registered",
                "premise": "9159",
                "thoroughfare": "Kelsey Court",
                "locality": "Fishers",
                "administrativeArea": "IN",
                "postalCode": "46038",
                "country": "US"
              }
            ]
          }
        ]
      }
    }
  }'
```

The start response is the same as in [Step 2](#step-2-start-the-journey). Skip Steps 3 and 4, and poll for the result as in [Step 5](#step-5-poll-for-the-result). To get the result in the start response instead, see [Run a journey synchronously](/docs/go-v2/tutorials/run-journeys-synchronously.md).

## Troubleshooting

These are the problems seen most often when submitting business details.

> **Submit returns success but the journey doesn't move on**
>
> The submit request doesn't include a `participants` array. GBG Go returns `"status": "success"`, but the next fetch still lists `PrimaryEntity/name` as outstanding. The state endpoint shows an empty `subject`.
>
> Add `participants` with `PrimaryEntity` and `EntityAddress`, as in [Step 4](#step-4-submit-the-business-details), and submit again.

> **Submit returns 400**
>
> GBG Go validates the submitted business details against the journey schema and returns `400` with a `4002` error code. The `problem` field names the field at fault.
>
> The Core SDKs throw an `ErrorResponse` with this body. Read it from `body` in TypeScript, or with `bodyAsString()` in Java.
>
> | `problem` | Fix |
> | - | - |
> | `Required domain element 'PrimaryEntity' data is missing from context` | Add `name` to the entity. With the TypeScript Core SDK, also check that you use `0.2.0-alpha.0` or later. Earlier versions drop `entities` from the request. |
> | `context.subject.entities.0.addresses.0.country: Invalid string: must match pattern /^[A-Z]{2,3}$/` | Send an ISO 2 or ISO 3 country code in capitals, such as `US`. |
> | `context.subject.entities.0.addresses.0.purpose: Invalid option: expected one of "registered"\|"mailing"\|"site"\|"agent"\|"headquarters"` | Use one of the listed values, or leave `purpose` out. |
> | `Interaction ID does not match current interaction` | Send the full `interactionId` from the fetch response, such as `grn:::gbg:design:interaction:segment1@latest`. |
>
> A failed submit leaves every field outstanding, so you can correct the request and submit it again.

## Related pages

* [Business Insights](/docs/go-v2/platform/modules/others/business-insights.md)
* [Configure outcome decisions](/docs/go-v2/guides/product-guides/configure-outcome-decisions.md)
* [View details of a journey session](/docs/go-v2/platform/investigate/view-journey-details.md)
