This document describes the public NashTwin plugin contract that is currently safe to rely on when preparing a third-party submission. The validator exported by @nashtwin/common is the source of truth for manifest shape. Where the broader codebase contains internal or staged functionality, this document intentionally limits itself to the supported public submission surface.
For onboarding guidance, see the Plugin SDK. For in-product UI guidance, see Plugin UI Best Practices.
Scope
This contract covers:
- manifest validation rules
- public capability names
- public event subscription names accepted in manifests today
- graph and optimization extension rules
- submission lifecycle and review states currently used by the product
This contract does not invent behavior that the runtime does not clearly expose yet. If a field is not fully public or is still staged, the notes below call that out directly.
Manifest
Every plugin must ship a manifest.json file. The platform validates the manifest before storing a submission and validates it again before publishing.
Required fields
| Field | Type | Constraints |
|---|---|---|
id |
string |
Reverse-domain format. Must match /^[a-z][a-z0-9]*(\.[a-z][a-z0-9-]*)+$/. Immutable once published. |
name |
string |
1 to 100 characters. |
description |
string |
1 to 500 characters. |
version |
string |
Strict semver, for example 1.2.0. No v prefix. |
author |
string |
1 to 100 characters. |
capabilities |
string[] |
At least one value from the capability registry. |
events |
string[] |
Zero or more values from the public event registry below. |
Optional fields
| Field | Type | Constraints |
|---|---|---|
authorUrl |
string |
Must be a valid URL if present. |
iconUrl |
string |
Must be a valid URL if present. Used in Marketplace surfaces. |
configSchema |
object |
Limited JSON-schema-like object. See Configuration Schema. |
webhookEndpoint |
string |
Must be a valid URL if present. Required when webhook-handler is declared. |
oauthScopes |
string[] |
Free-form scope request list subject to manual review. No formal public scope registry is frozen yet. |
graphExtension |
object |
Declares graph contributions, scopes, residency, retention, and supported event schema versions. |
optimizationExtension |
object |
Declares optimization runtime and scenario support for strategy plugins. |
tags |
string[] |
Marketplace discovery tags. |
Minimal valid manifest
{
"id": "com.example.my-plugin",
"name": "My Plugin",
"description": "Does something useful in NashTwin.",
"version": "1.0.0",
"author": "Example Corp",
"capabilities": ["digital-twin-extension"],
"events": ["twin.updated"]
}
Capability Registry
| Capability | Meaning |
|---|---|
crm-enrichment |
Enriches contacts, deals, or related CRM records with external or derived data. |
digital-twin-extension |
Extends the twin projection and graph-backed business view. |
game-theory-strategy |
Provides strategy, ranking, simulation, or recommendation logic. |
ui-component |
Renders product UI inside NashTwin. |
webhook-handler |
Declares a webhook endpoint in the manifest. |
Capability notes
webhook-handlerrequireswebhookEndpoint.ui-componentshould follow Plugin UI Best Practices.game-theory-strategyoften pairs withoptimizationExtension.digital-twin-extensionoften pairs withgraphExtension.webhook-handleris part of the validated manifest surface, but public third-party delivery transport should be treated as staged unless your integration has been explicitly approved for it.
Event Subscription Registry
These are the event names currently accepted in plugin manifests.
| Event type | Triggered when |
|---|---|
contact.created |
A new contact is created in CRM. |
contact.updated |
An existing contact changes. |
deal.created |
A new deal is created. |
deal.deleted |
A deal is deleted. |
deal.updated |
An existing deal changes. |
outcome.recorded |
An optimization outcome is recorded for calibration and governance. |
pipeline.created |
A new pipeline is created. |
pipeline.updated |
A pipeline changes. |
recommendation.generated |
A recommendation set is generated by an optimization workflow. |
simulation.completed |
A simulation run completes successfully. |
simulation.failed |
A simulation run fails. |
simulation.requested |
A simulation run is requested. |
twin.updated |
The tenant's digital twin snapshot is recomputed. |
Event notes
- Event delivery uses versioned envelopes with replay-safe identifiers.
idempotencyKeyis the deduplication key plugin runtimes should honor.- Some optimization workflow states exist elsewhere in the codebase as internal platform events, but they are not currently accepted in public third-party manifests. Examples include approval-decision workflow states and denied simulation states.
Event envelope
interface VersionedPluginEvent<T = unknown> {
eventId: string
eventType: PluginEventType
idempotencyKey: string
payload: T
schemaVersion: string
tenantId: string
timestamp: string
}
Configuration Schema
configSchema allows a plugin to declare install-time configuration fields.
interface PluginConfigSchema {
type: 'object'
properties?: Record<string, PluginConfigSchemaProperty>
required?: string[]
}
interface PluginConfigSchemaProperty {
type: 'boolean' | 'number' | 'string'
description?: string
minimum?: number
maximum?: number
}
Rules
typemust be the literal stringobject.- Only scalar property types are supported:
boolean,number, andstring. minimumandmaximumapply only to numeric fields.- Nested schema objects and arrays are not currently part of the supported public contract.
- Use
descriptiongenerously so install and review screens are understandable without source access.
Graph Extension Manifest
Add graphExtension when the plugin contributes graph records or reads graph-backed twin data.
interface PluginGraphExtension {
contributions: PluginGraphContribution[]
handledDataClassifications: GraphDataClassification[]
readScopes: GraphReadScope[]
residencyGuarantees: GraphResidencyZone[]
retentionStrategy: PluginGraphRetentionStrategy
supportedEventSchemaVersions: string[]
writeScopes: GraphWriteScope[]
}
type PluginGraphContribution =
| {
kind: 'node'
schemaId: string
displayName: string
description: string
layer: GraphDataLayer
nodeFamily: GraphNodeFamily
version: string
dataClassificationTargets: GraphDataClassification[]
}
| {
kind: 'edge'
schemaId: string
displayName: string
description: string
layer: GraphDataLayer
edgeType: GraphEdgeType
version: string
dataClassificationTargets: GraphDataClassification[]
}
Graph rules
contributionsmust contain at least one node or edge definition.schemaIdmust use reverse-domain format.versionmust be semver.readScopesmust contain at least one declared scope.writeScopesmay only use non-core write scopes.supportedEventSchemaVersionsmust contain semver strings.handledDataClassificationsmust enumerate every classification the runtime may touch.
Public graph read scopes
| Scope | Meaning |
|---|---|
graph.read.governed-core |
Canonical governed business facts. |
graph.read.observation-evidence |
Observation and evidence records. |
graph.read.derived-overlay |
Derived scores, forecasts, and overlays. |
graph.read.compliance |
Compliance and governance metadata. |
Public graph write scopes
| Scope | Meaning |
|---|---|
graph.write.observation-evidence |
Observation and evidence writes. |
graph.write.derived-overlay |
Derived and overlay writes. |
Optimization Extension Manifest
Add optimizationExtension when the plugin exposes strategy or simulation behavior that the product UI and policy controls need to understand.
interface PluginOptimizationExtension {
advisoryOnly: boolean
defaultRuntimeMode: 'inline' | 'worker'
defaultRecommendationCount: number
maxHorizon: number
maxRecommendationCount: number
scenarios?: OptimizationScenarioDefinition[]
requiresHumanReview: boolean
supportsApprovalWorkflow: boolean
supportsExternalWorkers: boolean
supportsOutcomeCapture: boolean
supportedGameClasses: OptimizationGameClass[]
supportedSolverFamilies: OptimizationSolverFamily[]
supportedTimeScales: OptimizationTimeScale[]
supportedUtilityTopologies: GameUtilityTopology[]
timeoutMs: number
}
Scenario shape
interface OptimizationScenarioDefinition {
description: string
defaults: {
gameClass: OptimizationGameClass
horizon?: number
timeScale: OptimizationTimeScale
utilityTopology?: GameUtilityTopology
}
id: string
label: string
tier: 'basic' | 'advanced' | 'premium'
}
Optimization rules
defaultRecommendationCountandmaxRecommendationCountmust be integers from 1 to 5.maxHorizonmust be an integer from 1 to 365.timeoutMsmust be an integer from 100 to 300000.- Every scenario
idmust be lowercase kebab-case or dot-separated lowercase segments. - Every support array must be non-empty.
- Use scenario IDs as stable public keys; do not rename them casually after publication.
Delivery and Compatibility
Event schema versioning
- Public event envelopes currently use schema version
1.0.0. - Graph-aware plugins should explicitly declare support through
graphExtension.supportedEventSchemaVersions. idempotencyKeyis the replay-safe key your runtime should persist or compare before making durable changes.
Webhook transport note
The manifest contract currently validates webhookEndpoint and webhook-handler, but you should not assume automatic external delivery outside approved rollout paths. Treat those fields as a reviewed integration surface rather than an always-on public transport guarantee.
Submission Lifecycle
The current product flow uses the following submission statuses:
| Status | Meaning |
|---|---|
PENDING |
Submitted and waiting for review. |
UNDER_REVIEW |
An admin has started the review. |
REJECTED |
The manifest was rejected. Review notes may be attached. |
PUBLISHED |
The manifest was revalidated and published into the plugin registry. |
Publishing behavior
- Submission-time validation happens in the Developer Portal before the record is created.
- Publish-time validation happens again in the admin flow before the plugin is upserted into the registry.
- Published third-party plugins are stored with
source: THIRD_PARTYandstatus: ACTIVE. - A developer cannot keep multiple active review-queue submissions open at once.
Validation Summary
| Rule | Detail |
|---|---|
id format |
Must match /^[a-z][a-z0-9]*(\.[a-z][a-z0-9-]*)+$/ |
version format |
Must match MAJOR.MINOR.PATCH |
capabilities |
Must be non-empty and use only registered names |
events |
Must use only the public event names accepted by the validator |
configSchema.type |
Must be object |
graphExtension.contributions |
Must be non-empty when present |
optimizationExtension arrays |
Must be non-empty when present |
webhookEndpoint |
Required when webhook-handler is declared |
Summary
Treat this document as the publishable contract for third-party plugins. If a field is not clearly implemented as public behavior yet, this document does not overstate it. Build from the Plugin SDK, validate against @nashtwin/common, and keep your in-product surfaces aligned with Plugin UI Best Practices.