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

# Sessions and Embedding

> Start a hosted enrollment for an applicant, show it to them, and receive the outcome.

A **session** is one applicant's run through a journey you published in Console. Your server starts it, the applicant completes it on Baselayer's hosted screens, and Baselayer tells your server the outcome. Your server makes two calls and receives one webhook:

| Step | Call |
| - | - |
| Start a session | `POST /onboarding/sessions` |
| Notified on completion | `OnboardingSession.completed` webhook |
| Read the results | `GET /onboarding/sessions/{id}/results` |

## Start a session

Start one session per applicant. The `template_id` is the journey's ID, the last part of its URL in Console:

```bash theme={null}
curl -X POST https://api.baselayer.com/onboarding/sessions \
  -H "X-API-Key: $BASELAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "template_id": "<journey id>", "reference_id": "applicant-123" }'
```

`reference_id` is your own identifier for the applicant; it comes back on the session, its results and its webhook. The response carries the session's `id`, an `embed_url`, the `session_token` already embedded in it, `expires_at` (when the link stops working) and your `reference_id`. A journey that was never published answers `409`.

Retrying the call starts a second session rather than returning the first; use `reference_id` to tell them apart.

## Show it to the applicant

Open `embed_url` in an iframe inside your product:

```html theme={null}
<iframe src="EMBED_URL" title="Verify your identity" style="width: 100%; min-height: 720px; border: 0"></iframe>
```

Or redirect the applicant to it. Either way, the URL is the applicant's credential for this one session: treat it like a password, and start a new session rather than reusing a link.

## Receive the outcome

When the applicant finishes and the checks complete, Baselayer sends the `OnboardingSession.completed` webhook. It is a summary, not the full results:

| Field | Meaning |
| - | - |
| `submission_id` | The session's `id`. |
| `state` | `COMPLETED` for a finished session. |
| `verdict` | `APPROVED` or `UNDER_REVIEW`; see below. |
| `reference_id` | Your identifier from when you started the session. |
| `completed_at` | When the session finished. |
| `principal_ref` | Your organization's stable reference to the verified individual. |
| `business_ref` | Your organization's reference to the verified business, when the journey covered one. |
| `business_operator_link_status` | Whether the applicant is confirmed to operate that business: `VERIFIED`, `UNDER_REVIEW` or `REJECTED`. |

Rarely, the webhook goes out late, after a reviewer has already decided the session; `verdict` then carries that decision, which can be `REJECTED`. Treat the results as the source of truth.

Then read the session's results with `GET /onboarding/sessions/{id}/results`. They carry the `verdict`, what the applicant entered (including your custom fields), their confirmed identity, the business, beneficial owners, and what each check found. A session that hasn't completed answers `409` with error code `6007`, and a completed session whose results aren't available answers `404` with error code `6008`.

| `verdict` | Meaning |
| - | - |
| `APPROVED` | Verified. |
| `UNDER_REVIEW` | Needs a person to look at it. Your team decides in Console, and the results then carry that decision's verdict. |
| `REJECTED` | Your team rejected it in Console. Checks never reject on their own: a failed check sends the session to `UNDER_REVIEW`. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.