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

# Renewal Management

> Never miss a trademark deadline. Query upcoming renewals and declarations, understand grace periods, triage by urgency, and handle US-specific Section 8/15 declarations.

You are a paralegal at a mid-sized IP firm responsible for docketing renewal deadlines across 200+ client marks in 12 jurisdictions. Missing a deadline means losing rights -- and potentially a malpractice claim. You need a system that surfaces every upcoming deadline with enough lead time to prepare filings.

This guide walks through building a renewal management workflow with the Signa API.

## Prerequisites

* A Signa API key with `trademarks:read` scope
* A list of trademark IDs (or office-native identifiers) for the client marks you docket

***

<Steps>
  <Step title="Understand deadline types by jurisdiction">
    Not every jurisdiction has the same deadline structure. The Signa API computes deadlines based on jurisdiction-specific rule sets (rules defined for 21 jurisdictions). Start by reviewing the rules for your key jurisdictions.

    <CodeGroup>
      ```bash cURL theme={null}
      # Get all deadline rules for the US
      curl "https://api.signa.so/v1/deadline-rules?jurisdiction=US" \
        -H "Authorization: Bearer $SIGNA_API_KEY"

      # Get rules for multiple jurisdictions in one call
      curl "https://api.signa.so/v1/deadline-rules?jurisdiction=US,EU,GB,DE,CA" \
        -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 rules = await signa.references.deadlineRules({
        jurisdiction: ["US", "EU", "GB", "DE", "CA"],
      });

      for (const rule of rules.data) {
        console.log(
          `[${rule.jurisdiction_code}] ${rule.name} (${rule.type}) ` +
            `due year ${rule.due_year}, renewal cycle ${rule.renewal_period_years}y`,
        );
      }
      ```

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

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

      resp = requests.get(
          "https://api.signa.so/v1/deadline-rules",
          params={"jurisdiction": "US,EU,GB,DE,CA"},
          headers=headers,
      ).json()
      for rule in resp["data"]:
          print(
              f"[{rule['jurisdiction_code']}] {rule['name']} ({rule['type']}) "
              f"due year {rule['due_year']}, renewal cycle {rule['renewal_period_years']}y"
          )
      ```
    </CodeGroup>

    **Key differences across jurisdictions:**

    | Jurisdiction | Renewal Period                                     | Grace Period | Special Requirements                                                    |
    | ------------ | -------------------------------------------------- | ------------ | ----------------------------------------------------------------------- |
    | US           | 10 years                                           | 6 months     | Section 8 (use), Section 15 (incontestability), combined 8+9 at renewal |
    | EU           | 10 years                                           | 6 months     | Simple renewal only                                                     |
    | GB           | 10 years                                           | 6 months     | Simple renewal + restoration period (6 months post-grace)               |
    | DE           | 10 years                                           | 6 months     | DPMA end-of-month rule (due date = last day of expiry month)            |
    | CA           | 15 years (pre-2019: 15 years, post-2019: 10 years) | 6 months     | Legacy 15-year marks transitioning to 10-year cycle                     |

    <Note>
      US marks have the most complex deadline structure. In addition to renewal, you must file a Section 8 Declaration of Use between years 5-6 after registration, and optionally a Section 15 Declaration of Incontestability at year 5. Missing the Section 8 results in cancellation, even if the mark is in active use.
    </Note>
  </Step>

  <Step title="Collect deadlines across your client marks">
    Every `GET /v1/trademarks/{id}` response includes a `deadlines` array -- Signa computes renewal, declaration, and grace windows from jurisdiction rule sets. Fetch each client mark (or use [Batch Get Trademarks](/api-reference/trademarks/batch-trademarks) for up to 100 at a time) and collect the deadlines that fall inside your docketing horizon.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const clientMarkIds: string[] = [
        /* your tm_... ids */
      ];
      const horizon = "2027-09-24";

      const batch = await signa.trademarks.batch({ ids: clientMarkIds });

      const deadlines = batch.data
        .filter((entry) => entry.status === "success")
        .flatMap((entry) =>
          (entry.data.deadlines ?? []).map((d) => ({
            trademark_id: entry.data.id,
            mark_text: entry.data.mark_text,
            office_code: entry.data.office_code,
            jurisdiction_code: entry.data.jurisdiction_code,
            ...d,
          })),
        )
        .filter((d) => d.due_date <= horizon)
        .sort((a, b) => a.due_date.localeCompare(b.due_date));

      console.log(`Total deadlines before ${horizon}: ${deadlines.length}`);

      const byType: Record<string, number> = {};
      for (const d of deadlines) {
        byType[d.type] = (byType[d.type] || 0) + 1;
      }
      console.log("By type:", byType);
      ```

      ```python Python theme={null}
      from collections import Counter

      client_mark_ids = [
          # your tm_... ids
      ]
      horizon = "2027-09-24"

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

      deadlines = []
      for entry in batch["data"]:
          if entry["status"] != "success":
              continue
          tm = entry["data"]
          for d in tm.get("deadlines", []):
              if d["due_date"] <= horizon:
                  deadlines.append({
                      "trademark_id": tm["id"],
                      "mark_text": tm["mark_text"],
                      "office_code": tm["office_code"],
                      "jurisdiction_code": tm["jurisdiction_code"],
                      **d,
                  })

      deadlines.sort(key=lambda d: d["due_date"])
      print(f"Total deadlines before {horizon}: {len(deadlines)}")

      by_type = Counter(d["type"] for d in deadlines)
      print("By type:", dict(by_type))
      ```
    </CodeGroup>

    **Expected output:**

    ```json theme={null}
    {
      "object": "list",
      "data": [
        {
          "trademark_id": "tm_abc001",
          "mark_text": "BRIGHTWAVE",
          "office_code": "uspto",
          "type": "declaration_of_use",
          "due_date": "2026-08-14",
          "grace_expiry": "2027-02-14",
          "window_opens": "2025-08-14",
          "jurisdiction_code": "US"
        },
        {
          "trademark_id": "tm_def002",
          "mark_text": "MERIDIAN",
          "office_code": "euipo",
          "type": "renewal",
          "due_date": "2026-11-30",
          "grace_expiry": "2027-05-31",
          "window_opens": "2025-11-30",
          "jurisdiction_code": "EU"
        }
      ]
    }
    ```
  </Step>

  <Step title="Categorize deadlines by urgency">
    Use the `window_opens` and `grace_expiry` dates to build an urgency triage:

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const today = new Date().toISOString().slice(0, 10);

      interface Deadline {
        trademark_id: string;
        mark_text: string;
        office_code: string;
        type: string;
        due_date: string;
        grace_expiry: string | null;
        window_opens: string;
        jurisdiction_code: string;
      }

      function categorize(d: Deadline): "overdue" | "grace_period" | "due_soon" | "window_open" | "upcoming" {
        if (d.due_date < today && d.grace_expiry && d.grace_expiry >= today) return "grace_period";
        if (d.due_date < today) return "overdue";

        const daysUntilDue = Math.ceil(
          (new Date(d.due_date).getTime() - Date.now()) / (1000 * 60 * 60 * 24),
        );

        if (daysUntilDue <= 30) return "due_soon";
        if (d.window_opens <= today) return "window_open";
        return "upcoming";
      }

      const categorized = {
        overdue: [] as Deadline[],
        grace_period: [] as Deadline[],
        due_soon: [] as Deadline[],
        window_open: [] as Deadline[],
        upcoming: [] as Deadline[],
      };

      for (const d of deadlines) {
        const category = categorize(d);
        categorized[category].push(d);
      }

      console.log(`OVERDUE (past grace): ${categorized.overdue.length}`);
      console.log(`IN GRACE PERIOD: ${categorized.grace_period.length}`);
      console.log(`DUE WITHIN 30 DAYS: ${categorized.due_soon.length}`);
      console.log(`WINDOW OPEN (can file now): ${categorized.window_open.length}`);
      console.log(`UPCOMING: ${categorized.upcoming.length}`);
      ```

      ```python Python theme={null}
      from datetime import date, datetime

      today = date.today().isoformat()

      def categorize(d):
          if d["due_date"] < today:
              if d.get("grace_expiry") and d["grace_expiry"] >= today:
                  return "grace_period"
              return "overdue"
          days_until = (datetime.fromisoformat(d["due_date"]).date() - date.today()).days
          if days_until <= 30:
              return "due_soon"
          if d["window_opens"] <= today:
              return "window_open"
          return "upcoming"

      categories = {"overdue": [], "grace_period": [], "due_soon": [], "window_open": [], "upcoming": []}
      for d in deadlines:
          categories[categorize(d)].append(d)

      for cat, items in categories.items():
          print(f"{cat.upper()}: {len(items)}")
      ```
    </CodeGroup>

    | Priority | Category                 | Action required                                  |
    | -------- | ------------------------ | ------------------------------------------------ |
    | P0       | **Overdue** (past grace) | Rights likely lost. Consult counsel immediately. |
    | P1       | **Grace period**         | File immediately. Late fees apply.               |
    | P2       | **Due within 30 days**   | Prepare and file.                                |
    | P3       | **Window open**          | Can file early. Schedule for next batch.         |
    | P4       | **Upcoming**             | No action needed yet.                            |
  </Step>

  <Step title="Handle US-specific Section 8 and Section 15 declarations">
    US marks require more than simple renewal. Pull the detail for each US mark to identify which declarations are needed.

    <CodeGroup>
      ```bash cURL theme={null}
      # Get full detail including all computed deadlines
      curl https://api.signa.so/v1/trademarks/tm_abc001 \
        -H "Authorization: Bearer $SIGNA_API_KEY"
      ```

      ```typescript TypeScript theme={null}
      // Filter US deadlines by type
      const usDeadlines = deadlines.filter((d) => d.jurisdiction_code === "US");

      const section8 = usDeadlines.filter((d) => d.type === "declaration_of_use");
      const section15 = usDeadlines.filter((d) => d.type === "declaration_of_incontestability");
      const combined89 = usDeadlines.filter((d) => d.type === "combined_renewal_and_use");
      const renewals = usDeadlines.filter((d) => d.type === "renewal");

      console.log(`\nUS Deadlines breakdown:`);
      console.log(`  Section 8 (Declaration of Use): ${section8.length}`);
      console.log(`  Section 15 (Incontestability): ${section15.length}`);
      console.log(`  Combined 8+9 (Renewal + Use): ${combined89.length}`);
      console.log(`  Simple renewals: ${renewals.length}`);

      // For each Section 8, get the full trademark to check filing bases
      for (const d of section8.slice(0, 3)) {
        const tm = await signa.trademarks.retrieve(d.trademark_id);
        console.log(`\n  ${tm.mark_text} (${tm.application_number})`);
        console.log(`  Section 8 due: ${d.due_date} (grace: ${d.grace_expiry})`);
        console.log(`  Filing bases: ${tm.filing_bases.map((b) => b.basis_type).join(", ")}`);
        console.log(`  Classes: ${tm.classifications.map((c) => c.nice_class).join(", ")}`);
      }
      ```

      ```python Python theme={null}
      us_deadlines = [d for d in deadlines if d["jurisdiction_code"] == "US"]

      section8 = [d for d in us_deadlines if d["type"] == "declaration_of_use"]
      section15 = [d for d in us_deadlines if d["type"] == "declaration_of_incontestability"]
      combined89 = [d for d in us_deadlines if d["type"] == "combined_renewal_and_use"]

      print(f"Section 8 (Declaration of Use): {len(section8)}")
      print(f"Section 15 (Incontestability): {len(section15)}")
      print(f"Combined 8+9 (Renewal + Use): {len(combined89)}")
      ```
    </CodeGroup>

    **US Declaration timeline for a mark registered on 2021-01-20:**

    ```
    Registration ──────────────────────────────────────────────> Time
         |                                                    |
         v                                                    v
      2021-01-20                                        2031-01-20

      Year 5 (2026-01-20):  Section 15 window opens (optional)
      Year 5 (2026-01-20):  Section 8 window opens
      Year 6 (2027-01-20):  Section 8 DUE (+ 6-month grace)
      Year 10 (2031-01-20): Combined Section 8 + 9 DUE (+ 6-month grace)
    ```

    <Note>
      Section 15 (incontestability) is optional but highly valuable. It eliminates most grounds for cancellation. The window opens at year 5 and remains open indefinitely -- but the mark must have been in continuous use for 5 consecutive years with no pending proceedings.
    </Note>
  </Step>

  <Step title="Refresh the roll-up on a schedule">
    Run the batch fetch nightly from your job scheduler, diff the results against the previous run, and surface any newly added or changed deadlines to your docketing team. Because Signa recomputes deadlines whenever a mark's lifecycle data moves (renewal filed, registration date corrected, status change), the cron job doubles as a change detector without any additional infrastructure.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      // Persist yesterday's deadlines keyed by (trademark_id, type, due_date)
      const previous = await loadYesterdaysDeadlines(); // from your own store
      const todaysKeys = new Set(deadlines.map((d) => `${d.trademark_id}|${d.type}|${d.due_date}`));
      const newOrChanged = deadlines.filter(
        (d) => !previous.has(`${d.trademark_id}|${d.type}|${d.due_date}`),
      );

      console.log(`New or changed deadlines: ${newOrChanged.length}`);
      await saveTodaysDeadlines(todaysKeys);
      ```

      ```python Python theme={null}
      # Persist yesterday's deadlines keyed by (trademark_id, type, due_date)
      previous = load_yesterdays_deadlines()  # from your own store
      todays_keys = {f"{d['trademark_id']}|{d['type']}|{d['due_date']}" for d in deadlines}
      new_or_changed = [
          d for d in deadlines
          if f"{d['trademark_id']}|{d['type']}|{d['due_date']}" not in previous
      ]

      print(f"New or changed deadlines: {len(new_or_changed)}")
      save_todays_deadlines(todays_keys)
      ```
    </CodeGroup>

    <Tip>
      Emit `new_or_changed` to your docketing queue or Slack channel. Most firms run this once overnight and pair it with a weekly full-portfolio review of the categorised buckets above.
    </Tip>
  </Step>
</Steps>

***

## Grace period reference

| Jurisdiction  | Grace Period                    | Late Fee | Notes                             |
| ------------- | ------------------------------- | -------- | --------------------------------- |
| US            | 6 months                        | Yes      | Applies to renewals and Section 8 |
| EU            | 6 months                        | Yes      | Surcharge applies                 |
| GB            | 6 months + 6 months restoration | Yes      | Two-stage: grace then restoration |
| DE            | 6 months                        | Yes      | DPMA end-of-month rule applies    |
| CA            | 6 months                        | Yes      |                                   |
| CH            | 6 months                        | Yes      |                                   |
| FR            | 6 months                        | Yes      |                                   |
| WIPO (Madrid) | 6 months                        | Yes      | Per designation                   |
| AU            | 6 months                        | Yes      |                                   |
| MX            | 6 months                        | Yes      | Post-anniversary window           |

***

## What's next

<Card title="Class Coverage Audits" href="/guides/use-cases/class-coverage-audits">
  Check whether your clients' Nice class coverage has gaps before renewal.
</Card>

<Card title="Opposition Tracking" href="/guides/use-cases/opposition-tracking">
  Monitor proceedings that could affect the renewability of contested marks.
</Card>
