> ## 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.

# Journey URL

> How to obtain and load a journey URL in your Android app.

This guide explains where the journey URL comes from, how to obtain it, and what your Android app needs to do with it.

## Overview

The journey URL is the web address your `WebView` loads to start an identity verification flow. It is not hardcoded — your backend generates it at runtime using the **GBG Go Core SDK** (`@gbg/go-core`).

```mermaid theme={null}
sequenceDiagram
    participant App as Android App
    participant Backend as Your Backend
    participant Core as GBG Go Core SDK

    App->>Backend: 1. Request journey session
    Backend->>Core: 2. Create session
    Core-->>Backend: 3. Return instance ID + URL
    Backend-->>App: 4. Pass journey URL
```

The Android app never calls the Core SDK directly. Session creation happens server-side, and the app receives the URL through whatever API your backend exposes.

## Core SDK setup

### Installation

```bash theme={null}
npm install @gbg/go-core
```

### Authentication

The Core SDK authenticates with a **customer access token** (Bearer). Generate one using the SDK's token endpoint:

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

const go = new Go();

const auth = await go.tokens.generate({
  clientId: "your-client-id",
  clientSecret: "your-client-secret",
  username: "api-user@example.com",
  password: "your-password",
  grantType: "password",
});

// auth.accessToken is a Bearer token for subsequent calls
```

Then initialize the SDK with the token:

```typescript theme={null}
const go = new Go({
  customerAccess: auth.accessToken,
  serverIdx: 0, // 0 = EU, 1 = US, 2 = AU
});
```

### Regional servers

| Index | Region | Server                                         |
| ----- | ------ | ---------------------------------------------- |
| 0     | EU     | `https://eu.platform.go.gbgplc.com/v2/captain` |
| 1     | US     | `https://us.platform.go.gbgplc.com/v2/captain` |
| 2     | AU     | `https://au.platform.go.gbgplc.com/v2/captain` |

Select the appropriate region for your deployment using `serverIdx`, or provide a custom `serverURL`.

## Create a journey session

Journey URL generation is a two-step process.

### Step 1: Start the journey

Call `go.journeys.start()` with a **resource ID** that identifies which journey template to run:

```typescript theme={null}
const journey = await go.journeys.start({
  resourceId: "a4c68509c24789888eb466@latest",
  context: {
    subject: {
      identity: {
        firstName: "John",
        lastNames: ["Doe"],
        dateOfBirth: "1990-01-01",
      },
    },
  },
});
```

Response:

```typescript theme={null}
{
  instanceId: "PiIuACmx8Q8R7qPnAkLAqBAT",  // Unique journey instance (16–64 chars)
  instanceUrl?: "https://..."                 // Journey UI URL (present in some responses)
}
```

The `resourceId` uses the format `<id>@<version>` — use `@latest` to always get the current version of the journey template.

#### Pre-populate context (optional)

You can pass identity data, documents, biometrics, and consent records in the `context` field. This pre-populates the journey so the end user doesn't have to re-enter information you already have:

```typescript theme={null}
context: {
  subject: {
    identity: {
      firstName: "John",
      middleNames: ["Robert"],
      lastNames: ["Doe"],
      dateOfBirth: "1990-01-01",
      // ... additional fields as needed
    },
    documents: [],   // Pre-captured document data
    biometrics: [],  // Pre-captured biometric data
    consent: [],     // Consent records
    uid: "your-internal-user-id",
  },
},
```

### Step 2: Get a connect token

For native app integrations, generate a **connect token** that the device uses for authentication:

```typescript theme={null}
const device = await go.devices.add({
  instanceId: journey.instanceId,
  scope: ["mobile"],
});
```

Response:

```typescript theme={null}
{
  connectToken: "s4FUx8Ny8ijXRtFigz3x1_8rb9bd_5ZD",
  tokenType: "connect",
  expiresIn: 120,    // seconds
  scope: ["mobile"],
}
```

The `connectToken` is short-lived (typically 120 seconds). Your backend should return it to the Android app immediately after generating it.

## Construct the URL

How you get the final URL to load in the `WebView` depends on your integration pattern:

* If the `instanceUrl` is present in the Step 1 response, use it directly.
* Otherwise, your backend constructs the URL from the `instanceId` and `connectToken` according to your deployment's URL scheme.

In either case, your backend returns the URL to the Android app through your own API.

## Load the URL in Android

Once the Android app has the URL from your backend, attach a `BridgeHost` to a `WebView` and load it. In Compose:

```kotlin theme={null}
@Composable
fun JourneyScreen(journeyUrl: String) {
  val host = remember { BridgeHost(hostVersion = "1.0.0") }

  AndroidView(
    factory = { context ->
      WebView(context).also { webView ->
        host.attach(webView)
        webView.loadUrl(journeyUrl)
      }
    },
  )

  DisposableEffect(Unit) {
    onDispose { host.detach() }
  }
}
```

In the View system the pattern is the same: create (or inflate) a `WebView`, call `host.attach(webView)`, then `webView.loadUrl(journeyUrl)`.

The bridge handles everything else — `attach()` configures the `WebView` (JavaScript, DOM storage, the bootstrap-injecting `WebViewClient`) and registers the JavaScript interface, so bootstrap injection and capability negotiation happen automatically when the page loads.

## Complete backend example

A minimal Express endpoint that creates a journey session and returns the URL:

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

const app = express();
app.use(express.json());

app.post("/api/journey/start", async (req, res) => {
  const go = new Go({
    customerAccess: process.env.GO_CUSTOMER_ACCESS,
    serverIdx: 0,
  });

  // Step 1: Start journey
  const journey = await go.journeys.start({
    resourceId: req.body.resourceId ?? "a4c68509c24789888eb466@latest",
    context: {
      subject: {
        identity: req.body.identity,
      },
    },
  });

  // Step 2: Get connect token for the mobile device
  const device = await go.devices.add({
    instanceId: journey.instanceId,
    scope: ["mobile"],
  });

  res.json({
    instanceId: journey.instanceId,
    journeyUrl: journey.instanceUrl,
    connectToken: device.connectToken,
    expiresIn: device.expiresIn,
  });
});
```

<Info>
  The [Android reference app](https://github.com/gbgplc/gbg-go-android-reference) ships a companion server (under `server/`) that implements exactly this pattern. It runs on port **3000** and exposes `POST /api/journey/start` (body `{ resourceId? }`) plus `GET /api/journey/:instanceId/state` for fetching the journey outcome after completion.
</Info>

And the corresponding Android call, using OkHttp and kotlinx-serialization:

```kotlin theme={null}
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException
import java.util.concurrent.TimeUnit

object JourneyService {

  @Serializable
  data class StartResponse(
    val journeyUrl: String,
    val instanceId: String,
    val connectToken: String? = null,
    val expiresIn: Int? = null,
  )

  class JourneyServiceException(
    message: String,
    cause: Throwable? = null,
  ) : RuntimeException(message, cause)

  private val json = Json { ignoreUnknownKeys = true }

  private val client = OkHttpClient.Builder()
    .connectTimeout(30, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .build()

  suspend fun startJourney(serverUrl: String, resourceId: String?): StartResponse =
    withContext(Dispatchers.IO) {
      val endpoint = "${serverUrl.trimEnd('/')}/api/journey/start"

      val bodyPayload =
        if (resourceId.isNullOrEmpty()) JsonObject(emptyMap())
        else JsonObject(mapOf("resourceId" to JsonPrimitive(resourceId)))
      val body = bodyPayload.toString().toRequestBody("application/json".toMediaType())

      val request = Request.Builder()
        .url(endpoint)
        .post(body)
        .header("Content-Type", "application/json")
        .build()

      val response = try {
        client.newCall(request).execute()
      } catch (error: IOException) {
        throw JourneyServiceException(
          "Cannot reach the server at $endpoint: ${error.message}. Is it running?",
          error,
        )
      }

      response.use { resp ->
        val raw = resp.body?.string().orEmpty()
        if (!resp.isSuccessful) {
          throw JourneyServiceException("Server returned HTTP ${resp.code}")
        }
        try {
          json.decodeFromString(StartResponse.serializer(), raw)
        } catch (error: Throwable) {
          throw JourneyServiceException("Unexpected server response", error)
        }
      }
    }
}
```

<Note>
  When developing against a backend on your own machine, remember that the Android emulator routes `localhost` / `127.0.0.1` to the emulator itself — not the host. Use the loopback alias `http://10.0.2.2:3000` to reach a server running on the host machine, or run `adb reverse tcp:3000 tcp:3000` and keep using `127.0.0.1`. Android also blocks cleartext HTTP by default (API 28+), so scope a `network_security_config.xml` exception to `10.0.2.2`/`localhost` for local development rather than enabling cleartext traffic globally.
</Note>

## Security considerations

* **Never embed the customer access token in the Android app.** It is a server-side secret. The Android app only ever sees the journey URL (and optionally the connect token).
* Always use **HTTPS** in production.
* Connect tokens are **short-lived** (typically 120 seconds) and **single-use**. Don't cache or persist them.
* The journey URL should be treated as sensitive — don't log it or store it beyond the current session.
* Fetch the URL over an **authenticated channel** between your app and your backend (e.g., behind your own auth middleware).

## Next steps

* [Integration Checklist](/docs/go-v2/developer-integration/sdks/android/integration-checklist) — End-to-end setup walkthrough
* [Embedding Guide](/docs/go-v2/developer-integration/sdks/android/embedding) — Compose and View-system patterns for displaying the journey
* [Capture Screens](/docs/go-v2/developer-integration/sdks/android/capture-screens) — Get a working capture flow without Smart Capture SDKs
