Build a Plugin - Docs menu

Build a Plugin

Plugins let a Navigic agent call an external service through a reviewed, versioned contract. A plugin is not loaded into Navigic as arbitrary code. It is a service you operate, described by a manifest, and called through the navigic.plugin/v1 protocol.

Current Availability

Plugin development is in managed preview. You can build a conforming plugin service and submit package evidence to Navigic for review. Self-serve marketplace discovery and dashboard installation for third-party plugins are not generally available yet.

If your goal is to connect a reviewed plugin to a workspace, start with Install a Plugin.

Managed-Preview Submission Flow

When Navigic has issued a developer API token for your account, you can submit a plugin release from the public devkit. The CLI stores the token outside the plugin repo and also accepts NAVIGIC_API_TOKEN and NAVIGIC_API_HOST.

npm exec --yes --package @navigicai/plugin-devkit@latest -- navigic plugin login --token <developer-api-token>
npm exec --yes --package @navigicai/plugin-devkit@latest -- navigic plugin publisher create example-inc \
  --display-name "Example Inc." \
  --website https://example.com \
  --support-url https://example.com/support \
  --contact-email [email protected]
npm exec --yes --package @navigicai/plugin-devkit@latest -- navigic plugin publisher verify-domain example-inc \
  --domain example.com \
  --method dns_txt
npm exec --yes --package @navigicai/plugin-devkit@latest -- navigic plugin package --out-dir dist --sbom
npm exec --yes --package @navigicai/plugin-devkit@latest -- navigic plugin verify-package dist/navigic-plugin-package.json --strict-package
npm exec --yes --package @navigicai/plugin-devkit@latest -- navigic plugin submit \
  --publisher example-inc \
  --package dist/navigic-plugin-package.json \
  --plugin-url https://plugins.example.com/example-support/
npm exec --yes --package @navigicai/plugin-devkit@latest -- navigic plugin status <submission-id> --watch

Browser login is not available in the current CLI slice. Domain verification may remain pending while Navigic review is managed-preview. A submitted package creates durable review evidence and a status object; it is not marketplace approval, public discovery, workspace install eligibility, or runtime operation granting.

If review requests changes, fix the plugin, package it again, and link the next submission to the prior review record:

npm exec --yes --package @navigicai/plugin-devkit@latest -- navigic plugin package --out-dir dist --sbom
npm exec --yes --package @navigicai/plugin-devkit@latest -- navigic plugin submit \
  --publisher example-inc \
  --package dist/navigic-plugin-package.json \
  --resubmit <previous-submission-id>

Scaffolded Development Loop

Navigic's public development loop is being introduced in stages. The first developer-facing milestone is scaffolding and local validation with the public devkit package. Use Node.js 22.14.0 or newer.

npm exec --yes --package @navigicai/plugin-devkit@latest -- navigic plugin init hello-action
cd hello-action
npm install --save-dev @navigicai/plugin-devkit@latest
npm test
npx navigic plugin validate contracts/navigic-plugin.manifest.json
npx navigic plugin package --out-dir dist --sbom
npx navigic plugin verify-package dist/navigic-plugin-package.json

This loop should let you build a conforming remote HTTP plugin without access to private Navigic repositories. It validates the manifest, starts a local host simulator, issues fake operation-scoped credential leases, and executes the plugin through the same navigic.plugin/v1 envelope used by hosted workspaces.

To test a running plugin service locally, start the scaffolded plugin in one terminal:

npm run dev

Then start the Navigic local host simulator in a second terminal:

npx navigic plugin dev \
  --plugin-url http://127.0.0.1:8787 \
  --service-token local-dev-token \
  --credential-lease-file fixtures/leases/local.json

The dev host prints a control token. Use that token from another terminal to execute an operation through the local host envelope:

npx navigic plugin execute create_ticket \
  --input fixtures/execute/create-ticket.json \
  --token <dev-host-control-token>

The public package is @navigicai/plugin-devkit. Older commands or private repo-local paths such as ../navigic-plugin-devkit/dist/cli.js are not required for the public development loop.

Local success and CLI submission are not production approval. Production installation still requires Navigic review, verified publisher metadata, link and manifest checks, and a workspace install path enabled by Navigic.

What You Build

A remote HTTP plugin should expose these routes:

RoutePurpose
GET /.well-known/navigic-plugin.jsonReturns the immutable plugin manifest.
GET /healthReports whether the plugin service is ready.
POST /navigic/plugin/v1/executeExecutes one manifest operation for the Navigic host.

Provider-facing routes, such as OAuth callbacks or webhooks, are plugin-specific. For example, a Slack plugin may also expose /slack/oauth/callback and /slack/events.

Build Steps

  1. Choose the narrow job the plugin performs.

    Start with one or two operations. Give each operation a stable operationId, model-facing toolName, JSON input schema, side effect, risk level, required scopes, approval default, and idempotency behavior.

  2. Publish a manifest.

    The manifest declares the plugin's protocol version, package version, structured publisher, required license, trust links, release metadata, runtime routes, auth scheme, permissions, data use, operations, events, outbound domains, and redaction fields.

    {
      "apiVersion": "navigic.plugin/v1",
      "kind": "PluginManifest",
      "metadata": {
        "name": "example-support",
        "version": "0.1.0",
        "displayName": "Example Support",
        "description": "Create and read support tickets.",
        "license": "Proprietary",
        "publisher": {
          "id": "example-inc",
          "displayName": "Example Inc.",
          "type": "community_preview",
          "website": "https://example.com",
          "verifiedDomain": "example.com",
          "verificationStatus": "pending",
          "supportUrl": "https://example.com/support",
          "contactEmail": "[email protected]"
        }
      },
      "links": {
        "homepageUrl": "https://example.com/navigic-plugin",
        "docsUrl": "https://example.com/navigic-plugin/docs",
        "installGuideUrl": "https://example.com/navigic-plugin/install",
        "configurationGuideUrl": "https://example.com/navigic-plugin/configure",
        "changelogUrl": "https://example.com/navigic-plugin/changelog",
        "supportUrl": "https://example.com/support",
        "privacyPolicyUrl": "https://example.com/privacy",
        "termsUrl": "https://example.com/terms",
        "securityUrl": "https://example.com/security",
        "repositoryUrl": "https://github.com/example/example-support-plugin"
      },
      "release": {
        "version": "0.1.0",
        "releasedAt": "2026-07-22T00:00:00.000Z",
        "releaseNotes": "Initial preview release.",
        "breakingChanges": [],
        "migrationNotes": []
      },
      "hostCompatibility": {
        "protocolVersions": ["navigic.plugin/v1"],
        "navigicMinVersion": "0.1.0"
      },
      "runtime": {
        "kind": "remote-http",
        "wellKnownPath": "/.well-known/navigic-plugin.json",
        "executePath": "/navigic/plugin/v1/execute",
        "healthPath": "/health"
     },
     "auth": {
       "schemes": [
         {
           "type": "service-token",
           "purpose": "Authenticate Navigic host-to-plugin execute requests."
         },
         {
           "type": "api-key",
           "provider": "example-support",
           "location": "credentialLease",
           "scopes": ["tickets:write"],
           "purpose": "Use a short-lived operation-scoped provider credential lease.",
           "credentialLeaseFields": ["apiKey"]
         }
       ]
     },
      "operations": [
        {
          "operationId": "create_ticket",
          "toolName": "example_support__create_ticket",
          "displayName": "Create Ticket",
          "description": "Create a customer support ticket.",
          "inputSchema": {
            "type": "object",
            "properties": {
              "subject": { "type": "string" },
              "body": { "type": "string" }
            },
            "required": ["subject", "body"],
            "additionalProperties": false
          },
          "outputSchema": { "type": "object" },
          "sideEffect": "external-send",
          "risk": "external-write",
          "requiredScopes": ["tickets:write"],
          "approval": { "defaultMode": "ask" },
          "idempotency": "required",
          "modelHints": {
            "readOnlyHint": false,
            "destructiveHint": false,
            "openWorldHint": true
          }
        }
      ],
      "permissions": {
        "providerScopes": [
          {
            "provider": "example-support",
            "scope": "tickets:write",
            "purpose": "Let an approved agent create a ticket for handoff.",
            "dataAccess": ["ticket subject", "ticket body", "workspace metadata"],
            "operations": ["create_ticket"],
            "required": true,
            "risk": "external-write",
            "providerDocsUrl": "https://example.com/docs/scopes/tickets-write"
          }
        ],
        "outboundDomains": [
          {
            "domain": "api.example.com",
            "purpose": "Create tickets through the Example Support API.",
            "dataCategories": ["ticket subject", "ticket body", "workspace metadata"]
          }
        ]
      },
      "dataUse": {
        "collectedDataCategories": ["ticket subject", "ticket body", "workspace metadata"],
        "processedDataCategories": ["ticket subject", "ticket body"],
        "storedDataCategories": ["installation metadata", "activity and audit records"],
        "retention": "Retained as needed to operate the workspace and support audit or deletion requests.",
        "sharedWith": ["Example Support"],
        "humanAccess": "Example and Navigic support operators may inspect redacted operation metadata for support.",
        "modelExposure": "Ticket subject and body may be model-visible when the agent invokes the operation.",
        "deletionInstructions": "Revoke the plugin installation and request ticket deletion in Example Support."
      },
      "outboundDomains": ["api.example.com"],
      "observability": {
        "redactFields": ["authorization", "access_token", "credentialLease"]
      }
    }
  3. Implement the execute endpoint.

    Accept only authenticated host calls. Validate the request, enforce operation support, reject expired credential leases, require idempotency keys for write operations, and return a structured success or failure result.

    {
      "protocolVersion": "navigic.plugin/v1",
      "requestId": "req_123",
      "workspaceId": "workspace_123",
      "installationId": "install_123",
      "pluginId": "example-support",
      "pluginVersion": "0.1.0",
      "operationId": "create_ticket",
      "toolName": "example_support__create_ticket",
      "input": {
        "subject": "Billing question",
        "body": "The customer needs help with an invoice."
      },
      "idempotencyKey": "workspace_123:req_123:create_ticket",
      "deadlineAt": "2026-07-21T20:00:00.000Z"
    }
  4. Keep credentials out of model-visible data.

    Long-lived customer credentials belong in Navigic's host-side installation store or in the provider account you operate. Runtime calls should use a short-lived credentialLease or a host service token, not raw customer secrets in prompts, transcripts, or logs.

  5. Test the plugin as a protocol boundary.

    At minimum, test manifest validation, health, authentication failure, unknown operations, invalid input, expired leases, write idempotency, provider rate limits, and redacted logs.

Agent Enablement

An agent should grant specific plugin operations, not the whole provider. In an agent spec, the capability is operation-level:

capabilities:
  pluginOperations:
    - pluginId: example-support
      operationId: create_ticket
      versionRange: ^0.1.0

The compiler lowers that into a bundle tool entry and a policy allowlist such as example-support.create_ticket.