# Web flows for end-customer actions (journeys)

Some steps with providing investment services require **Fondo's direct interaction with the end customer** — for example opening an account including completing KYC questionnaires, authorizing a fund transfer, add/change bank account, sign autogiro agreement, or defining an automated investment plan.

Fondo supports these processes through **journeys**: web flows that partners can launch from their own app or site.

A Journey is [created via the API](/openapi/api/journey/postjourney) and returns a unique, time-limited URL. When the partner redirects the customer to this URL, Fondo presents a user interface where the required action is completed. Each Journey captures the customer's input, records legally binding agreements when needed, and reports the outcome back to the partner application through callback URLs or follow-up GET requests.

## Journey types

| Type | Purpose |
|  --- | --- |
| `createAccount` | Open a new investment account — includes KYC, optional allocation plan, and optional transfer instructions |
| `addBankAccount` | Connect a bank account for withdrawals and optionally sign an autogiro mandate |
| `kycRenewal` | Renew KYC information for an existing account |
| `updateAllocationPlan` | Update the allocation plan for an existing account |


### Handling KYC renewal

Orders and withdrawals can be rejected with `kyc_error` if the account holder's KYC answers have expired. When this happens, start a `kycRenewal` journey to let the customer update their information before retrying.

To catch this early, call `GET /account/{id}/status` when the user signs in. This is a heavier request, so avoid calling it on every page load — a single check at sign-in is enough. If the status indicates a KYC renewal is needed, prompt the user before they attempt to trade.

## Lifecycle

Every journey moves through the following statuses:

```
new → pending → complete
                canceled
                error
                expired
```

```json
{
  "$ref": "#/components/schemas/status",
  "components": {
    "schemas": {
      "status": {
        "readOnly": true,
        "description": "- `new` — Journey created but not yet opened by the end customer\n- `pending` — End customer has opened the journey and is in progress\n- `canceled` — End customer aborted the journey\n- `error` — An error occurred during the journey\n- `complete` — Journey completed successfully\n- `expired` — Journey URL expired before completion\n",
        "oneOf": [
          {
            "description": "New, journey not yet activated",
            "type": "string",
            "readOnly": true,
            "enum": [
              "new"
            ],
            "title": "new"
          },
          {
            "description": "Pending, journey in progress",
            "type": "string",
            "readOnly": true,
            "enum": [
              "pending"
            ],
            "title": "pending"
          },
          {
            "description": "Canceled, end-user aborted",
            "type": "string",
            "readOnly": true,
            "enum": [
              "canceled"
            ],
            "title": "canceled"
          },
          {
            "description": "Expired, TTL exceeded",
            "type": "string",
            "readOnly": true,
            "enum": [
              "expired"
            ],
            "title": "expired"
          },
          {
            "description": "Error, an error occured during the journey",
            "type": "string",
            "readOnly": true,
            "enum": [
              "error"
            ],
            "title": "error"
          },
          {
            "description": "Complete, journey is completed",
            "type": "string",
            "readOnly": true,
            "enum": [
              "complete"
            ],
            "title": "complete"
          }
        ]
      }
    }
  }
}
```

You can check the current status at any time with a [GET request](/openapi/api/journey/getjourney).

## Callbacks

When creating a journey, you provide a `callbackUrl`. Fondo sends a POST request to this URL whenever the journey reaches a terminal status (`complete`, `canceled`, `error`, or `expired`).

### Callback payload

Every callback includes:

| Field | Description |
|  --- | --- |
| `id` | The journey ID |
| `status` | The terminal status |
| `scope` | The BASE64-encoded scope string you passed when creating the journey (if any) — use this to correlate the callback with your own records |


### On `error`, the callback also includes:

An `error.code` indicating what went wrong:

```json
{
  "$ref": "#/components/schemas/code",
  "components": {
    "schemas": {
      "code": {
        "type": "string",
        "description": "- `USER_ERROR` — End-user related process aborted the journey\n- `AUTH_ERROR` — End-user could not be authenticated\n- `KYC_ERROR` — Know Your Customer related process aborted the journey\n- `OTHER_ERROR` — An unspecified error occurred\n- `TRANSFER_ERROR` — Transfer of fund units or cash had an error; account is rejected\n- `SIGN_ERROR` — Error during signing\n",
        "enum": [
          "USER_ERROR",
          "AUTH_ERROR",
          "KYC_ERROR",
          "OTHER_ERROR",
          "TRANSFER_ERROR",
          "SIGN_ERROR"
        ]
      }
    }
  }
}
```

The `context` field indicates where in the flow the error occurred (if available).

### On `complete`, the callback also includes:

A `result` object whose contents depend on the journey type. For example, a `createAccount` completion returns the created account, bank accounts, transfers, and allocation plan. A `kycRenewal` or `addBankAccount` completion returns the journey type only.

See the [journey callback schema](/openapi/api/journey/postjourney) in the API Reference for full details.

### On `canceled`:

The `context` field indicates where the customer exited the flow (if available).

## Redirect URL

The optional `redirectUrl` field controls where BankID returns the user after mobile signing. Set this to a deep link (e.g. `customerApp:///`) so BankID sends the user back to your app instead of the browser. Not needed for web-only integrations.

## Completion redirect URL

The optional `completionRedirectUrl` field controls where the browser is sent once the journey completes successfully. It can be an `http(s)://` URL or a custom-scheme deep link such as `myapp://done` for native-app handoff. Fondo uses it verbatim — no query parameters are appended or modified.

Today this is applied on successful completion only. The `callbackUrl` callback remains the authoritative server-to-server notification of the journey result — the redirect is purely a browser-side handoff back to your application.

## Scope

The `scope` field (max 500 characters) lets you attach your own reference data to a journey. It is returned BASE64-encoded in both the callback payload and the GET response, allowing you to match a completed journey back to your internal state without maintaining a separate mapping.