> ## Documentation Index
> Fetch the complete documentation index at: https://docs.signa.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Class Coverage Audits

> Identify gaps in your Nice class coverage across a trademark portfolio. Map registered marks against business activities, spot unprotected jurisdictions, and generate a coverage matrix.

You are a trademark portfolio strategist at a technology company that sells SaaS products, hardware devices, and consulting services in 8 countries. Your CEO wants to know: "Are we fully protected?" You need to audit your Nice class coverage to identify which goods and services categories are unprotected in which jurisdictions -- and prioritize new filings to close the gaps.

This guide shows how to perform a class coverage audit using the Signa API.

## Prerequisites

* A Signa API key with `trademarks:read` scope
* An owner ID (or a list of trademark IDs) covering your registered marks
* A list of Nice classes relevant to your business

***

<Steps>
  <Step title="Define your target coverage matrix">
    Start by listing the Nice classes your business actually uses and the jurisdictions where you operate. This is your ideal state.

    ```typescript theme={null}
    // Define your target coverage
    const targetClasses = [
      { class: 9, description: "Software, hardware devices, downloadable apps" },
      { class: 35, description: "Business consulting, retail services, advertising" },
      { class: 36, description: "Financial services, payment processing" },
      { class: 38, description: "Telecommunications, cloud platform services" },
      { class: 41, description: "Training, education, conferences" },
      { class: 42, description: "SaaS, cloud computing, software development" },
    ];

    const targetOffices = ["uspto", "euipo", "ukipo", "cipo", "ipau", "jpo", "cnipa", "dpma"];

    console.log(
      `Target: ${targetClasses.length} classes x ${targetOffices.length} jurisdictions = ${targetClasses.length * targetOffices.length} cells`,
    );
    ```
  </Step>

  <Step title="Pull your current registrations and classify them">
    Fetch all marks in your portfolio, then map each one to the classes and jurisdictions it covers.

    <CodeGroup>
      ```bash cURL theme={null}
      # Get all marks in the portfolio (paginate through all)
      curl "https://api.signa.so/v1/owners/own_mycompany/trademarks?status_stage=registered,examining&limit=100" \
        -H "Authorization: Bearer $SIGNA_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      import Signa from "@signa-so/sdk";

      const signa = new Signa({ api_key: process.env.SIGNA_API_KEY });

      // Fetch all active marks (registered + pending)
      const activeStatuses = ["registered", "examining", "filed", "published", "opposition_period"];
      let allMarks: any[] = [];
      let cursor: string | null = null;

      do {
        const page = await signa.owners.trademarks("own_mycompany", {
          status_stage: activeStatuses,
          limit: 100,
          cursor,
        });
        allMarks.push(...page.data);
        cursor = page.has_more ? page.pagination.cursor : null;
      } while (cursor);

      console.log(`Total active marks: ${allMarks.length}`);

      // Build coverage map: jurisdiction -> class -> marks[]
      type CoverageMap = Record<string, Record<number, any[]>>;
      const coverage: CoverageMap = {};

      for (const tm of allMarks) {
        const office = tm.office_code;
        if (!coverage[office]) coverage[office] = {};

        for (const c of tm.classifications) {
          const cls = c.nice_class;
          if (!coverage[office][cls]) coverage[office][cls] = [];
          coverage[office][cls].push({
            id: tm.id,
            mark_text: tm.mark_text,
            status: tm.status.stage,
            filing_date: tm.filing_date,
            goods_services_text: c.goods_services_text,
          });
        }
      }
      ```

      ```python Python theme={null}
      import os
      import requests

      headers = {"Authorization": f"Bearer {os.environ['SIGNA_API_KEY']}"}

      active_statuses = ["registered", "examining", "filed", "published", "opposition_period"]
      all_marks = []
      cursor = None

      while True:
          params = {"status_stage": ",".join(active_statuses), "limit": 100}
          if cursor:
              params["cursor"] = cursor
          page = requests.get(
              "https://api.signa.so/v1/owners/own_mycompany/trademarks",
              params=params,
              headers=headers,
          ).json()
          all_marks.extend(page["data"])
          if not page["has_more"]:
              break
          cursor = page["pagination"]["cursor"]

      print(f"Total active marks: {len(all_marks)}")

      # Build coverage map
      coverage = {}
      for tm in all_marks:
          office = tm["office_code"]
          if office not in coverage:
              coverage[office] = {}
          for c in tm["classifications"]:
              cls = c["nice_class"]
              if cls not in coverage[office]:
                  coverage[office][cls] = []
              coverage[office][cls].append({
                  "id": tm["id"],
                  "mark_text": tm["mark_text"],
                  "status": tm["status"]["stage"],
                  "goods_services_text": c["goods_services_text"],
              })
      ```
    </CodeGroup>
  </Step>

  <Step title="Generate a coverage matrix">
    Compare your actual coverage against your target to find gaps.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      interface GapEntry {
        officeCode: string;
        niceClass: number;
        classDescription: string;
        status: "covered" | "pending_only" | "gap";
        marks: Array<{ id: string; mark_text: string; status: string }>;
      }

      const matrix: GapEntry[] = [];

      for (const office of targetOffices) {
        for (const cls of targetClasses) {
          const marks = coverage[office]?.[cls.class] || [];
          const registeredMarks = marks.filter((m) => m.status === "registered");
          const pendingMarks = marks.filter((m) => m.status !== "registered");

          let status: "covered" | "pending_only" | "gap";
          if (registeredMarks.length > 0) {
            status = "covered";
          } else if (pendingMarks.length > 0) {
            status = "pending_only";
          } else {
            status = "gap";
          }

          matrix.push({
            officeCode: office,
            niceClass: cls.class,
            classDescription: cls.description,
            status,
            marks,
          });
        }
      }

      // Print the matrix
      const gaps = matrix.filter((m) => m.status === "gap");
      const pendingOnly = matrix.filter((m) => m.status === "pending_only");
      const covered = matrix.filter((m) => m.status === "covered");

      console.log("\n=== Coverage Audit Results ===");
      console.log(`Total cells: ${matrix.length}`);
      console.log(`Covered (registered): ${covered.length} (${((covered.length / matrix.length) * 100).toFixed(0)}%)`);
      console.log(`Pending only: ${pendingOnly.length}`);
      console.log(`GAPS: ${gaps.length}`);

      console.log("\n--- GAPS (no coverage) ---");
      for (const gap of gaps) {
        console.log(`  ${gap.officeCode} / Class ${gap.niceClass}: ${gap.classDescription}`);
      }
      ```

      ```python Python theme={null}
      target_classes = [
          {"class": 9, "desc": "Software, hardware devices"},
          {"class": 35, "desc": "Business consulting, retail"},
          {"class": 36, "desc": "Financial services"},
          {"class": 38, "desc": "Telecommunications"},
          {"class": 41, "desc": "Training, education"},
          {"class": 42, "desc": "SaaS, cloud computing"},
      ]
      target_offices = ["uspto", "euipo", "ukipo", "cipo", "ipau", "jpo", "cnipa", "dpma"]

      gaps = []
      pending_only = []
      covered_cells = []

      for jur in target_offices:
          for cls in target_classes:
              marks = coverage.get(jur, {}).get(cls["class"], [])
              registered = [m for m in marks if m["status"] == "registered"]
              pending = [m for m in marks if m["status"] != "registered"]

              if registered:
                  covered_cells.append((jur, cls["class"]))
              elif pending:
                  pending_only.append((jur, cls["class"]))
              else:
                  gaps.append((jur, cls["class"], cls["desc"]))

      total = len(target_classes) * len(target_offices)
      print(f"\n=== Coverage Audit ===")
      print(f"Covered: {len(covered_cells)}/{total} ({len(covered_cells)/total*100:.0f}%)")
      print(f"Pending only: {len(pending_only)}")
      print(f"GAPS: {len(gaps)}")

      print("\n--- GAPS ---")
      for jur, cls, desc in gaps:
          print(f"  {jur} / Class {cls}: {desc}")
      ```
    </CodeGroup>

    **Expected output:**

    ```
    === Coverage Audit Results ===
    Total cells: 48
    Covered (registered): 32 (67%)
    Pending only: 4
    GAPS: 12

    --- GAPS (no coverage) ---
      AU / Class 36: Financial services
      AU / Class 38: Telecommunications
      AU / Class 41: Training, education
      JP / Class 35: Business consulting, retail services
      JP / Class 36: Financial services
      JP / Class 38: Telecommunications
      JP / Class 41: Training, education
      CN / Class 36: Financial services
      CN / Class 38: Telecommunications
      CN / Class 41: Training, education
      DE / Class 36: Financial services
      DE / Class 41: Training, education
    ```
  </Step>

  <Step title="Check Nice class details for gap classes">
    Before recommending new filings, verify the exact scope of each Nice class to ensure it matches your business activities.

    <CodeGroup>
      ```bash cURL theme={null}
      # Get class 36 details
      curl https://api.signa.so/v1/classifications/36 \
        -H "Authorization: Bearer $SIGNA_API_KEY"

      # Search for specific G&S terms in class 36
      curl "https://api.signa.so/v1/goods-services?q=payment+processing&class=36&limit=10" \
        -H "Authorization: Bearer $SIGNA_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      // Get details for each gap class
      const gapClasses = [...new Set(gaps.map((g) => g.niceClass))];

      for (const classNum of gapClasses) {
        const classification = await signa.references.classification(classNum);
        console.log(`\nClass ${classNum}: ${classification.title}`);
        console.log(`  Category: ${classification.category}`);

        // Search for relevant terms
        const terms = await signa.goodsServices.list({
          q: "software", // adjust per class
          class: classNum,
          limit: 5,
        });

        if (terms.data.length > 0) {
          console.log("  Relevant terms:");
          for (const t of terms.data) {
            console.log(`    - ${t.term} ${t.is_harmonised ? "(harmonised)" : ""}`);
          }
        }
      }
      ```

      ```python Python theme={null}
      gap_classes = list({cls for _, cls, _ in gaps})

      for cls_num in gap_classes:
          classification = requests.get(
              f"https://api.signa.so/v1/classifications/{cls_num}",
              headers=headers,
          ).json()
          print(f"\nClass {cls_num}: {classification['title']}")
          print(f"  Category: {classification['category']}")
      ```
    </CodeGroup>
  </Step>

  <Step title="Prioritize gap closures">
    Not all gaps are equally urgent. Prioritize based on business revenue in each jurisdiction and the importance of the class.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      interface GapPriority {
        jurisdiction: string;
        niceClass: number;
        description: string;
        priority: "critical" | "high" | "medium" | "low";
        reason: string;
      }

      // Define business priority weights
      const officePriority: Record<string, number> = {
        us: 10, eu: 9, gb: 8, ca: 7, de: 6, au: 5, jp: 4, cn: 3,
      };

      const classPriority: Record<number, number> = {
        9: 10,  // Core product
        42: 10, // Core service
        35: 7,  // Business services
        38: 6,  // Telecom
        36: 5,  // Financial
        41: 3,  // Training
      };

      const prioritizedGaps: GapPriority[] = gaps.map((gap) => {
        const score =
          (officePriority[gap.officeCode] || 1) *
          (classPriority[gap.niceClass] || 1);

        let priority: "critical" | "high" | "medium" | "low";
        if (score >= 70) priority = "critical";
        else if (score >= 40) priority = "high";
        else if (score >= 20) priority = "medium";
        else priority = "low";

        return {
          jurisdiction: gap.officeCode,
          niceClass: gap.niceClass,
          description: gap.classDescription,
          priority,
          reason: `Score ${score} (jur: ${officePriority[gap.officeCode]}, class: ${classPriority[gap.niceClass]})`,
        };
      });

      // Sort by priority
      const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
      prioritizedGaps.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);

      console.log("\n=== Prioritized Filing Recommendations ===");
      for (const gap of prioritizedGaps) {
        console.log(`  [${gap.priority.toUpperCase()}] ${gap.officeCode} / Class ${gap.niceClass}: ${gap.description}`);
      }
      ```

      ```python Python theme={null}
      office_priority = {"uspto": 10, "euipo": 9, "ukipo": 8, "cipo": 7, "dpma": 6, "ipau": 5, "jpo": 4, "cnipa": 3}
      cls_priority = {9: 10, 42: 10, 35: 7, 38: 6, 36: 5, 41: 3}

      prioritized = []
      for jur, cls, desc in gaps:
          score = office_priority.get(jur, 1) * cls_priority.get(cls, 1)
          if score >= 70:
              priority = "CRITICAL"
          elif score >= 40:
              priority = "HIGH"
          elif score >= 20:
              priority = "MEDIUM"
          else:
              priority = "LOW"
          prioritized.append((priority, jur, cls, desc, score))

      prioritized.sort(key=lambda x: {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}[x[0]])

      print("\n=== Prioritized Filing Recommendations ===")
      for priority, jur, cls, desc, score in prioritized:
          print(f"  [{priority}] {jur} / Class {cls}: {desc}")
      ```
    </CodeGroup>

    **Expected output:**

    ```
    === Prioritized Filing Recommendations ===
      [HIGH] JP / Class 35: Business consulting, retail services
      [HIGH] DE / Class 36: Financial services
      [MEDIUM] AU / Class 36: Financial services
      [MEDIUM] AU / Class 38: Telecommunications
      [MEDIUM] CN / Class 36: Financial services
      [MEDIUM] CN / Class 38: Telecommunications
      [MEDIUM] JP / Class 36: Financial services
      [MEDIUM] JP / Class 38: Telecommunications
      [LOW] AU / Class 41: Training, education
      [LOW] JP / Class 41: Training, education
      [LOW] CN / Class 41: Training, education
      [LOW] DE / Class 41: Training, education
    ```
  </Step>

  <Step title="Check for competitor activity in your gap areas">
    Before filing, check whether competitors already hold registrations in your gap areas. This informs both the filing strategy and the likelihood of opposition.

    <CodeGroup>
      ```bash cURL theme={null}
      # Search for competitor marks in a gap area
      curl -X POST https://api.signa.so/v1/trademarks \
        -H "Authorization: Bearer $SIGNA_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "query": "YourBrandName",
          "strategies": ["exact", "phonetic"],
          "filters": {
            "offices": ["jpo"],
            "nice_classes": [35],
            "status_stage": ["registered", "examining"]
          },
          "options": {
            "aggregations": ["office_code", "status_stage"],
            "include_total": true
          },
          "limit": 10
        }'
      ```

      ```typescript TypeScript theme={null}
      // For each critical/high gap, check for conflicting marks
      for (const gap of prioritizedGaps.filter((g) => g.priority === "critical" || g.priority === "high")) {
        const conflicts = await signa.trademarks.list({
          query: "YourBrandName",
          strategies: ["exact", "phonetic"],
          filters: {
            offices: [gap.officeCode],
            nice_classes: [gap.niceClass],
            status_stage: ["registered", "examining"],
          },
          options: { include_total: true },
          limit: 5,
        });

        console.log(
          `\n${gap.officeCode} / Class ${gap.niceClass}: ${conflicts.search_meta.total_results} potential conflicts`,
        );
        for (const tm of conflicts.data) {
          console.log(`  ${tm.mark_text} (${tm.status.stage}) - Score: ${tm.relevance_score}`);
        }
      }
      ```

      ```python Python theme={null}
      for priority, jur, cls, desc, score in prioritized:
          if priority not in ("CRITICAL", "HIGH"):
              continue

          conflicts = requests.post(
              "https://api.signa.so/v1/trademarks",
              headers={**headers, "Content-Type": "application/json"},
              json={
                  "query": "YourBrandName",
                  "strategies": ["exact", "phonetic"],
                  "filters": {
                      "offices": [jur],
                      "nice_classes": [cls],
                      "status_stage": ["registered", "examining"],
                  },
                  "options": {"include_total": True},
                  "limit": 5,
              },
          ).json()

          total = conflicts["search_meta"]["total_results"]
          print(f"\n{jur} / Class {cls}: {total} potential conflicts")
      ```
    </CodeGroup>
  </Step>
</Steps>

***

## Coverage matrix visualization

Build a simple text matrix to share with stakeholders:

```typescript theme={null}
function printMatrix(
  jurisdictions: string[],
  classes: Array<{ class: number }>,
  coverage: CoverageMap,
) {
  // Header
  const header = "     | " + classes.map((c) => `C${c.class}`.padStart(4)).join(" | ");
  console.log(header);
  console.log("-".repeat(header.length));

  for (const jur of jurisdictions) {
    const cells = classes.map((cls) => {
      const marks = coverage[jur]?.[cls.class] || [];
      const hasRegistered = marks.some((m) => m.status === "registered");
      const hasPending = marks.some((m) => m.status !== "registered");

      if (hasRegistered) return "  OK";
      if (hasPending) return " PEN";
      return " GAP";
    });

    console.log(`${jur.padStart(4)} | ${cells.join(" | ")}`);
  }
}

printMatrix(targetOffices, targetClasses, coverage);
```

**Output:**

```
     |   C9 |  C35 |  C36 |  C38 |  C41 |  C42
-----------------------------------------------------
  US |   OK |   OK |   OK |   OK |   OK |   OK
  EU |   OK |   OK |   OK |   OK |  PEN |   OK
  GB |   OK |   OK |   OK |  PEN |   OK |   OK
  CA |   OK |   OK |   OK |   OK |   OK |   OK
  AU |   OK |   OK |  GAP |  GAP |  GAP |   OK
  JP |   OK |  GAP |  GAP |  GAP |  GAP |   OK
  CN |   OK |   OK |  GAP |  GAP |  GAP |   OK
  DE |   OK |   OK |  GAP |   OK |  GAP |   OK
```

***

## What's next

<Card title="Trademark Clearance" href="/guides/use-cases/trademark-clearance">
  Run clearance searches before filing in your gap jurisdictions.
</Card>

<Card title="Renewal Management" href="/guides/use-cases/renewal-management">
  Ensure existing coverage does not lapse while you work on closing gaps.
</Card>
