> ## 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.

# M&A Due Diligence

> Evaluate a target company's trademark portfolio during an acquisition. Map the owner hierarchy via GLEIF, enumerate all marks across subsidiaries, check proceedings and litigation risk, assess portfolio health, and verify the ownership transfer after close.

You are an IP counsel advising on the acquisition of **Helios Consumer Brands Inc.**, a mid-market consumer goods company. Before the deal closes, you need a complete picture of their trademark portfolio: how many marks they hold, where, in what condition, and what risks exist (pending oppositions, upcoming deadlines, lapsed registrations).

This guide shows how to perform trademark due diligence, and then verify the ownership transfer once the deal closes, using the Signa API.

## Prerequisites

* A Signa API key with `trademarks:read` scope
* The target company name or known identifiers (ticker symbol, LEI, owner ID)

***

<Steps>
  <Step title="Identify the target's owner record">
    Search for the target by name, ticker symbol, or LEI (Legal Entity Identifier). The ticker and LEI filters join through Signa's public companies data (SEC + GLEIF).

    <CodeGroup>
      ```bash cURL theme={null}
      # Search by name
      curl "https://api.signa.so/v1/owners?q=Helios+Consumer+Brands&limit=5" \
        -H "Authorization: Bearer $SIGNA_API_KEY"

      # Or search by ticker if publicly traded
      curl "https://api.signa.so/v1/owners?ticker=HLCS&limit=5" \
        -H "Authorization: Bearer $SIGNA_API_KEY"

      # Or search by LEI
      curl "https://api.signa.so/v1/owners?lei=5493001KJTIIGC8Y1R12&limit=5" \
        -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 });

      // Try by name first
      const results = await signa.owners.list({ q: "Helios Consumer Brands", limit: 5 });

      // Or by ticker
      // const results = await signa.owners.list({ ticker: "HLCS", limit: 5 });

      const target = results.data[0];
      console.log(`Target: ${target.name} (${target.id})`);
      console.log(`Trademark count: ${target.trademark_count}`);
      ```
    </CodeGroup>

    **Expected output:**

    ```json theme={null}
    {
      "id": "own_helios01",
      "name": "Helios Consumer Brands Inc.",
      "canonical_name": "HELIOS CONSUMER BRANDS INC",
      "country_code": "US",
      "entity_type": "corporation",
      "trademark_count": 234
    }
    ```
  </Step>

  <Step title="Map the corporate hierarchy via GLEIF">
    Acquisition targets often hold trademarks through subsidiaries. Use the GLEIF corporate relationship data to identify the full ownership tree.

    <CodeGroup>
      ```bash cURL theme={null}
      # Get the target's full profile including public company data
      curl https://api.signa.so/v1/owners/own_helios01 \
        -H "Authorization: Bearer $SIGNA_API_KEY"

      # Get corporate relationships (parent/child companies)
      curl https://api.signa.so/v1/owners/own_helios01/related \
        -H "Authorization: Bearer $SIGNA_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      // Get full owner detail including public company matches
      const ownerDetail = await signa.owners.retrieve("own_helios01");

      if (ownerDetail.companies) {
        for (const pc of ownerDetail.companies) {
          console.log(`Public company: ${pc.legal_name} (${pc.source}: ${pc.source_id})`);
          if (pc.ticker) console.log(`  Ticker: ${pc.ticker} (${pc.exchange})`);
        }
      }

      // Get corporate hierarchy
      const related = await (await signa.owners.related("own_helios01")).toArray();

      console.log(`\nCorporate relationships: ${related.length}`);
      for (const rel of related) {
        console.log(
          `  ${rel.direction === "child" ? "Subsidiary" : "Parent"}: ${rel.name} (${rel.country_code})`,
        );
        console.log(`    Relationship: ${rel.relationship_type}`);
        console.log(`    Ownership: ${rel.ownership_pct ? rel.ownership_pct + "%" : "unknown"}`);
        console.log(`    Owner ID: ${rel.related_owner_id}`);
      }
      ```
    </CodeGroup>

    **Expected output:**

    ```json theme={null}
    {
      "object": "list",
      "data": [
        {
          "related_owner_id": "own_hel_eu01",
          "name": "Helios Brands Europe GmbH",
          "country_code": "DE",
          "relationship_type": "IS_DIRECTLY_CONSOLIDATED_BY",
          "direction": "child",
          "ownership_pct": 100.0
        },
        {
          "related_owner_id": "own_hel_asia01",
          "name": "Helios Asia Pacific Pte Ltd",
          "country_code": "SG",
          "relationship_type": "IS_DIRECTLY_CONSOLIDATED_BY",
          "direction": "child",
          "ownership_pct": 100.0
        }
      ]
    }
    ```

    <Note>
      GLEIF relationships use standardized vocabulary. `IS_DIRECTLY_CONSOLIDATED_BY` means the child entity is directly owned by the parent. Use `direction` to determine which side of the relationship the target sits on.
    </Note>
  </Step>

  <Step title="Enumerate all marks across the corporate family">
    Collect trademarks from the target and all subsidiaries. This gives you the full scope of what is being acquired.

    <CodeGroup>
      ```bash cURL theme={null}
      # Get marks from the parent
      curl "https://api.signa.so/v1/owners/own_helios01/trademarks?limit=100" \
        -H "Authorization: Bearer $SIGNA_API_KEY"

      # Get marks from each subsidiary
      curl "https://api.signa.so/v1/owners/own_hel_eu01/trademarks?limit=100" \
        -H "Authorization: Bearer $SIGNA_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      // Collect all owner IDs (parent + subsidiaries)
      const familyOwnerIds = [
        "own_helios01",
        ...related.filter((r) => r.direction === "child" && r.related_owner_id).map((r) => r.related_owner_id),
      ];

      console.log(`Corporate family: ${familyOwnerIds.length} entities`);

      // Enumerate marks across the family. SignaList follows cursors automatically,
      // so toArray() collects every page without a manual cursor loop.
      const allMarks: Awaited<ReturnType<typeof signa.owners.trademarks>>["data"] = [];

      for (const ownerId of familyOwnerIds) {
        const marks = await (await signa.owners.trademarks(ownerId, { limit: 100 })).toArray();
        allMarks.push(...marks);
      }

      console.log(`Total marks across family: ${allMarks.length}`);
      ```
    </CodeGroup>
  </Step>

  <Step title="Assess portfolio health">
    Analyze the collected marks to build a health scorecard: status distribution, jurisdiction coverage, upcoming deadlines, and risk indicators.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      // Status distribution
      const statusDist: Record<string, number> = {};
      const jurisdictionDist: Record<string, number> = {};
      const classDist: Record<number, number> = {};

      for (const tm of allMarks) {
        statusDist[tm.status.stage] = (statusDist[tm.status.stage] || 0) + 1;
        jurisdictionDist[tm.jurisdiction_code] = (jurisdictionDist[tm.jurisdiction_code] || 0) + 1;
        for (const c of tm.classifications) {
          classDist[c.nice_class] = (classDist[c.nice_class] || 0) + 1;
        }
      }

      const registered = statusDist["registered"] || 0;
      const abandoned = statusDist["abandoned"] || 0;
      const expired = statusDist["expired"] || 0;
      const pendingCount = (statusDist["filed"] || 0) + (statusDist["examining"] || 0);

      console.log("\n=== Portfolio Health Scorecard ===");
      console.log(`Total marks: ${allMarks.length}`);
      console.log(`Registered: ${registered} (${((registered / allMarks.length) * 100).toFixed(0)}%)`);
      console.log(`Pending: ${pendingCount}`);
      console.log(`Abandoned/Expired: ${abandoned + expired}`);
      console.log(`Jurisdictions: ${Object.keys(jurisdictionDist).length}`);
      console.log(`Nice classes covered: ${Object.keys(classDist).length}`);
      console.log("\nStatus breakdown:", statusDist);
      console.log("Top jurisdictions:", Object.entries(jurisdictionDist).sort((a, b) => b[1] - a[1]).slice(0, 5));
      console.log("Top classes:", Object.entries(classDist).sort((a, b) => b[1] - a[1]).slice(0, 10));
      ```
    </CodeGroup>

    **Expected output:**

    ```
    === Portfolio Health Scorecard ===
    Total marks: 312
    Registered: 241 (77%)
    Pending: 38
    Abandoned/Expired: 33
    Jurisdictions: 14
    Nice classes covered: 18

    Status breakdown: { registered: 241, examining: 28, filed: 10, abandoned: 18, expired: 15 }
    Top jurisdictions: [["us", 124], ["eu", 68], ["cn", 34], ["gb", 28], ["de", 22]]
    Top classes: [["3", 89], ["5", 72], ["35", 56], ["21", 45], ["29", 38]]
    ```
  </Step>

  <Step title="Check for active proceedings and litigation risk">
    Identify any pending oppositions, cancellations, or other proceedings that could affect the portfolio's value.

    <CodeGroup>
      ```bash cURL theme={null}
      # Check proceedings where the target is a party (as respondent or opponent)
      curl "https://api.signa.so/v1/proceedings?party_owner_id=own_helios01&limit=50" \
        -H "Authorization: Bearer $SIGNA_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      // Check proceedings for each entity in the family
      const allProceedings: Awaited<ReturnType<typeof signa.proceedings.list>>["data"] = [];

      for (const ownerId of familyOwnerIds) {
        const proceedings = await signa.proceedings.list({
          party_owner_id: ownerId,
          limit: 50,
        });
        allProceedings.push(...proceedings.data);
      }

      const pendingProceedings = allProceedings.filter((p) => p.status === "pending");
      const decided = allProceedings.filter((p) => p.status.startsWith("decided"));

      console.log(`\n=== Proceedings ===`);
      console.log(`Total: ${allProceedings.length}`);
      console.log(`Pending (active risk): ${pendingProceedings.length}`);
      console.log(`Decided: ${decided.length}`);

      for (const p of pendingProceedings) {
        console.log(`\n  [PENDING] ${p.proceeding_type} - ${p.proceeding_number}`);
        console.log(`  Filed: ${p.filed_date}`);
        console.log(`  Contested classes: ${p.contested_classes.join(", ")}`);
        for (const party of p.parties) {
          console.log(`    ${party.role}: ${party.name}`);
        }
      }
      ```
    </CodeGroup>

    **Expected output:**

    ```json theme={null}
    {
      "object": "list",
      "data": [
        {
          "id": "prc_hel001",
          "proceeding_type": "opposition",
          "proceeding_number": "91278456",
          "status": "pending",
          "filed_date": "2026-01-10",
          "parties": [
            { "owner_id": "own_other99", "name": "GreenGlow Naturals LLC", "role": "opponent" },
            { "owner_id": "own_helios01", "name": "Helios Consumer Brands Inc.", "role": "respondent" }
          ],
          "contested_classes": [3, 5]
        }
      ]
    }
    ```

    <Note>
      Pending proceedings are a material risk factor in M\&A. An ongoing opposition could result in loss of rights to a key brand. Make sure to flag these for the deal team and factor potential outcomes into the valuation.
    </Note>
  </Step>

  <Step title="Generate a diligence summary">
    Compile all findings into a structured report suitable for the deal team.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const diligenceReport = {
        target: {
          name: ownerDetail.name,
          ownerId: ownerDetail.id,
          country: ownerDetail.country_code,
          publicCompanies: ownerDetail.companies ?? [],
        },
        corporateFamily: {
          entityCount: familyOwnerIds.length,
          subsidiaries: related
            .filter((r) => r.direction === "child")
            .map((r) => ({
              name: r.name,
              country: r.country_code,
              ownerId: r.related_owner_id,
              ownershipPct: r.ownership_pct,
            })),
        },
        portfolio: {
          totalMarks: allMarks.length,
          registered,
          pending: pendingCount,
          abandonedOrExpired: abandoned + expired,
          grantRate: registered / allMarks.length,
          jurisdictions: Object.keys(jurisdictionDist).length,
          niceClasses: Object.keys(classDist).length,
          statusBreakdown: statusDist,
        },
        proceedings: {
          total: allProceedings.length,
          pending: pendingProceedings.length,
          pendingDetails: pendingProceedings.map((p) => ({
            type: p.proceeding_type,
            number: p.proceeding_number,
            filedDate: p.filed_date,
            contestedClasses: p.contested_classes,
            opponent: p.parties.find((party) => party.role === "opponent")?.name,
          })),
        },
        riskAssessment: {
          overallRisk: pendingProceedings.length > 3 ? "HIGH" : pendingProceedings.length > 0 ? "MEDIUM" : "LOW",
          flags: [
            ...(pendingProceedings.length > 0 ? [`${pendingProceedings.length} pending proceedings`] : []),
            ...(abandoned + expired > allMarks.length * 0.15
              ? ["High abandonment/expiry rate (>15%)"]
              : []),
          ],
        },
        generatedAt: new Date().toISOString(),
      };

      console.log(JSON.stringify(diligenceReport, null, 2));
      ```
    </CodeGroup>
  </Step>
</Steps>

***

## Key risk indicators to flag

| Risk Factor                                | How to detect                                                          | Severity |
| ------------------------------------------ | ---------------------------------------------------------------------- | -------- |
| Pending oppositions/cancellations          | `GET /v1/proceedings?party_owner_id=...&status=pending`                | High     |
| High abandonment rate (>15%)               | Owner stats `abandonment_rate`                                         | Medium   |
| Marks approaching expiry without renewal   | `deadlines[]` on `GET /v1/trademarks/{id}` across the target portfolio | High     |
| Thin jurisdiction coverage                 | Owner stats `jurisdiction_count` vs business footprint                 | Medium   |
| No Madrid filings for international brands | Filter `filing_route=direct_national` only                             | Low      |
| Missing use declarations (US)              | Deadline type `declaration_of_use` past its window                     | High     |

***

## After the deal closes: verify the ownership transfer

Once the acquisition closes and you file the assignment paperwork with each office, use the same `allMarkIds` you already collected during diligence to confirm the transfer landed correctly. There is no separate "transfer" endpoint. An assignment shows up as an ordinary owner change on the trademark record once the office processes and Signa ingests it.

<Steps>
  <Step title="Check recorded ownership in bulk">
    Batch-fetch the acquired marks (chunked to the 100-item limit) and check which ones now list the acquiring entity as owner.

    ```typescript TypeScript theme={null}
    const allMarkIds = allMarks.map((tm) => tm.id);
    const buyerOwnerId = "own_apex01";

    function chunk<T>(items: T[], size: number): T[][] {
      const chunks: T[][] = [];
      for (let i = 0; i < items.length; i += size) {
        chunks.push(items.slice(i, i + size));
      }
      return chunks;
    }

    let transferred = 0;
    for (const ids of chunk(allMarkIds, 100)) {
      const page = await signa.trademarks.batch({ ids });
      for (const tm of page.data) {
        if (tm.owners.some((o) => o.id === buyerOwnerId)) transferred++;
      }
      if (page.not_found.length > 0) {
        console.warn("Not found:", page.not_found);
      }
    }

    console.log(`Transferred: ${transferred} / ${allMarkIds.length}`);
    ```

    <Tip>
      Run this on a schedule (weekly is typical for a multi-month recording project) and share the ratio with the deal team. There is no per-item error status in the batch response, an ID either resolves into `data` or lands in `not_found`; a mark that no longer exists under the seller's ID (a rare renumbering case) is the only thing that would show up there.
    </Tip>
  </Step>

  <Step title="Pinpoint exactly when a transfer was recorded">
    For any individual mark, [Trademark Changes](/api-reference/trademarks/trademark-changes) gives you the version history so you can see the exact moment the owner field changed.

    ```typescript TypeScript theme={null}
    const changes = await signa.trademarks.changes("tm_hel001", { limit: 5 });

    for (const change of changes.data) {
      if (change.changed_fields.some((f) => f.includes("owner"))) {
        console.log(`Ownership change at version ${change.version}`);
        console.log(`  Register data date: ${change.source_data_date}`);
        console.log(`  Changed fields: ${change.changed_fields.join(", ")}`);
      }
    }
    ```
  </Step>
</Steps>

***

## What's next

<Card title="Renewal Management" href="/guides/use-cases/renewal-management">
  Make sure no deadlines are missed while transfer recordings are in progress.
</Card>

<Card title="Opposition Tracking" href="/guides/use-cases/opposition-tracking">
  Monitor any proceedings that transfer with the acquired portfolio.
</Card>
