Skip to content

Getting started

This guide takes you from zero to a registered Tango Vision module.

What you need

  • Node 22+ and npm
  • Access to the private registry https://npm.k8s.tangovision.dev/
  • A sandbox API key (ask the platform team, or see On-demand sandboxes)

Install the SDK

bash
npm install @tv/extension-sdk

.npmrc:

@tv:registry=https://npm.k8s.tangovision.dev/

The SDK ships everything in one package, exposed via subpaths:

SubpathWhat it gives you
@tv/extension-sdkModuleManifest types + Zod validator + PlatformContext types
@tv/extension-sdk/manifestManifest schema, validator, JSON Schema
@tv/extension-sdk/contextRuntime context types
@tv/extension-sdk/react<PlatformProvider>, usePlatformContext(), useBuilding(), useCurrentUser()
@tv/extension-sdk/nestjs@ModuleCapability(), @RequiresLicense() decorators
@tv/extension-sdk/testingcreateMockPlatformContext()
tv-sdk (bin)CLI validator

The anatomy of a module

my-module/
├── module-manifest.json     ← the contract
├── frontend/                ← React, exposes a federated "Shell"
│   └── src/App.tsx
└── backend/                 ← optional NestJS service
    └── src/

The manifest is the heart of it. It declares your module's id, the permissions it needs, the events it speaks, and where its UI mounts. The platform reads it three times:

  1. In your CItv-sdk validate module-manifest.json
  2. At publish — the registry rejects an invalid manifest
  3. At runtime — the Building OS shell composes your module from it

The golden rule

Your module talks to the platform only through PlatformContext.

No localStorage. No manual tokens. No hand-built API URLs. The context gives you a pre-authenticated, tenant-scoped API client. This is what lets the same code run in your sandbox and in a customer's production tenant unchanged.

tsx
import { usePlatformContext, useBuilding } from '@tv/extension-sdk/react';

export function WorkOrderList() {
  const { api } = usePlatformContext();   // already authenticated + scoped
  const building = useBuilding();          // the active building
  return useQuery({
    queryKey: ['work-orders', building.id],
    queryFn: () => api.get(`/api/v1/buildings/${building.id}/work-orders`),
  });
}

Next

Your first module builds a working hello-world end to end.

Built on the Tango Vision platform.