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

# Opposition & Cancellation Tracking

> Monitor TTAB proceedings, track opposition and cancellation deadlines, detect proceeding status changes, and build a litigation dashboard across your clients' marks.

You are an IP litigation associate responsible for monitoring 30 active TTAB (Trademark Trial and Appeal Board) proceedings for your firm's clients. Some of your clients are opponents; others are respondents. You need to track proceeding status changes, flag new oppositions filed against your clients' marks, and maintain an overview of all active cases.

This guide shows how to build an opposition monitoring system using the Signa API.

## Prerequisites

* A Signa API key with `trademarks:read` scope
* Client owner IDs or a list of marks to monitor

***

<Steps>
  <Step title="Find all proceedings involving your clients">
    Start by querying proceedings where your client appears as a party -- either as the opponent (your client filed the opposition) or the respondent (someone opposed your client's mark).

    <CodeGroup>
      ```bash cURL theme={null}
      # Find proceedings where your client is the respondent (someone opposed their mark)
      curl "https://api.signa.so/v1/proceedings?party_owner_id=own_client01&party_role=respondent&status=pending&limit=50" \
        -H "Authorization: Bearer $SIGNA_API_KEY"

      # Find proceedings where your client is the opponent (they filed the opposition)
      curl "https://api.signa.so/v1/proceedings?party_owner_id=own_client01&party_role=opponent&status=pending&limit=50" \
        -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 });

      const clientOwnerIds = ["own_client01", "own_client02", "own_client03"];

      // Collect all active proceedings for all clients
      const allProceedings: any[] = [];

      for (const ownerId of clientOwnerIds) {
        const asRespondent = await signa.proceedings.list({
          party_owner_id: ownerId,
          party_role: "respondent",
          status: "pending",
          limit: 50,
        });

        const asOpponent = await signa.proceedings.list({
          party_owner_id: ownerId,
          party_role: "opponent",
          status: "pending",
          limit: 50,
        });

        for (const p of asRespondent.data) {
          allProceedings.push({ ...p, clientRole: "respondent", clientOwnerId: ownerId });
        }
        for (const p of asOpponent.data) {
          allProceedings.push({ ...p, clientRole: "opponent", clientOwnerId: ownerId });
        }
      }

      console.log(`Active proceedings across all clients: ${allProceedings.length}`);
      ```

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

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

      client_owner_ids = ["own_client01", "own_client02", "own_client03"]
      all_proceedings = []

      for owner_id in client_owner_ids:
          for role in ["respondent", "opponent"]:
              resp = requests.get(
                  "https://api.signa.so/v1/proceedings",
                  params={"party_owner_id": owner_id, "party_role": role, "status": "pending", "limit": 50},
                  headers=headers,
              ).json()
              for p in resp["data"]:
                  all_proceedings.append({**p, "client_role": role, "client_owner_id": owner_id})

      print(f"Active proceedings across all clients: {len(all_proceedings)}")
      ```
    </CodeGroup>

    **Expected output:**

    ```json theme={null}
    {
      "object": "list",
      "data": [
        {
          "id": "prc_op001",
          "proceeding_type": "opposition",
          "proceeding_number": "91278456",
          "status": "pending",
          "office_code": "uspto",
          "filed_date": "2026-01-10",
          "decision_date": null,
          "parties": [
            { "owner_id": "own_other99", "name": "Apex Global Corp", "role": "opponent" },
            { "owner_id": "own_client01", "name": "Meridian Labs Inc.", "role": "respondent" }
          ],
          "contested_classes": [9, 42],
          "trademark_id": "tm_merid01"
        }
      ]
    }
    ```
  </Step>

  <Step title="Get details on the contested marks">
    For each proceeding, fetch the full trademark detail to understand what is at stake.

    <CodeGroup>
      ```bash cURL theme={null}
      # Get the contested trademark
      curl https://api.signa.so/v1/trademarks/tm_merid01 \
        -H "Authorization: Bearer $SIGNA_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      // Collect unique trademark IDs from all proceedings
      const trademarkIds = [...new Set(allProceedings.map((p) => p.trademark_id).filter(Boolean))];

      // Batch fetch all contested marks
      const contestedMarks = await signa.trademarks.batch({ ids: trademarkIds });

      for (const tm of contestedMarks.data) {
        console.log(`\n${tm.mark_text} (${tm.id})`);
        console.log(`  Office: ${tm.office_code}`);
        console.log(`  Status: ${tm.status.stage}`);
        console.log(`  Classes: ${tm.classifications.map((c) => c.nice_class).join(", ")}`);
        console.log(`  Owner: ${tm.owners[0]?.name}`);
        console.log(`  Filing date: ${tm.filing_date}`);
      }
      ```

      ```python Python theme={null}
      trademark_ids = list({p["trademark_id"] for p in all_proceedings if p.get("trademark_id")})

      contested = requests.post(
          "https://api.signa.so/v1/trademarks/batch",
          headers={**headers, "Content-Type": "application/json", "Idempotency-Key": "batch-contested-marks"},
          json={"ids": trademark_ids},
      ).json()

      for tm in contested["data"]:
          print(f"\n{tm['mark_text']} ({tm['id']})")
          print(f"  Status: {tm['status']['stage']}")
          print(f"  Classes: {', '.join(str(c['nice_class']) for c in tm['classifications'])}")
          print(f"  Owner: {tm['owner_name']}")
      ```
    </CodeGroup>
  </Step>

  <Step title="Surface marks most exposed to opposition">
    Pull each client's pending and published marks -- the stages most vulnerable to opposition -- so you know which applications to watch most closely.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -G "https://api.signa.so/v1/owners/own_client01/trademarks" \
        -H "Authorization: Bearer $SIGNA_API_KEY" \
        --data-urlencode "status_stage=examining,published,opposition_period,pending_opposition" \
        --data-urlencode "limit=100"
      ```

      ```typescript TypeScript theme={null}
      for (const ownerId of clientOwnerIds) {
        const vulnerableMarks = await signa.owners.trademarks(ownerId, {
          status_stage: ["examining", "published", "opposition_period", "pending_opposition"],
          limit: 100,
        });

        console.log(`${ownerId}: ${vulnerableMarks.data.length} marks to monitor`);
      }
      ```

      ```python Python theme={null}
      for owner_id in client_owner_ids:
          vulnerable = requests.get(
              f"https://api.signa.so/v1/owners/{owner_id}/trademarks",
              params={
                  "status_stage": "examining,published,opposition_period,pending_opposition",
                  "limit": 100,
              },
              headers=headers,
          ).json()
          print(f"{owner_id}: {len(vulnerable['data'])} marks to monitor")
      ```
    </CodeGroup>

    <Tip>
      Focus on marks in the `published`, `opposition_period`, and `examining` stages -- these are the ones most vulnerable to new proceedings. Registered marks can still face cancellation petitions but the risk is lower.
    </Tip>
  </Step>

  <Step title="Check prosecution history for contested marks">
    Review the event timeline of a contested mark to understand how the opposition fits into the prosecution history.

    <CodeGroup>
      ```bash cURL theme={null}
      curl "https://api.signa.so/v1/trademarks/tm_merid01/history?limit=20&sort=-event_date" \
        -H "Authorization: Bearer $SIGNA_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      const history = await signa.trademarks.history("tm_merid01", {
        limit: 20,
        sort: "-event_date",
      });

      console.log("\nProsecution timeline:");
      for (const event of history.data) {
        console.log(
          `  ${event.event_date} | ${event.event_type.padEnd(20)} | ${event.description}`,
        );
        if (event.status_after_event) {
          console.log(`  ${"".padEnd(12)} Status -> ${event.status_after_event}`);
        }
      }
      ```

      ```python Python theme={null}
      history = requests.get(
          "https://api.signa.so/v1/trademarks/tm_merid01/history",
          params={"limit": 20, "sort": "-event_date"},
          headers=headers,
      ).json()

      print("\nProsecution timeline:")
      for event in history["data"]:
          print(f"  {event['event_date']} | {event['event_type']:20} | {event['description']}")
      ```
    </CodeGroup>

    **Expected output:**

    ```
    Prosecution timeline:
      2026-01-10 | opposition           | OPPOSITION FILED
                 Status -> pending_opposition
      2025-11-15 | publication          | PUBLISHED FOR OPPOSITION
                 Status -> published
      2025-09-20 | examination          | APPROVED FOR PUBLICATION
      2025-06-01 | filing               | NEW APPLICATION FILED
                 Status -> filed
    ```
  </Step>

  <Step title="Track proceeding outcomes and filter by type">
    Query proceedings by type and status to build different views of your case docket.

    <CodeGroup>
      ```bash cURL theme={null}
      # All opposition proceedings (any party, any status)
      curl "https://api.signa.so/v1/proceedings?party_owner_id=own_client01&proceeding_type=opposition&limit=50" \
        -H "Authorization: Bearer $SIGNA_API_KEY"

      # Cancellation proceedings only
      curl "https://api.signa.so/v1/proceedings?party_owner_id=own_client01&proceeding_type=cancellation&limit=50" \
        -H "Authorization: Bearer $SIGNA_API_KEY"

      # Decided proceedings (to review outcomes)
      curl "https://api.signa.so/v1/proceedings?party_owner_id=own_client01&status=decided_granted&limit=50" \
        -H "Authorization: Bearer $SIGNA_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      // Build a complete litigation dashboard for one client
      const dashboard: Record<string, any[]> = {
        pending_oppositions: [],
        pending_cancellations: [],
        decided_this_year: [],
        settled: [],
      };

      const oppositions = await signa.proceedings.list({
        party_owner_id: "own_client01",
        proceeding_type: "opposition",
        status: "pending",
        limit: 50,
      });
      dashboard.pending_oppositions = oppositions.data;

      const cancellations = await signa.proceedings.list({
        party_owner_id: "own_client01",
        proceeding_type: "cancellation",
        status: "pending",
        limit: 50,
      });
      dashboard.pending_cancellations = cancellations.data;

      const decided = await signa.proceedings.list({
        party_owner_id: "own_client01",
        decision_date: { gte: "2026-01-01" },
        limit: 50,
      });
      dashboard.decided_this_year = decided.data;

      const settled = await signa.proceedings.list({
        party_owner_id: "own_client01",
        status: "settled",
        limit: 50,
      });
      dashboard.settled = settled.data;

      console.log("\n=== Litigation Dashboard ===");
      console.log(`Pending oppositions: ${dashboard.pending_oppositions.length}`);
      console.log(`Pending cancellations: ${dashboard.pending_cancellations.length}`);
      console.log(`Decided this year: ${dashboard.decided_this_year.length}`);
      console.log(`Settled: ${dashboard.settled.length}`);
      ```

      ```python Python theme={null}
      # Build dashboard
      for proc_type in ["opposition", "cancellation"]:
          resp = requests.get(
              "https://api.signa.so/v1/proceedings",
              params={
                  "party_owner_id": "own_client01",
                  "proceeding_type": proc_type,
                  "status": "pending",
                  "limit": 50,
              },
              headers=headers,
          ).json()
          print(f"Pending {proc_type}s: {len(resp['data'])}")

      # Decided this year
      decided = requests.get(
          "https://api.signa.so/v1/proceedings",
          params={
              "party_owner_id": "own_client01",
              "decision_date_gte": "2026-01-01",
              "limit": 50,
          },
          headers=headers,
      ).json()
      print(f"Decided this year: {len(decided['data'])}")
      ```
    </CodeGroup>
  </Step>

  <Step title="Detect proceeding status changes by polling history">
    Poll [Trademark History](/api-reference/trademarks/trademark-history) on each contested mark to catch status transitions. Run this hourly from your job scheduler and compare the most recent event against the last one you saw.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const watchedMarkIds = new Set(allProceedings.map((p) => p.trademark_id));

      for (const markId of watchedMarkIds) {
        const history = await signa.trademarks.history(markId, {
          limit: 5,
          sort: "-event_date",
        });

        const latest = history.data[0];
        if (!latest) continue;

        if (latest.event_type === "status_changed") {
          console.log(
            `[ALERT] ${markId}: ${latest.details?.from_stage} -> ${latest.details?.to_stage} (${latest.event_date})`,
          );
        }
      }
      ```

      ```python Python theme={null}
      watched_ids = {p["trademark_id"] for p in all_proceedings if p.get("trademark_id")}

      for mark_id in watched_ids:
          history = requests.get(
              f"https://api.signa.so/v1/trademarks/{mark_id}/history",
              params={"limit": 5, "sort": "-event_date"},
              headers=headers,
          ).json()

          if not history["data"]:
              continue

          latest = history["data"][0]
          if latest["event_type"] == "status_changed":
              print(
                  f"[ALERT] {mark_id}: "
                  f"{latest['details'].get('from_stage')} -> {latest['details'].get('to_stage')} "
                  f"({latest['event_date']})"
              )
      ```
    </CodeGroup>

    <Note>
      A status change from `pending_opposition` to `registered` means the opposition was resolved in your client's favor -- the mark proceeded to registration. A change to `abandoned` or `refused` would indicate the opposite outcome.
    </Note>
  </Step>
</Steps>

***

## Proceeding status reference

| Status             | Meaning                                                |
| ------------------ | ------------------------------------------------------ |
| `pending`          | Active, awaiting decision                              |
| `decided_granted`  | Decided in favor of the petitioner/opponent            |
| `decided_rejected` | Decided in favor of the respondent                     |
| `withdrawn`        | Petitioner/opponent withdrew the proceeding            |
| `settled`          | Parties reached a settlement                           |
| `suspended`        | Proceeding paused (often pending related litigation)   |
| `partial`          | Mixed outcome -- some grounds sustained, others denied |
| `other`            | Catch-all for unusual outcomes                         |

***

## What's next

<Card title="Trademark Clearance" href="/guides/use-cases/trademark-clearance">
  Run clearance searches to avoid triggering oppositions before you file.
</Card>

<Card title="Competitor Intelligence" href="/guides/use-cases/competitor-intelligence">
  Track competitor filing patterns to anticipate potential opposition actions.
</Card>

<Card title="Renewal Management" href="/guides/use-cases/renewal-management">
  Ensure contested marks do not lapse during proceedings by tracking their deadlines.
</Card>
