Create a Plugin

Build your first bizSupply plugin — from scaffolding to registration — using the Python SDK.

Last updated: 2026-04-01

This guide walks you through creating your first bizSupply plugin. By the end, you will have a working Classification plugin that you can register with the platform and use in a pipeline.


Install the SDK

The bizSupply SDK is distributed as a Python package. Install it with pip:

bash
pip install bizsupply-sdk

The SDK requires Python 3.10 or later. Verify your installation:

bash
python -c "import bizsupply_sdk; print(bizsupply_sdk.__version__)"
="color:#5c6370;font-style:italic"># Output: 1.4.2

Plugin Types

bizSupply supports four plugin types. Choose the one that matches your use case.

TypeBase ClassWhen to Use
SourceSourcePluginYou need to ingest documents from an external system (email, cloud storage, API, FTP).
ClassificationClassificationPluginYou need to determine the type of a document (invoice, contract, receipt, etc.).
ExtractionExtractionPluginYou need to pull structured fields from a document based on an ontology.
AggregationAggregationPluginYou need to normalize, enrich, or combine data across multiple documents.

Critical Requirements

All plugins must satisfy these requirements. Violating any of them will cause registration or execution failures.

RequirementDetailsWhat Happens If Violated
Single required methodEach plugin type has exactly one method you must implement (e.g., classify for Classification).Plugin fails validation at registration time.
Return the correct typeclassify() must return a string, extract() must return a dict, fetch_documents() must return a list.Runtime error — the pipeline stage fails and the job enters a partial or failed state.
No side effects on platform statePlugins must not directly modify documents, pipelines, or other platform resources. Return data and let the platform handle persistence.Undefined behavior — data corruption is possible.
Handle errors gracefullyRaise PluginError for expected failures. Unhandled exceptions cause the entire job to fail.Job enters failed state instead of partial — retryable documents are lost.
Idempotent executionRunning the same plugin on the same document twice must produce the same result. The platform may retry failed documents.Duplicate or inconsistent extracted data.
No blocking I/O in the main threadUse the provided async helpers (e.g., prompt_llm) for any LLM calls or external API requests. Do not use time.sleep() or synchronous HTTP clients.Worker thread starvation — other documents in the same job are delayed.

Quick Start

1

Choose Your Plugin Type

For this guide, we will create a Classification plugin. This plugin will analyze a document and determine whether it is an invoice, a purchase order, or an unknown type.

2

Scaffold with the CLI

The SDK includes a scaffolding CLI that generates a plugin project with the correct structure:

bash
bizsupply scaffold classification my-classifier
cd my-classifier/

This creates the following structure:

text
my-classifier/
├── plugin.py          # Your plugin implementation
├── plugin.yaml        # Plugin metadata and configuration
├── requirements.txt   # Python dependencies
└── tests/
    └── test_plugin.py # Unit tests
3

Validate Your Plugin

Before registering, validate that your plugin meets all requirements:

bash
bizsupply validate ./plugin.py
="color:#5c6370;font-style:italic"># ✓ Plugin class found: MyClassifier
="color:#5c6370;font-style:italic"># ✓ Base class: ClassificationPlugin
="color:#5c6370;font-style:italic"># ✓ Required method implemented: classify
="color:#5c6370;font-style:italic"># ✓ Return type annotation: str
="color:#5c6370;font-style:italic"># ✓ Plugin metadata valid
="color:#5c6370;font-style:italic"># All checks passed.

Plugin Structure

Here is a complete Classification plugin that uses the LLM service to classify documents:

plugin.pypython
from bizsupply_sdk import ClassificationPlugin, PluginError


class InvoiceClassifier(ClassificationPlugin):
    """Classifies documents as invoice, purchase_order, or unknown."""

    # Plugin metadata
    name = "invoice-classifier"
    version = "1.0.0"
    description = "Classifies financial documents using LLM analysis."

    # Configurable parameters (set via pipeline config)
    confidence_threshold: float = 0.8

    def classify(self, document) -> str:
        """
        Analyze the document content and return a document type string.

        Args:
            document: A Document object with properties:
                - content: str (extracted text content)
                - filename: str
                - mime_type: str
                - metadata: dict

        Returns:
            A string representing the document type (e.g., "invoice").
        """
        # Guard: ensure we have content to classify
        if not document.content or len(document.content.strip()) == 0:
            raise PluginError(
                "Document has no extractable text content.",
                retryable=False,
            )

        # Build the classification prompt
        prompt = f"""Analyze the following document and classify it as one of:
- invoice
- purchase_order
- receipt
- contract
- unknown

Document filename: {document.filename}
Document content:
{document.content[:4000]}

Respond with ONLY the document type, nothing else."""

        # Call the LLM via the platform service
        result = self.prompt_llm(prompt)
        doc_type = result.strip().lower()

        # Validate the result
        valid_types = {"invoice", "purchase_order", "receipt", "contract", "unknown"}
        if doc_type not in valid_types:
            return "unknown"

        return doc_type

Register Your Plugin

Once validated, register the plugin with the platform:

bash
curl -X POST https://api.bizsupply.com/v1/plugins \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "invoice-classifier",
    "type": "classification",
    "version": "1.0.0",
    "description": "Classifies financial documents using LLM analysis.",
    "module_path": "invoice_classifier.plugin.InvoiceClassifier",
    "config_schema": {
      "confidence_threshold": {
        "type": "number",
        "default": 0.8,
        "description": "Minimum confidence score to accept a classification."
      }
    }
  }'

The API returns the registered plugin with its assigned ID:

json
{
  "id": "plg_a1b2c3d4",
  "name": "invoice-classifier",
  "type": "classification",
  "version": "1.0.0",
  "status": "active",
  "created_at": "2026-01-15T10: 30: 00Z"
}

Common Mistakes

These are the most frequent errors when developing plugins.

1. Returning the wrong type from classify()

python
# WRONG — returning a dict instead of a string
def classify(self, document) -> str:
    return {"type": "invoice", "confidence": 0.95}

# CORRECT — return only the document type string
def classify(self, document) -> str:
    return "invoice"

2. Modifying the document object directly

python
# WRONG — mutating the document
def extract(self, document, fields) -> dict:
    document.status = "extracted"  # Don't do this!
    return {"vendor": "Acme"}

# CORRECT — return the extracted data, let the platform handle state
def extract(self, document, fields) -> dict:
    return {"vendor": "Acme"}

3. Using synchronous HTTP instead of platform services

python
# WRONG — blocking synchronous HTTP call
import requests
def classify(self, document) -> str:
    resp = requests.post("https://my-llm.com/classify", json={"text": document.content})
    return resp.json()["type"]

# CORRECT — use the platform's LLM service
def classify(self, document) -> str:
    result = self.prompt_llm(f"Classify this document: {document.content[:4000]}")
    return result.strip().lower()

Next Steps

  • Read the Plugin Interface Specification for the full API reference, including all available service methods.
  • Create an Ontology to define the fields your Extraction plugin will populate.
  • Create a Pipeline to connect your plugin with sources and ontologies.
  • Test your plugin locally using the SDK test harness: bizsupply test ./plugin.py --document sample.pdf