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

> ## Agent Instructions
> These docs cover learner integrations through Hosted Lab Pages, the SDK, and the REST API. Content management is outside this integration scope.
> Read the setup page for the chosen approach before implementing it. Keep organization API keys and hosted mint secrets on the server.
> The public SDK is coming soon. Check the SDK quickstart for current availability. Do not invent installation commands or direct readers to a private package.
> Cybr provides completion tracking and CTF verification. The integrating platform decides whether to award points.

# Hosted integration

> Create signed launch links, embed a launch card, and receive browser notifications.

Your server exchanges a mint secret for a signed launch link. Your website then shows a link or a compact launch card.

The full lab experience runs on Hosted Lab Pages. The mint API is separate from the [organization REST API](/api-reference/overview).

## Create a launch link

The hosted mint route is `POST /launch/api/mint`. Use the full endpoint URL from onboarding.

Every request includes the `X-Mint-Secret` header and a JSON body:

```json theme={null}
{
  "labId": "lab_id",
  "learnerId": "academy:user:uuid",
  "membership": "free",
  "displayName": "Learner"
}
```

### Request fields

| Field | Required | Meaning |
| - | - | - |
| `labId` | Yes | The lab to open |
| `learnerId` | No | Stable learner ID from your server session |
| `membership` | No | `free` or `premium`, defaults to `free` |
| `displayName` | No | Public name for discussions, up to 80 characters |
| `memberId` | No | Optional member identifier for configured survey attribution |

The server chooses membership from your access policy. Premium lab access requires `membership: "premium"`. An absent or unrecognized value becomes `free`.

The mint route normalizes `displayName` and removes control characters. It does not derive a public name from `learnerId`.

`memberId` accepts 1–64 letters, numbers, underscores, or hyphens. Invalid values are ignored. This field does not replace `learnerId`.

### Learner identity

A supplied learner ID stays the same across launches. It can identify a signed-in learner or a guest session managed by your platform.

Without an ID, Cybr generates a new `anon:` identity for that mint request. The response includes the generated ID.

Completion is recorded against that anonymous ID. A later mint without an ID creates a different learner identity and does not retain the previous completion.

Automatically generated anonymous identities cannot post or delete discussion comments.

### Response

A successful request returns HTTP `200`:

```json theme={null}
{
  "token": "signed-launch-token",
  "url": "https://labs.cybr.com/launch?token=...",
  "embedUrl": "https://labs.cybr.com/launch/embed?token=...",
  "expiresAt": "2026-10-01T12:00:00.000Z",
  "learnerId": "academy:user:uuid"
}
```

| Field | Meaning |
| - | - |
| `token` | Signed authorization for this launch link |
| `url` | Full hosted lab page |
| `embedUrl` | Compact launch card for an iframe |
| `expiresAt` | Launch-token expiration timestamp |
| `learnerId` | Supplied or generated learner ID |

A signed URL grants access to the associated learner’s lab session. Return it only to that learner.

### Server example

`mintEndpoint` and `mintSecret` come from secure server configuration. `learnerId` and `membership` come from your server session and access policy.

```js theme={null}
async function mintLaunch({ labId, learnerId, membership, displayName }) {
  const response = await fetch(mintEndpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Mint-Secret': mintSecret
    },
    body: JSON.stringify({ labId, learnerId, membership, displayName })
  })

  if (!response.ok) {
    const detail = await response.json().catch(() => ({}))
    throw new Error(detail.error || `Mint failed: ${response.status}`)
  }

  return response.json()
}
```

The mint secret authorizes requests for your organization. It does not belong in browser code, source control, or logs.

### Mint errors

| HTTP status | Meaning | Next action |
| - | - | - |
| `400` | Missing lab ID or invalid input | Correct the request |
| `401` | Missing or invalid mint secret | Check server configuration |
| `403` or `404` | Lab lookup denied or lab unavailable | Check the lab ID and organization access |
| `429` | Mint request limit reached | Wait for `retryAfter` seconds |
| `5xx` | Hosted service or upstream request failed | Show an error and handle retries |

A rate-limit response includes `retryAfter` in the JSON body and a `Retry-After` header. Named learners and generated anonymous identities use separate mint limits.

Mint limits are separate from deployment launch limits. A successful mint does not reserve a deployment slot or guarantee a successful launch.

## Show a link or launch card

### Direct link

Set the link target to `url` from the mint response:

```html theme={null}
<a href="https://labs.cybr.com/launch?token=..." target="_blank" rel="noopener">
  Open lab
</a>
```

This opens the full hosted page. It does not add a completion listener to your website.

### Embedded card

Set an iframe source to `embedUrl`:

```html theme={null}
<iframe
  id="cybr-lab"
  title="Cybr lab launch card"
  src="https://labs.cybr.com/launch/embed?token=..."
  style="width: 100%; height: 320px; border: 0;"
></iframe>
```

The iframe contains a compact launch card. Its button opens the full lab page in a new tab.

Cybr must allow your embedding origin. An origin includes the scheme and host, such as `https://learn.example.com`, without a page path.

The iframe height is an example. Your page can adjust it to fit the card’s content.

### Dark card

The compact card accepts `theme=dark`:

```js theme={null}
const cardUrl = new URL(embedUrl)
cardUrl.searchParams.set('theme', 'dark')
document.getElementById('cybr-lab').src = cardUrl.toString()
```

This parameter controls the compact card. It is separate from the full player’s theme and your organization’s branding arrangements.

## Completion notifications

The embedded card sends this message to an allowed parent origin:

```json theme={null}
{ "type": "cybr:lab-completed", "labId": "lab_id" }
```

A correct CTF flag records completion. The hosted page also supports explicit completion for other lab experiences.

The card checks stored completion and can send the message again after a reload. The event does not identify a new attempt or verification method.

Register a listener on the page that contains the iframe:

```js theme={null}
const frame = document.getElementById('cybr-lab')
const hostedOrigin = 'https://labs.cybr.com'

window.addEventListener('message', (event) => {
  if (event.origin !== hostedOrigin) return
  if (event.source !== frame.contentWindow) return
  if (event.data?.type !== 'cybr:lab-completed') return
  if (event.data.labId !== expectedLabId) return

  showLessonCompleted(expectedLabId)
})
```

`expectedLabId` and `showLessonCompleted` belong to your application. If the hosted origin differs from the example, use the origin from onboarding.

The message is a browser notification, not a server webhook. Your server can [read learner completion](/guides/completion-and-scoring) through the organization API before it updates trusted records.

That API request uses an organization API key, not the mint secret.

A direct link does not provide the embedded-card relay. Direct-link integrations can read completion through the API instead.

## Link expiration and reuse

A launch token expires after two hours by default. Hosted configuration can change that duration. The response’s `expiresAt` is the exact deadline.

Each signed link can start at most one deployment. A reload can restore the existing session, but a new deployment requires a new link.

A second launch with a spent link returns `409` with `code: "link_used"`. A failed launch releases the link reservation for another attempt.

After the lab ends, obtain a fresh link for a new attempt. After token expiration, obtain a fresh link before opening the lab page again.

### End notification

The embedded card can relay an end event:

```json theme={null}
{ "type": "cybr:lab-ended", "labId": "lab_id", "reason": "destroyed" }
```

Reasons include `destroyed`, `timeUp`, `expired`, `linkUsed`, and `failed` after a deployment spent the link.

Your page can obtain a fresh link after this event. Use the same origin, iframe-source, and lab-ID checks as the completion listener.

End-event delivery is best effort. Your interface must also offer a way to request a fresh link without that event.

Ending or expiring a lab does not itself record learner completion.

## Before going live

Complete these checks:

1. Store the mint secret in secure server configuration.
2. Check your lab IDs and learner access policy.
3. Send Cybr the exact origins that will embed the card.
4. Launch a lab with both free and premium access where applicable.
5. If your platform supports guests, check guest identity behavior.
6. Complete a lab and check your platform’s progress update.
7. End a lab and obtain a fresh link for another attempt.

For setup questions, contact [support@cybr.com](mailto:support@cybr.com).
