diff --git a/.cursor/skills/create-user-documentation/SKILL.md b/.cursor/skills/create-user-documentation/SKILL.md
new file mode 100644
index 0000000000..249cf53102
--- /dev/null
+++ b/.cursor/skills/create-user-documentation/SKILL.md
@@ -0,0 +1,417 @@
+---
+name: create-user-documentation
+description: Generate or update user documentation for ValidMind software features. Use when creating .qmd files, updating docs based on Shortcut stories/epics, or following ValidMind style conventions.
+---
+
+# Create User Documentation
+
+Use this skill when creating new or updating existing user documentation for ValidMind software features.
+
+---
+
+## Workflow
+
+### 1. Gather Context from Typical Inputs
+
+Documentation updates typically start with these inputs from the writer:
+
+#### Shortcut Context (Primary Source)
+The writer provides Shortcut story/epic IDs or links. Use these to gather:
+- `stories-get-by-id` — Full story details, acceptance criteria, description
+- `epics-get-by-id` — Epic overview for larger features
+- `stories-search` — Find related stories in the same epic or with similar labels
+
+**Always check for parent epic:** When given a story ID, check if it belongs to an epic (`epic_id` field in the story). If so, fetch the epic with `epics-get-by-id` — epics often contain:
+- Broader feature context and goals
+- Links to backend/frontend GitHub PRs
+- Related stories that may affect documentation
+- Overall acceptance criteria beyond the individual story
+
+**Extract from Shortcut:**
+- Feature requirements and acceptance criteria
+- User-facing behavior descriptions
+- Edge cases and limitations
+- Related stories for cross-references
+- GitHub PR links (often found in epic or story descriptions/comments)
+
+#### Backend/Frontend PRs (Implementation Details)
+The writer provides PR numbers or links. Use these to understand what changed:
+- `get_pull_request` — PR description, context, reviewer comments
+- `get_pull_request_files` — List of changed files
+- `get_file_contents` — Read specific implementation files if needed
+
+**Extract from PRs:**
+- Actual UI changes and new fields/buttons
+- API changes or new endpoints
+- Configuration options
+- Implementation notes from PR description
+
+#### Screenshots from Demos
+The writer attaches screenshots showing the feature in action. Use these to:
+- Verify UI element names and locations
+- Understand the user flow
+- Identify what needs visual documentation
+- Confirm the feature matches Shortcut acceptance criteria
+
+#### Writer's Notes
+The writer provides human context such as:
+- Draft content or bullet points
+- Specific sections to focus on
+- Questions to address
+- Audience considerations
+- Links to existing docs that need updating
+
+#### Notion (Optional — Feature Specs)
+If available, the writer may provide Notion links to feature specs:
+- `notion-fetch` — Retrieve full page content
+- Look for: detailed requirements, UI mockups, stakeholder context
+
+### 2. Choose the Appropriate Template
+
+Templates are available in `internal/templates/`. Choose based on content type:
+
+#### Task Template (`internal/templates/task.qmd`)
+Step-by-step instructions for completing a specific action.
+- Use for: "How to configure X", "Setting up Y", "Managing Z"
+- Structure: Prerequisites → Steps → Troubleshooting → What's next
+- **Title convention:** Start with a verb, nouns in plural (e.g., "Register models in the inventory")
+
+#### Concept Template (`internal/templates/concept.qmd`)
+Understanding a feature or system.
+- Use for: "What is X?", "How X works", "Understanding Y"
+- Structure: Introduction → Key concepts → Examples → What's next
+- **Title convention:** Nouns only, end with `-overview` if introducing a product area
+
+#### Reference Template (`internal/templates/reference.qmd`)
+Technical specifications or API details.
+- Use for: Field descriptions, configuration options, API endpoints
+- Structure: Sections → Subsections → Additional Resources
+- **Note:** Only use if content cannot be generated programmatically
+
+#### Other Templates
+- `mermaid-diagrams.qmd` — For process flows and diagrams
+- `tachyons-flexbox.qmd` — For responsive column layouts
+- `single-source/` — For content shared between guides and training
+
+---
+
+## Style Guide Reference
+
+Follow the ValidMind style guide at `site/about/contributing/style-guide/` in the documentation repo. Key principles summarized below — refer to the full guide for details.
+
+### Voice and Tone
+
+**Human-first approach:**
+- Use **active voice** — "You'll need to review" not "The content will be reviewed by you"
+- Use **common language** — Define acronyms on first use
+- **Break it down** — Bulleted lists, visual aids, digestible parts
+
+**Inclusive and welcoming:**
+- Show empathy, acknowledge user frustrations
+- Focus on solutions, not problems
+- Use bias-free communication
+- Provide alt text for images
+
+**Conversational but professional:**
+- Address reader directly (second person: "you")
+- Use contractions where appropriate
+- Avoid stiff formality
+
+### Conventions
+
+**American English** spelling and grammar.
+
+**Titles:**
+- Task titles: Start with verb, nouns in plural ("Register models in the inventory")
+- Parent tasks: Use gerund ("Working with the model inventory")
+- Reference titles: Name only, or end with "reference"
+
+**Formatting:**
+
+| Element | Format | Example |
+|---------|--------|---------|
+| UI elements (interactive) | Bold | **{{< fa cubes >}} Inventory** |
+| Statuses/roles | Bubble class | `[Status Name]{.bubble}` |
+| First use of terms | Italics | *Uncertainty* is defined as... |
+| Code/params/files | Backticks | `.env` file, `classifier_full_suite` |
+| Smallcaps UI | Smallcaps class | `[model status]{.smallcaps}` |
+
+**UI element bolding:**
+- Bold UI elements only when the user must interact with them directly in a numbered step (e.g., "Click **Settings**").
+- Do not bold UI elements when mentioned descriptively (e.g., "tasks appear under the Tasks tab").
+
+**List punctuation:**
+- End list items with a period if the item contains a verb.
+- List items that are noun phrases only (no verb) do not need a period.
+
+**Callouts:**
+```markdown
+::: {.callout title="Note or tip"}
+Supplemental information.
+:::
+
+::: {.callout-important title="Warning"}
+Critical information or caveats.
+:::
+```
+
+**Links:**
+- Always use `.qmd` extension, not `.html`
+- Use absolute paths from site root: `/guide/feature/page.qmd`
+- Use margin footnotes instead of inline links in instructional text
+- Use variables for product names: `{{< var validmind.platform >}}`
+
+**Footnotes (at end of file):**
+```markdown
+
+
+[^1]: [Register models in the inventory](/guide/model-inventory/register-models-in-inventory.qmd)
+
+[^2]: [Manage permissions](/guide/configuration/manage-permissions.qmd)
+```
+
+**Screenshots:**
+```markdown
+{fig-alt="Detailed alt text" .screenshot}
+```
+
+**Responsive columns (use Tachyons, NOT CSS Grid):**
+```markdown
+:::: {.flex .flex-wrap .justify-around}
+
+::: {.w-50-ns}
+Column 1
+:::
+
+::: {.w-50-ns}
+Column 2
+:::
+
+::::
+```
+
+---
+
+## Required File Structure
+
+### YAML Header (Required)
+```yaml
+---
+# Copyright © 2023-2026 ValidMind Inc. All rights reserved.
+# Refer to the LICENSE file in the root of this repository for details.
+# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
+title: "Title in Sentence Case"
+date: last-modified
+---
+```
+
+### Opening Paragraph
+- One sentence describing what the page covers and why it matters
+- No heading before this paragraph
+
+### Prerequisites Block (for tasks)
+```markdown
+::: {.attn}
+
+## Prerequisites
+
+- [x] {{< var link.login >}}
+- [x] Additional prerequisite
+- [x] Required role or permission[^1]
+
+:::
+```
+
+---
+
+## Navigation Updates
+
+When creating new pages, update the appropriate navigation file:
+
+- **Sidebar navigation:** `site/guide/_sidebar.yaml`, `site/get-started/_sidebar.yaml`, etc.
+- **Main config:** `site/_quarto.yml` for top-level pages
+- **Listing pages:** Parent `.qmd` files with `listing:` YAML for grid layouts
+
+**Always add aliases when moving/renaming files:**
+```yaml
+aliases:
+ - /old/path/to/page.html
+```
+
+---
+
+## Review Checklist
+
+Before finalizing:
+- [ ] Copyright header present
+- [ ] Title follows sentence case and naming conventions
+- [ ] Prerequisites listed (for tasks)
+- [ ] Steps are numbered and actionable
+- [ ] UI elements are bolded with Font Awesome icons
+- [ ] Links use `.qmd` extension and absolute paths
+- [ ] Images have descriptive alt text and `.screenshot` class
+- [ ] Footnotes for cross-references (not inline links)
+- [ ] American English spelling
+- [ ] Active voice throughout
+- [ ] Page added to navigation (`_sidebar.yaml` or `_quarto.yml`)
+
+---
+
+## Example Prompts
+
+**Creating new documentation:**
+> "Create documentation for the new attestation feature. Here's the Notion spec: [URL]. The Shortcut epic is #1234."
+
+**Updating existing documentation:**
+> "Update the model registration docs to include the new bulk import feature from PR #567."
+
+**From scratch with context:**
+> "I need to document how users configure webhook notifications. I'll provide screenshots and the Shortcut story has acceptance criteria."
+
+---
+
+## Training Materials (Revealjs)
+
+Training courses use a different format and style. Templates are in `site/training/training-templates/`.
+
+### Course Structure
+
+Each course lives in its own subdirectory: `/training/course-name/`
+
+A course consists of:
+1. **Course registration page** (`course-registration.qmd`) — Overview and sign-up
+2. **Course slides** (`course-slides.qmd`) — Revealjs presentation
+
+### Course Registration Template (`training-templates/course-registration.qmd`)
+
+```yaml
+---
+title: "Course Title"
+subtitle: For {{< var vm.product >}}
+listing:
+ - id: modules
+ type: grid
+ contents:
+ # IMPORTANT: Use .html path for revealjs output
+ - path: course-slides.html
+ title: "Module Title"
+ subtitle: "Module 1"
+ description: "{{< fa check >}} Learning point 1
{{< fa check >}} Learning point 2"
+ reading-time: "30"
+---
+```
+
+### Course Slides Template (`training-templates/course-slides.qmd`)
+
+**YAML Header:**
+```yaml
+---
+title: "Module Title"
+subtitle: "Course Name — Module # of #"
+lightbox: true
+format:
+ revealjs:
+ include-in-header:
+ - text: |
+
+ theme: [default, ../assets/slides.scss]
+ slide-number: true
+ footer: "{{< var validmind.training >}} | [Home {{< fa person-walking-dashed-line-arrow-right >}}](/training/training.qmd)"
+ revealjs-plugins:
+ - slideover
+ html:
+ output-file: _course-slides.html
+ search: false
+title-slide-attributes:
+ data-background-color: "#083E44"
+ data-background-image: "../assets/home-hero.svg"
+---
+```
+
+### Training-Specific Conventions
+
+**Links:** Use inline links (not footnotes) — footnotes don't display in presentation mode.
+
+**Section Headers:** Use background colors/images for major sections:
+```markdown
+# Section Title {background-color="#083E44" background-image="/assets/img/about-us-esphere.svg"}
+```
+
+**Interactive iFrame Embeds:**
+```markdown
+## {background-iframe="https://app.prod.validmind.ai/" data-preload="yes"}
+
+:::: {.slideover--r}
+Right-aligned modal content over the live platform.
+::::
+```
+
+**Slideover Positions:**
+- `.slideover--r` — Right-aligned
+- `.slideover--l` — Left-aligned
+- `.slideover--t` — Top-aligned
+- `.slideover--b` — Bottom-aligned
+
+**Slideover Options:**
+- `.auto-collapse` — Auto-collapse after 5 seconds
+- `.auto-collapse-10` — Auto-collapse after 10 seconds
+- `.three-quarters`, `.half`, `.third` — Width options
+
+**Scrollable Content:**
+```markdown
+## {.scrollable}
+
+:::: {.columns}
+::: {.column width="30%"}
+Summary content
+:::
+
+::: {.column width="70%" .bl .pl4}
+Detailed instructions (scrollable)
+:::
+::::
+```
+
+**Embed Callout (within slideovers):**
+```markdown
+::: {.embed}
+Important information styled as a nested callout.
+:::
+```
+
+**Learning Objectives Format:**
+```markdown
+# Learning objectives {.center}
+
+_"As a {{ role }} who ... {{< var vm.product >}}, I want to learn how to {{ task A }}, {{ task B }}, and {{ task C }}."_
+```
+
+**Summary Slide Format:**
+```markdown
+In this module, you learned how to:
+
+- [x] Task 1
+- [x] Task 2
+- [x] Task 3
+```
+
+---
+
+## MCP Tool Reference
+
+### Notion
+- `notion-search` — Find pages/databases by keyword
+- `notion-fetch` — Get full page content by URL or ID
+
+### Shortcut
+- `stories-search` — Find stories by various filters
+- `stories-get-by-id` — Get full story details
+- `epics-get-by-id` — Get epic details
+- `iterations-get-active` — Current sprint context
+
+### GitHub
+- `list_pull_requests` — Find PRs by state/branch
+- `get_pull_request` — Full PR details
+- `get_pull_request_files` — Changed files in PR
+- `list_commits` — Recent commits
+- `get_file_contents` — Read specific files
diff --git a/site/guide/_sidebar.yaml b/site/guide/_sidebar.yaml
index 4db6ecc9f2..8fe3da897d 100644
--- a/site/guide/_sidebar.yaml
+++ b/site/guide/_sidebar.yaml
@@ -42,7 +42,8 @@ website:
contents:
- guide/integrations/manage-secrets.qmd
- guide/integrations/configure-connections.qmd
- - guide/integrations/linking-external-models.qmd
+ - guide/integrations/implement-custom-integrations.qmd
+ - guide/integrations/link-external-models.qmd
- guide/integrations/integrations-examples.qmd
- text: "---"
- text: "Workflows"
diff --git a/site/guide/integrations/configure-connections.qmd b/site/guide/integrations/configure-connections.qmd
index 1e42ae5420..587b3b88b1 100644
--- a/site/guide/integrations/configure-connections.qmd
+++ b/site/guide/integrations/configure-connections.qmd
@@ -122,6 +122,23 @@ Required configuration details:
**[personal access token]{.smallcaps}**
: A PAT secret that includes the `api` or `read_api` scope.
+### Custom
+
+A user-defined connection to a third-party system that implements the {{< var vm.product >}} reference API.[^4]
+
+Use custom integrations to connect internal model registries, proprietary platforms, or any service that exposes data through a compatible API.
+
+Required configuration details:
+
+**[endpoint url]{.smallcaps}**
+: The base URL of your deployed integration that implements the reference API.
+
+**[api key]{.smallcaps}**
+: A secret containing the API key for authenticating with your integration.
+
+**[integration types]{.smallcaps}**
+: Select which types of objects this integration provides — Model Registry and/or Artifact / Ticket Source.
+
::::
## Add connections
@@ -207,3 +224,5 @@ If the test is successful, the message **{{< fa check-circle >}} Connection succ
[^3]: [Supported connections](#supported-connections)
+[^4]: [Implement custom integrations](implement-custom-integrations.qmd)
+
diff --git a/site/guide/integrations/implement-custom-integrations.qmd b/site/guide/integrations/implement-custom-integrations.qmd
new file mode 100644
index 0000000000..8c5f295b0e
--- /dev/null
+++ b/site/guide/integrations/implement-custom-integrations.qmd
@@ -0,0 +1,225 @@
+---
+# Copyright © 2023-2026 ValidMind Inc. All rights reserved.
+# Refer to the LICENSE file in the root of this repository for details.
+# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
+title: "Implement custom integrations"
+date: last-modified
+---
+
+Connect {{< var vm.product >}} to third-party systems that aren't supported out of the box by building custom integrations using our reference API.
+
+Custom integrations let you link resources, such as AI systems or models, from internal registries, proprietary platforms, or any service you control to the {{< var vm.product >}} model inventory.
+
+::: {.attn}
+
+## Prerequisites
+
+- [x] {{< var link.login >}}
+- [x] You can manage permissions.[^1]
+- [x] Your integration service is deployed and accessible from {{< var vm.product >}}.
+- [x] You have the endpoint URL and API key for your integration service.
+- [x] A secret containing your API key exists in integration secrets.[^2]
+
+:::
+
+## How custom integrations work
+
+To create a custom integration, you build a service that implements the reference API endpoints:
+
+1. Build a service that implements the required endpoints and connects to your data sources.
+2. Deploy your service at a URL accessible to {{< var vm.product >}}.
+3. Configure a custom integration in {{< var vm.product >}} that points to your service URL.
+4. After implementation, your users can browse and link resources from your system to records in the model inventory.
+
+Data flows bidirectionally — {{< var vm.product >}} reads your data and can write back metadata to enable linking.
+
+```{mermaid}
+flowchart LR
+ VM[ValidMind Platform]
+ API[Your API service]
+ DATA[Your data sources]
+
+ VM <-->|Reference API| API
+ API <--> DATA
+```
+
+## Reference API
+
+The reference API defines the HTTP endpoints your service must implement. {{< var vm.product >}} calls these endpoints to discover and synchronize resources.
+
+::: {.callout title="Reference implementation"}
+A reference implementation with sample code is available. Contact your ValidMind representative for access.
+:::
+
+### Required endpoints
+
+Your service must expose these endpoints at the `/api/v1` base path:
+
+**List models**
+: `GET /models` — Returns all models from your system. Supports optional `?resource_type=` filter.
+
+**Get model**
+: `GET /models/{id}` — Returns a specific model by its unique identifier.
+
+**Update model** (optional)
+: `PUT /models/{id}` — Accepts metadata updates from {{< var vm.product >}}, enabling bidirectional sync.
+
+**Health check**
+: `GET /health` — Returns the connection status so users can test the integration.
+
+### Optional endpoints
+
+**List tickets**
+: `GET /tickets` — Returns tickets or findings from your system.
+
+**Get ticket**
+: `GET /tickets/{id}` — Returns a specific ticket by ID.
+
+**Schema**
+: `GET /schema` — Returns field definitions to enable formula and workflow access to your integration data.
+
+**Resource types**
+: `GET /resource-types` — Returns available resource categories for filtering in the UI.
+
+### Model response format
+
+Your models endpoint should return objects with these fields:
+
+| Field | Required | Description |
+|-------|----------|-------------|
+| `id` | Yes | Unique identifier |
+| `name` | Yes | Human-readable name |
+| `status` | No | Lifecycle status (active, training, deprecated) |
+| `resource_type` | No | Category for filtering (ml_model, llm, agent) |
+| `metadata` | No | Flexible object with additional fields |
+| `created_at` | No | ISO 8601 timestamp |
+| `updated_at` | No | ISO 8601 timestamp |
+
+### Authentication
+
+{{< var vm.product >}} authenticates requests using the `X-API-Key` header. Your service should validate this key and return `401 Unauthorized` if invalid or missing.
+
+### Bidirectional sync
+
+When linking resources, {{< var vm.product >}} can write metadata back to your system via `PUT` requests. This bidirectional sync enables you to store the {{< var vm.product >}} model identifier (`vm_cuid`) alongside your external records, creating a two-way link between systems.
+
+::: {.callout collapse="true" title="Code examples"}
+
+**GET /models response**
+
+```json
+[
+ {
+ "id": "model-001",
+ "name": "fraud-detection-xgboost",
+ "status": "active",
+ "resource_type": "ml_model",
+ "metadata": {
+ "framework": "xgboost",
+ "version": "2.1.3"
+ },
+ "created_at": "2024-01-15T10:30:00Z",
+ "updated_at": "2024-01-22T14:20:00Z"
+ }
+]
+```
+
+**PUT /models/{id} request body** (what {{< var vm.product >}} sends)
+
+```json
+{
+ "vm_cuid": "mdl_abc123xyz"
+}
+```
+
+**Health check response**
+
+```json
+{
+ "status": "healthy"
+}
+```
+
+**Error response format**
+
+```json
+{
+ "detail": "Model not found"
+}
+```
+
+:::
+
+## Set up custom integrations
+
+### Best practices
+
+**Secure your service**
+: Deploy your integration service behind HTTPS. Validate the API key on every request.
+
+**Handle errors gracefully**
+: Return appropriate HTTP status codes (`400`, `401`, `404`, `500`) and error messages so users can troubleshoot connection issues.
+
+**Paginate large responses**
+: If your external system contains many resources, implement pagination to keep responses fast.
+
+**Monitor availability**
+: Track the health of your integration service. {{< var vm.product >}} displays connection status in the UI, so users notice outages quickly.
+
+**Version your API**
+: Include version information in your API path (`/api/v1/`) to support future changes without breaking existing integrations.
+
+### Add a custom integration
+
+1. In the left sidebar, click **{{< fa gear >}} Settings**.
+
+2. Under Integrations, select **Connections**.
+
+3. Click **{{< fa plus >}} Add Connection**.
+
+4. In the modal that opens, select **Custom Integration**.
+
+5. Enter the:
+
+ - **[integration name]{.smallcaps}** — A display name to identify this integration.
+ - **[description]{.smallcaps}** (optional) — Additional details about the intended usage.
+ - **[endpoint url]{.smallcaps}** — The base URL of your deployed integration that implements the reference API.
+ - **[api key]{.smallcaps}** — Select a secret containing the API key for authenticating with your integration.
+ - **[integration types]{.smallcaps}** — Select which types of objects this integration provides:
+ - **Model Registry** — Sync models from your external system.
+ - **Artifact / Ticket Source** — Sync artifacts or tickets from your external system.
+ - **[initial status]{.smallcaps}** — Set to `Operational` to enable immediately or `Disabled` to finish setup later.
+
+6. Click **Save Integration**.
+
+7. Test the connection to verify your service is reachable:
+
+ a. Hover over the custom integration you created.
+
+ b. When the **{{< fa ellipsis-vertical >}}** menu appears, click it and select **{{< fa circle-check >}} Test Connection**.
+
+ If the test succeeds, the message **{{< fa check-circle >}} Connection successful** displays.
+
+### Link resources
+
+After configuring your custom integration, link resources to models in the inventory:
+
+1. In the left sidebar, click **{{< fa cubes >}} Inventory**.
+
+2. Select a model by clicking on it.
+
+3. In the right sidebar, locate your custom integration connection.
+
+4. Hover over the connection.
+
+5. When the **{{< fa ellipsis-vertical >}}** menu appears, click it and select **{{< fa link >}} Link Model**.
+
+6. In the modal that opens, select the resource from your external system.
+
+7. Click **Link Model**.
+
+
+
+[^1]: [Manage permissions](/guide/configuration/manage-permissions.qmd)
+
+[^2]: [Manage secrets](manage-secrets.qmd)
diff --git a/site/guide/integrations/linking-external-models.qmd b/site/guide/integrations/link-external-models.qmd
similarity index 98%
rename from site/guide/integrations/linking-external-models.qmd
rename to site/guide/integrations/link-external-models.qmd
index 33d25665e7..70318c1d94 100644
--- a/site/guide/integrations/linking-external-models.qmd
+++ b/site/guide/integrations/link-external-models.qmd
@@ -4,6 +4,8 @@
# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
title: "Linking external models"
date: last-modified
+aliases:
+ - /guide/integrations/linking-external-models.html
---
Link models in the {{< var vm.product >}} model inventory to external systems to keep model information synchronized.
@@ -112,4 +114,3 @@ The model is unlinked from the external system.
[^6]: [Configure connections](configure-connections.qmd)
[^7]: [Working with the model inventory](/guide/model-inventory/working-with-model-inventory.qmd#search-filter-and-sort-models)
-
diff --git a/site/guide/integrations/managing-integrations.qmd b/site/guide/integrations/managing-integrations.qmd
index a294aaf201..adba0ee80d 100644
--- a/site/guide/integrations/managing-integrations.qmd
+++ b/site/guide/integrations/managing-integrations.qmd
@@ -14,7 +14,8 @@ listing:
contents:
- manage-secrets.qmd
- configure-connections.qmd
- - linking-external-models.qmd
+ - implement-custom-integrations.qmd
+ - link-external-models.qmd
- integrations-examples.qmd
---
@@ -90,6 +91,10 @@ Link to models in external registries and development platforms and keep invento
- GitLab
:::
+::: {.w-20-ns}
+- Custom integrations
+:::
+
::::
---