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

# Changelog archive

> Full technical notes for every change up to October 11, 2026.

The full technical notes behind the [changelog](https://docs.signa.so/changelog.md), kept as published.

## October 11, 2026: screening coverage classes, usage summary window, calendar office codes

**Screening `coverage.classes` names the classes searched.**
[`GET /v1/screening`](https://docs.signa.so/api-reference/screening/screen) can match classes from
your `goods_services` text and search them too. `coverage.classes.resolved`
now lists the classes matched from `nice_classes` plus `goods_services`;
`searched` is the class filter applied by the class-filtered retrieval lanes
(the containment lane, which finds marks that contain the name, is not
class-filtered); and `expanded` lists the classes `searched` adds beyond
`requested`. Before, a screen with `nice_classes=16` and a coffee description
searched classes 16 and 30 but reported `resolved: [16]`, `searched: [16]`
and `expanded: []`. Results, risk levels and verdicts are unchanged.

**Usage summary rows match `billing_period`.** Without `start_date` and
`end_date`,
[`GET /v1/organization/usage/summary`](https://docs.signa.so/api-reference/administration/get-usage-summary)
and the MCP `get_usage_summary` tool now list rows for the same window as
`billing_period`: the current plan period, from the moment it started, the
same period as `credits_used`. A period that started mid-day has a first row that
starts at `billing_period.start` and holds only the spend after it. Before,
an organization whose plan renewed mid-month got rows from its billing anchor
day, including spend from the previous period, next to the new period's
`credits_used`.

**Calendar feed office codes.** The portfolio deadlines calendar (`.ics`) names
the office by its public code (`US`, `EM`), as the JSON deadline rows do. It
said `USPTO`.

## October 11, 2026: TypeScript SDK 0.22.0; MCP proceedings filters and compact results

**MCP `list_proceedings`** takes every filter of
[`GET /v1/proceedings`](https://docs.signa.so/api-reference/proceedings/search-proceedings):
party name (`q`), `party_role`, `filed_date_gte`/`filed_date_lt`,
`decision_date_gte`/`decision_date_lt`, `contested_class` and `aggregations`,
so an agent can ask for oppositions against a company in a date range.
Cursors are bound to the new filters too. An unknown `status`,
`proceeding_type` or `party_role` now returns `validation_error`; before, an
unknown `status` or `proceeding_type` returned an empty page that read as "no
proceedings".

**MCP results are compact JSON**, without indentation, so tool results cost
agents fewer context tokens. The content is unchanged.

**TypeScript SDK 0.22.0** types everything the API added since 0.21.0: the
deadline row numbers and `warnings` (on `SignaList`, so `horizon_truncated`
reaches SDK users), `office_class`, the owner, attorney and firm trademark
filters the API already accepted (`sort`, `goods_services_text` and more), the
`filing_route` aliases on the rule catalogs and the `designation_date` filters.
`trademarks.search()` sends request bodies up to 64 KB. Install it with
`npm install @signa-so/sdk@0.22.0` once it is on
[npm](https://www.npmjs.com/package/@signa-so/sdk).

## October 11, 2026: API contract fixes

**OpenAPI contract.** The spec now matches what the API sends:

* `request_id` is optional on the four publicly cached single responses
  (`GET /v1/offices/{code}`, `/v1/jurisdictions/{code}`,
  `/v1/classifications/{classNumber}`, `/v1/design-codes/{code}`). They leave it
  out so a shared cache never hands one caller's id to another; the cached lists
  already said so. Clients that validate responses against the spec no longer
  reject these.
* Fields the API already sent are now declared: `created_at` and `updated_at`
  on proceedings, `revoked_at` on API keys, and `attorneys` with
  `attorneys_has_more` on firm detail requested with `include=attorneys`.

**Rule catalogs accept trademark filing routes where the mapping is clear.**
[`GET /v1/deadline-rules`](https://docs.signa.so/api-reference/reference/deadline-rules) accepts
`direct_national` (read as `national`), `direct_regional` (as `regional`), and
`madrid_ir` and `madrid_designation` (as `madrid`).
[`GET /v1/opposition-rules`](https://docs.signa.so/api-reference/reference/opposition-rules) accepts
`direct_national` and `direct_regional` (as `domestic`). The other trademark
values (`transformation`, `divisional`, `unknown`, and `madrid_ir` on
opposition rules) still return `400` with the accepted list. The rule values
keep working.

**Credit pricing.** The `read` row of
[`GET /v1/credits/pricing`](https://docs.signa.so/api-reference/administration/get-credit-pricing)
is `per_request`, which is how it is billed: one record, or one page of a list
such as `/v1/owners/{id}/trademarks`. It said `per_record`. `POST
/v1/trademarks/batch`, billed per submitted identifier, including the ones
returned in `not_found`, carries `unit: "per_record"`
on its endpoint entry. The [rate limits](https://docs.signa.so/api-reference/rate-limits.md) page now
shows a real `RateLimit-Policy` header, with both limits it lists.

**Swedish office links.** `office_url` is `null` on Swedish (SE) trademarks,
in detail, search and coverage rows. The link Sweden's office used to serve
(`was.prv.se/VarumarkesDb`) now ends at a page that does not exist.

**Deadline rows name the mark's numbers.** Rows of
[`GET /v1/deadlines`](https://docs.signa.so/api-reference/reference/list-deadlines) and
[`GET /v1/portfolios/{id}/deadlines`](https://docs.signa.so/api-reference/portfolios/deadlines)
carry `application_number`, `registration_number` and `ir_number`, so rows of
marks with the same name can be told apart and the rows of one international
registration's designations grouped. The
[calendar file](https://docs.signa.so/api-reference/portfolios/deadlines-ics.md) puts the number in each
event's title and adds a `URL` link to the office record when the office serves
one. A deadline already in its grace period is placed on the grace period's
last day, so its alarms are still ahead; before, it sat on the passed due date.
Long lines are folded at 75 octets as RFC 5545 requires, and the calendar is
named `<portfolio name> deadlines`.

**Deadline horizon warning.** Deadlines are computed five years ahead. A
`due_before` later than that on
[`GET /v1/deadlines`](https://docs.signa.so/api-reference/reference/list-deadlines) or
[`GET /v1/portfolios/{id}/deadlines`](https://docs.signa.so/api-reference/portfolios/deadlines) now
returns `warnings` with a `horizon_truncated` entry naming the horizon date;
before, the rows past the horizon were missing without a word.

**Coverage and billing period times.** `coverage_through` on
[`GET /v1/monitoring/status`](https://docs.signa.so/api-reference/monitoring/watches/monitoring-status) and on watch
health is never later than the time of the response. Offices that publish
whole days used to read "covered through the end of today" hours before the day
ended. `GET /v1/organization/plan` and
[`GET /v1/organization/usage`](https://docs.signa.so/api-reference/administration/get-usage) report the same `billing_period` as
[`GET /v1/organization/usage/summary`](https://docs.signa.so/api-reference/administration/get-usage-summary):
for an organization on credits, the window of its current plan credits, which
is when they reset. Before, the plan and usage endpoints gave the anchor day at
midnight.

**Classes as the office states them.** Each entry of `classifications` on
[trademark detail](https://docs.signa.so/api-reference/trademarks/retrieve-trademark.md) carries
`office_class`, the class as the office states it. A row with no Nice class now
says what it is: US `A` and `B` (certification marks for goods and for
services), US `200` (collective membership marks) or Spain's `00`. It is `null`
on an international registration's classes rolled up from its designations.

## October 11, 2026: status counts add up, and France has a pending pipeline

Every stats count now sorts status stages into buckets from one table, so each
status stage lands in exactly one bucket and the buckets add up to the total.
Before, some stages were in no bucket.

* **Pending includes the opposition period.** `opposition_period` records were
  in no bucket. They now count as `pending`. France's
  [office analytics](https://docs.signa.so/api-reference/analytics/office-analytics.md) `pending_count`
  goes from 1 to about 41,700, and market and class `by_status.pending` rise by
  the same records.
* **A pending cancellation is still registered.** `pending_cancellation`
  records (about 3,800, all EUIPO) were in no bucket. They now count as
  `registered`, since the mark stays on the register until the cancellation is
  decided. `registered_count` and the grant rate change for the owners,
  attorneys and firms that hold them (including the per-firm grant rate on
  [`GET /v1/firms/{id}/attorneys`](https://docs.signa.so/api-reference/parties/firm-attorneys)), and
  for the EUIPO office (`registered_count`, `registration_rate` and the
  `registered` yearly trend).
* **New `unknown_count`.** Records whose status stage is `unknown` (about
  913,000, mostly WIPO and USPTO) now have their own count: `unknown_count` on
  [office analytics](https://docs.signa.so/api-reference/analytics/office-analytics.md), on the owner
  and attorney `stats` object, and on the analytics `portfolio` block, and
  next to `by_status` on [market](https://docs.signa.so/api-reference/analytics/market.md) and
  [class](https://docs.signa.so/api-reference/analytics/classification-analytics.md) analytics, where
  it is `null` until those stats are next recomputed. With it, the bucket
  counts sum to `total_marks` (office, market, class), to `mark_count`
  (portfolio), or to the records counted (owner and attorney stats).
* **Attorney, firm and entity `active_count` rise.** They now count
  `opposition_period` and `pending_cancellation` marks as active, as owner
  `active_count` already did. The analytics `portfolio.active_count` does the
  same, so `active_count`, `dead_count` and `unknown_count` add up to
  `mark_count`.

Party, office and entity counts update with the nightly stats run; market and
class counts with their next recompute.

## October 11, 2026: EU legs read EU everywhere; every country filter matches every spelling

Follow-up to "filters that match nothing now say so" below, so the same
spellings behave the same on every endpoint.

* **Deadlines of EU legs.** The WIPO record of a Madrid designation of the EU
  (stored by WIPO as `EM`) already read `jurisdiction_code: "EU"`. Its deadline
  rows now read `EU` too: `derived.next_deadline` and `derived.deadlines.items`
  on trademarks, `GET /v1/deadlines`, portfolio deadlines,
  `POST /v1/deadlines/compute` (the item and its rows) and the reconcile
  `computed` context. Trademark and portfolio ETags change once.
* **Webhooks.** The `trademark.status_changed`, `trademark.corrected` and
  `office_action.issued` payloads carry `jurisdiction_code: "EU"` for that
  record, where they carried `EM`, and so does `trademark_event.territory_code`
  on `office_action.issued` (and `territory_code` on
  `GET /v1/trademarks/{id}/events`). This is the same territory written the way
  the trademark row writes it, not a new meaning, so `payload_version` does not
  change. Events and deliveries stored before the change read `EU` too:
  `GET /v1/events/{id}`, automatic retries and `POST .../redeliver` all serve
  `EU` (the stored copy keeps `EM`).
* **Compute jurisdiction.** `jurisdiction` on `POST /v1/deadlines/compute`
  reads `EM` as `EU`, `UK` as `GB` and `WIPO` as `WO`, as
  `/v1/deadline-rules` does, and the item answers with the code it was read
  as. `UK` and `EM` used to answer `unsupported_jurisdiction`. A code that is
  neither a country code nor known to the deadline rules (`XX`, `ZZ`) now fails
  only its own item, as an unknown ID does: a `deadline_computation_error`
  with `id: null` and `error.code` `unknown_jurisdiction_code`, while the
  other items are computed. `deadline_computation_error.id` can therefore be
  `null`. A real country with no modeled rules still returns
  `unsupported_jurisdiction`.
* **Country filters.** `owner_country` on trademark search and on the owner,
  attorney, firm and entity trademark lists, `country_code` on
  `/v1/entities`, `/v1/attorneys` and `/v1/firms`, and a watch's
  `filters.ownerCountry` (and the same filters on the MCP tools) now match
  every spelling, as `country_code` on
  `/v1/owners` does: `EU` and `EM` both match records stored under either, as
  do `GB` and `UK`. `owner_country=EU` used to miss the owners stored as `EM`.
  Any two-letter code is accepted, an empty value is no filter, and a value
  that is not two letters returns `400 unknown_country_code`.
* **Trademark jurisdictions.** `jurisdictions=UK` on trademark search, the
  party trademark lists, image search, `/v1/screening`, listing screening,
  `/v1/compare` and the MCP search tools is read as `GB` instead of returning
  `400` (or matching nothing), and `WIPO` as `WO`. `jurisdictions=EM` still returns `400` pointing at `offices=EM`:
  `EM` is EUIPO's office code, and `jurisdictions=EU` already matches the EU
  legs WIPO stores as `EM`.
* **Watches.** `filters.jurisdictions` reads `EM` as `EU`, `UK` as `GB` and
  `WIPO` as `WO` (`["EM"]` used to fail with `no_live_office`). Stored watch
  queries are not rewritten, but evaluation and preview now treat `EU` and
  `EM` as one territory: a stored watch with `jurisdictions` `EU` and WIPO in
  its offices may now alert on WIPO's records of EU designations, which it
  missed before. `filters.ownerCountry` must be a two-letter country code or a
  list of them (`400 unknown_country_code` otherwise); an empty value is no
  filter, and a value a stored watch already holds is accepted on edit.
  Creating, previewing or editing a watch with an owner, attorney or firm ID
  that was merged into another now returns `400` naming the surviving ID to
  watch (`owner own_X was merged into own_Y; watch own_Y`). A merged ID a
  stored watch already holds is still accepted when you edit the watch, but
  that watch does not alert on the merged ID's marks while
  `WATCH_MERGE_FOLLOW` is off (the default): point it at the surviving ID.
* **Cursors.** A list cursor issued before this change for a request with a
  `country_code` or `owner_country` filter returns `400 cursor_expired` once,
  because the filter now resolves to every stored spelling; restart from the
  first page.
* `search_meta.warnings[].code` now documents `all_words_required` and
  `multi_word_retry_skipped`.

## October 10, 2026: no restoration for Madrid designations, and nothing past an expired mark's last renewal grace

Two corrections to maintenance deadlines on
[`POST /v1/deadlines/compute`](https://docs.signa.so/api-reference/reference/compute-deadlines),
the deadline lists, `derived` on trademark records and
[`POST /v1/reconcile`](https://docs.signa.so/api-reference/reference/reconcile). Both remove rows
that were served in error, and one case gains a row: a live Madrid designation in its
renewal grace now also shows its next international renewal, which the removed
restoration row used to hide. On affected records `derived.term`,
`derived.expiry_date` and `derived.next_deadline` change with them.

* **Madrid designations get no national restoration window.** A designation
  of Japan, the UK, Singapore, India or Iceland was served a `restoration` row
  (`jp_restoration`, `gb_restoration`, ...) after its renewal grace. Those
  remedies restore a national renewal, and a designation renews only at WIPO:
  Japan's Trademark Act Article 68-22(1) and the UK's International
  Registration Order 2008 disapply them for designations, and Singapore's
  International Registration Rules, India's Trade Marks Act section 36E(4) and
  Iceland's Regulation 850/2020 do not extend them to designations. A cancelled
  designation is now served nothing (for example IR 2015-03-10, term end
  2023-07-19: the row running 2024-01-19 to 2024-07-19 is gone), and an
  expired one only the international renewal while its six-month grace is
  open. National registrations keep their restoration windows. In
  [`GET /v1/deadline-rules`](https://docs.signa.so/api-reference/reference/deadline-rules) every
  restoration rule now has `applies_to: "domestic"`, so
  `filing_route=madrid` no longer returns them. Because no restoration row
  follows the grace any more, a live designation inside its grace period is
  shown the next international renewal, as designations of other countries
  already were.
* **An expired mark owes nothing past its last renewal grace.** Once the
  grace period of an `expired` mark's latest renewal whose window has opened
  has passed with no renewal on record, no row due after it is served, with
  or without a stated `expiry_date`. Before, a Philippine mark registered
  2010-01-15 and expired was served, as of 2025-06-01, a declaration of use
  due 2026-01-15 for the fifth anniversary of a renewal that never happened.
  A renewal whose window is open is still served, and a renewal on record
  keeps the next term's rows. On compute, an expired national mark with no renewal
  on record no longer lists the Philippine fifth-anniversary declaration in
  `unsupported_rules`.

## October 10, 2026: `POST /v1/trademarks` accepts bodies up to 64 KB

[`POST /v1/trademarks`](https://docs.signa.so/api-reference/trademarks/search-trademarks#post)
accepts request bodies up to 64 KB, up from 8 KB, so an exclusion list of the
documented 1,000 `trademark_ids` now fits in one search. Before, a body over
8 KB (about 190 ids) got a `413` before it reached the API. A `GET` query
string is still limited to 2 KB (`403`), so send long lists by `POST`.

## October 10, 2026: lien releases cited in their text pair with their lien; owner list drops the redacted placeholder; related owners page fully

**Lien releases.** Assignments gain `release_match` on
[`GET /v1/assignments`](https://docs.signa.so/api-reference/records/transactions/search-assignments),
[`GET /v1/assignments/{id}`](https://docs.signa.so/api-reference/records/transactions/retrieve-assignment)
and [`GET /v1/trademarks/{id}/assignments`](https://docs.signa.so/api-reference/trademarks/trademark-assignments):
`pointer` when a release cites the security interest's reel/frame, and `null`
otherwise (always `null` for other conveyance types). A release now also cites
a security interest when its text names the reel/frame (`RECORDED AT R/F
1234/0567`, `REEL 1234/FRAME 0567`, `REEL NO. 1234 FRAME NO. 0567`) and the
release covers one of its marks; before, only the `release_of_*` fields
counted. Owner and entity analytics
([`GET /v1/owners/{id}/analytics`](https://docs.signa.so/api-reference/parties/owner-analytics),
[`GET /v1/entities/{id}/analytics`](https://docs.signa.so/api-reference/parties/entity-analytics))
`unreleased_security_interests` and `marks_with_liens` use the same pairing, so
both counts drop where a release cites its lien only in its text. Most USPTO
lien releases cite no reel/frame, so these two counts can still include liens
that were in fact released: only releases that cite their security interest are
paired. Pairing uncited releases by lender, borrower and mark is planned. New recordings described with these
citation forms, in the plural ("release of liens and security interests"), as
"release by secured party" or as a bare "security agreement" are now classified
as releases and security interests; recordings stored before this change keep
their type for now.

**Owner list.** [`GET /v1/owners`](https://docs.signa.so/api-reference/parties/search-owners) no
longer leads with "Redacted applicant (PRV #2)", the placeholder Sweden's office
uses for applicants whose names it redacts. Placeholder owners are left out of
the owner list, owner search and suggestions, as the docs already said.
[`GET /v1/owners/{id}`](https://docs.signa.so/api-reference/parties/retrieve-owner) still answers for
a placeholder owner, because the Swedish trademarks it holds link to it.

**Related owners.** [`GET /v1/owners/{id}/related`](https://docs.signa.so/api-reference/parties/owner-related)
returns every relationship when paging. A related owner with two relationships
(for example the direct and the ultimate parent) used to lose one of them at a
page boundary. A `cursor` issued before this change returns
`400 cursor_expired`; restart from the first page.

## October 10, 2026: screening no longer clears non-Latin marks; real coverage figures; compare flags self-comparisons

These changes apply to the alpha screening and compare endpoints.

* **Non-Latin marks need review.** `GET /v1/screening` matches Latin text, but
  a mark written mostly in another script, such as `НАЙК` or `耐克`, could come
  back `clear` because nothing matched. When a mark's non-Latin letters, all
  scripts together, outnumber its Latin letters, the screen now reports
  `coverage.language.supported: false` and the main script in
  `coverage.language.detected`, and a would-be `clear` becomes `needs_review`
  with the `unsupported_language` warning. Accented and fullwidth Latin letters
  count as Latin. Found conflicts still band as before. Because the verdict
  gate changed for these marks, their `screening_id` changes too; screens of
  Latin marks keep their ids.
* **Coverage figures.** `coverage.horizon` reported `analyzed: 0` and `cap: 0`
  on every screen. It now gives the conflicts analysed and the cap (200).
  `capped` is `true` when the overall horizon or a retrieval lane's quota had
  more matches than it could analyse. A `trademark_id` screen sent without
  `nice_classes` or `goods_services` lists the record's classes it searched in
  `coverage.classes.resolved` and `searched` instead of empty lists.
  `coverage.lanes` is reserved and stays empty.
* **Self-comparison.** `POST /v1/compare` adds a `self_comparison` warning to a
  result whose `trademark_id` conflict is the candidate record or another
  record in its Madrid family. The risk level is unchanged.

## October 10, 2026: owner, attorney and firm search lists the real name first

`q` on [`GET /v1/owners`](https://docs.signa.so/api-reference/parties/search-owners),
[`GET /v1/attorneys`](https://docs.signa.so/api-reference/parties/search-attorneys) and
[`GET /v1/firms`](https://docs.signa.so/api-reference/parties/search-firms) returns its matches in a
new order. Before, a name that was only close to the query (a longer word that
starts with it, a typo, two names run together) could rank above the party you
searched for: `q=nike` on owners listed "nike", "NIKEL" and "nikema", with no
trademarks, ahead of NIKE, Inc. Now a name that contains every word of the
query gets a boost for its trademark count and ranks above typo matches, so
among those names the party with more trademarks ranks higher. A name with a
longer word that starts with the query can still rank high. Typos still match,
lower in the list.

The new order applies when `sort` is not sent. An explicit `sort` orders the
matches by that field, as before, and so do owner searches with `ticker`, `lei`,
`publicly_traded` or `has_lei`, which order matches by trademark count. One kind of match is gone on every order: a
query word with a typo has to be close to a whole word of the name, and no
longer matches a name that only starts with a similar word.

A cursor issued before this release for a `q` list without `sort` can skip or
repeat rows; start that walk again from the first page. The reference pages
described `q` as a trigram or substring search. They now describe what it does.

## October 10, 2026: filters from other endpoints return 400 instead of being ignored

A filter an endpoint does not support used to be ignored, so the answer looked
filtered when it was not: `GET /v1/analytics/market?offices=US` returned the
worldwide totals and `GET /v1/deadline-rules?office=EM` the whole catalogue.

* **Filters from other endpoints.** A GET request with a query parameter the
  endpoint does not support, when that parameter is a filter on another
  endpoint (`offices`, `office`, `jurisdiction`, `q`, `status`,
  `nice_classes`, and so on), returns `400` with code `unknown_parameter`.
  The message names the parameter this endpoint has for it, if any:
  `deadline-rules?office=EM` says to use `jurisdiction`.
* **Common spellings.** A few spellings no endpoint uses are rejected the same
  way: `country`, `nice_class`, `query` and `trademark_ids`.
* **`watch_id` on `GET /v1/alerts`.** It never filtered that list; it now
  returns `400` pointing at `GET /v1/watches/{id}/alerts`.
* **Still ignored.** Paging and shaping parameters (`limit`, `cursor`,
  `include_total`, `sort`, `order`, `fields`, `include`, `format`,
  `language`, `page`, `per_page`, `offset`) and other names no endpoint uses.
  Endpoints that already rejected unknown parameters, `/v1/trademarks`
  included, behave as before.

## October 10, 2026: filters that match nothing now say so

Several filters matched nothing, with a 200, for a value that only differed in
spelling or case from one Signa knows, or that was no code at all. These
requests now return `400 validation_error` with a hint, or are read the way you
meant them.

New `400` responses:

* **Jurisdiction codes.** `jurisdiction` on `/v1/deadline-rules` and
  `/v1/opposition-rules` returns `400` for a value that is not a country or
  jurisdiction code, such as `ZZ` or `USPTO`. A real country with no data
  still returns an empty list, and historic Madrid designation codes the
  rules cover, such as `YU` or `SU`, keep working. `jurisdictions=ZZ` on trademark search also
  returns `400`: `ZZ` is internal and was never a jurisdiction you could
  search. `country_code` on `/v1/owners` returns `400` only for a value that
  is not two letters; historical codes owners carry, such as `DD` or `SU`,
  keep working.
* **Fee catalog.** `GET /v1/fees?office=` returns `400` for a value that is not
  an office or country code (`XX`, `JPO`). Other offices and countries still
  return an empty list.
* **Event types.** `event_type` on `GET /v1/trademarks/{id}/events` and
  `GET /v1/events` returns `400` for an unknown type, listing the valid ones.

Read the way you meant:

* **Office spellings of a jurisdiction.** `EM` is read as `EU`, `UK` as `GB`
  and `WIPO` as `WO` in `jurisdiction` on `/v1/deadline-rules` and
  `/v1/opposition-rules`. `deadline-rules?jurisdiction=EM` used to return only
  the Madrid renewal rule; it now returns the EU rules too.
  `GET /v1/fees?office=UK` now returns the GB fees.
* **Owner country.** `country_code` on `/v1/owners` is case-insensitive and
  matches every spelling owners are stored under: `EU` and `EM` both return
  the owners recorded as either, as do `GB` and `UK`.
* **Event type case.** `event_type` is case-insensitive: `Renewal` matches
  `renewal`.
* **EU legs of Madrid marks.** With `territory_match=direct`,
  `jurisdictions=EU` now also matches the WIPO record of a Madrid designation
  of the EU, which WIPO stores as `EM`. That record's `jurisdiction_code` now
  reads `EU` in every mode and on trademark detail, as territory codes
  already did, and the `jurisdiction_code` aggregation and the party
  analytics `jurisdiction_spread` count it under `EU`.

Better errors:

* Every `validation_error` now carries an `errors` array. An error that is not
  about one field has a single entry with field `(request)`.
* A misspelt parameter on an endpoint that rejects unknown parameters gets a
  suggestion: `stauts` says to use `status_stage`.
* A trademark number used as a trademark ID (`GET /v1/trademarks/73120444`)
  points at `GET /v1/trademarks?q=73120444`.

New warning: `search_meta.warnings` includes `partial_owner_country_coverage`
when you filter by `owner_country` and EUIPO records are in scope. EUIPO
publishes no owner country, so the filter matches no EUIPO trademark and EU
trademark counts are missing from the result.

Because the EU leg's `jurisdiction_code` changed with no write to the mark,
trademark and portfolio ETags change once: a client holding an older validator
gets the new body with a `200` instead of a `304`.

## October 10, 2026: `near_identical` means about the same length; fewer look-alike rows for Cyrillic and Greek names

`match.tier` on [`GET /v1/trademarks`](https://docs.signa.so/api-reference/trademarks/search-trademarks)
read `near_identical` for a one-letter difference or a strong sound-alike even
when the mark had more words than `q`: `q=BRANDX` labelled MARLON BRANDO and
BRANDX ACTIVEWEAR `near_identical`. Such a match is `q` inside a longer mark,
and is now `similar`, as the tier table says. Each name of a `q` list is
measured against its own word count. Ranking and the rows returned are
unchanged; only the label moves.

The same tier gates watches: a watch with `min_tier: near_identical` no longer
alerts on a mark that matches only this way, and alerts carry the new tier. A
watch with `min_tier: similar` or weaker is unaffected.

A `q` written only in Cyrillic or Greek letters, such as `Кока-Кола`, no longer
runs the look-alike matches that allow a changed letter. Those matches folded
the whole name into Latin letters and returned unrelated marks (KODA LUNA, KOGA
PACE) as `near_identical`. Whole-mark look-alike matches still run (`ВОТОХ` written in Cyrillic still
finds BOTOX), and a name that mixes Latin with Cyrillic or Greek letters keeps
every look-alike match. Watches match the same way: an existing watch whose `q`
is written only in Cyrillic or Greek letters stops alerting on marks it reached
only through those matches, without being re-saved.

Because these searches return different rows, `search_meta.ranking_version` is
now `v13`, and a search cursor issued before this release is rejected once with
`400 cursor_invalid`: restart that search from its first page.

## October 10, 2026: batch and reconcile find numbers written with separators; one IR number format

[`POST /v1/trademarks/batch`](https://docs.signa.so/api-reference/trademarks/batch-trademarks) and
[`POST /v1/reconcile`](https://docs.signa.so/api-reference/reference/reconcile) now find numbers
written the way docketing systems export them. A number that finds nothing as
sent is tried again with spaces, commas, periods, slashes and hyphens removed
(and a leading `WO` removed from a batch `ir_number`), when what is left is a
number: `73/273,292`, `1,201,370` and `WO1874500` find `73273292`, `1201370`
and IR `1874500`, where they used to come back in `not_found`. A record stored
with the separators still wins. On reconcile with `verdict_detail: true`,
`lookup.identifier_normalization` is `separators_removed` when this happened,
and `lookup` names the numbers looked up. A batch application number that more
than one record shares now always answers the same record.

`ir_number` on trademark rows (search, list, detail, batch and every party's
trademark list) and on assignment properties is now always the international
registration number without leading zeros. USPTO, EUIPO and CIPO records of a
Madrid registration used to show it as stored (`0551089`) while the WIPO rows
showed `551089`, so linking them by string equality failed. If you stored the
zero-padded value for a US designation, match on the new value; the `ir_number`
filters accept both forms, as before.
Because the value changed with no change to the records, the trademark detail,
portfolio and assignment ETags change once: a cached copy revalidates into a
`200` with the new value.

## October 10, 2026: search says when a name of several words found little

A `q` of several words on [`GET /v1/trademarks`](https://docs.signa.so/api-reference/trademarks/search-trademarks)
generally requires its words to appear in the mark, so
`q=stone lion capital` does not find LION CAPITAL. Such a
search that returns fewer than 5 rows in all now carries an `all_words_required`
warning in `search_meta.warnings` (not with the `phonetic` channel, which also
finds marks sharing one sound-alike word), with a ranked list of shorter phrases to send
instead (`q=["stone lion","lion capital"]`). When Signa skips its second search
for a multi-word name with no results (one that allows a one-letter difference
in each word) because the first took too long, the response now carries
`multi_word_retry_skipped` instead of saying nothing. Results are unchanged.
The search docs and the MCP `search_trademarks` description now explain the
rule.

## October 10, 2026: reconcile no longer counts an empty register field as a mismatch

[`POST /v1/reconcile`](https://docs.signa.so/api-reference/reference/reconcile): a field the office
does not publish at all now comes back with `match: null` instead of
`match: false`, and it no longer counts toward `mismatch_count` or turns
`result` into `mismatch`. That is the expiry and renewal due date at USPTO,
which publishes neither, and the renewal due date at most other offices. A US
mark sent with `expiry_date` or `renewal_due_date` used to read `mismatch` on
every item, and now reads `match` when everything else agrees. A field the
office does publish but this record lacks, such as the registration number of a
pending application, is still `match: false`. `match` on `fields[]` is now `boolean | null`. With
`verdict_detail: true`, `field_verdicts` still gives Signa's computed date for
those fields.

## October 10, 2026: watch previews apply every filter; dead watch settings are rejected

**Preview.** On a busy office, a watch without a text query used to report
every changed mark at the watched offices: a US class 25 watch, a class 46
watch and a watch on a made-up owner all previewed 167,817 with
`estimate_basis: "candidacy_upper_bound"`. Such a watch is now previewed like a
text watch, with its filters as the search, so classes, owners, statuses and
`trigger_events` apply. The count is exact, or `estimate_basis: "lower_bound"`
when more than 10,000 marks matching the filters changed in the window.
`candidacy_upper_bound` remains only for a watch scoped by offices or trademark
IDs alone, or a time budget that ran out. When the search backend is down, such
a preview returns `503 service_unavailable` (retryable), as a text watch already
did, instead of an unfiltered count.

**New `400` responses** on create, `PATCH`, bulk create and preview:

* a `filters.niceClasses` entry that is not a class from 1 to 45 (field
  `query.filters.niceClasses[i]`). A class sent as a numeric string, such as
  `"25"`, is still accepted and stored as the number `25`;
* a `filters.ownerId`, `attorneyId` or `firmId` that is not a non-empty ID
  string, names no record, or names an owner Signa has retired.

Existing watches are not changed. A watch saved earlier with such a value keeps
loading and evaluating, and a `PATCH` that sends the stored value back is
accepted. A watch whose stored Nice classes include a value outside 1-45 now
shows `status: "degraded"` with `invalid_filter` in its `health.issues`.

**Merged owners, attorneys and firms.** Support for a watch to follow an owner,
attorney or firm that is later merged into another record ships disabled: it is
being rolled out behind a switch, and until it is on a watch matches the marks
of the ID it names. The stored watch keeps the ID you saved either way.
Diagnostics names a merge, with `merge follow not enabled` while it is off.

**Bulk create.** A failed batch lists every failing item in one `400`, and each
`errors[].field` starts with the item's index (`watches[2].query.q`). Before,
only the first error came back, without an index.

**Diagnostics.** `reason` now says when the change predates the watch, when the
watch is paused (with the cause: paused, out of credits, or disabled) or has
not evaluated the change yet, when a watched owner was
retired, and otherwise `mark did not match the watch query or filters` (naming
a merged owner, attorney or firm).
It no longer returns `no matching reason available`. An unknown `trademark_id`
on your own watch returns a `404` that names the trademark, not the watch.

**Attestation.** In the month a watch was created, an office with no
evaluations shows its gap from the watch's creation, not from the start of the
month, so the attestation no longer records a lapse before the watch existed.
This changes `content_hash` for those periods. A `period` that ended before the
watch was created, with no evaluations recorded in it, returns
`422 period_before_watch`.

## October 10, 2026: Madrid renewal deadlines for zero-padded IR numbers

Some offices store an international registration number with leading zeros
(`0921642`), while WIPO stores it without (`921642`). Signa missed the WIPO
registration for those designations, so their renewal deadlines were anchored
on the designation's own date, or not computed at all. They now anchor on the
international registration date, like every other Madrid designation. This
changes the `derived` deadlines on trademark detail, batch and search
responses, the deadline endpoints, portfolio deadline lists and calendar
feeds, and `/v1/reconcile` for the affected designations: a few dozen EUIPO
and CIPO records.

## October 10, 2026: documentation corrected where it could lead to a wrong legal read

No API behavior changed. These pages stated something the API does not do, or left out a limit that changes how to read a value:

* **US Section 71 deadlines.** The guides named `protection_grant_date` as the trigger for US Madrid Section 71 and Section 15 deadlines. Signa computes them from `registration_date`. On the USPTO's own 79-series record that is the date of the certificate of extension of protection, the date 15 U.S.C. 1141k sets for Section 71. A US designation row from WIPO's records carries the date WIPO recorded instead, so docket from the USPTO record. With [Compute Deadlines](https://docs.signa.so/api-reference/reference/compute-deadlines.md), send the certificate date as `registration_date`. See [Deadline rules](https://docs.signa.so/guides/deadline-rules.md#us-madrid-marks).
* **Compute Deadlines `operative_date_basis`.** The office action fact's `operative_date_basis` was described as defaulting to `caller_supplied`. It is required and has no default; omitting it returns `400`.
* **US proceeding outcomes.** Most ended US (TTAB) oppositions and cancellations read `outcome: other`, so a US win rate, including `win_rate` on owner and entity analytics, covers only cases decided on the record. The opposition guide, Search Proceedings and the analytics pages now say so.
* **Watch preview.** The quickstart showed an exact count for a US class watch that the preview could not produce then: on a busy office it reported every changed mark at the watched offices. Since the same day's preview change (see the entry above), preview applies the class filter there too, and the quickstart shows that. See [Watches](https://docs.signa.so/guides/monitoring/watches.md).
* **Record fields.** `filing_bases` are the bases as filed; later changes are not reflected. `legal_form` is a normalized value (US LLCs map to `corporation`), not the form as filed. `figurative` marks can contain words in stylized lettering.
* **Search.** The USPTO publishes no expiry date, so `expiry_date_*` never matches USPTO records, and `renewal_due_date_*` matches only USPTO Madrid extensions (79-series) carrying the international renewal date, never a domestic Section 8 or 9 date. A name written as one word (`cocacola`) does not find it split by punctuation inside a longer mark (COCA-COLA ZERO); search each spelling, or send `mark_text_contains` without `q`.
* **Analytics and liens.** Office, market and class counts are trademark records, one per office record: a Madrid international registration counts for its own record, for each designation record, and again for any national office's own record of a designation (such as a USPTO 79-series record). The office `registration_rate` is not reliable for `GB`. An unreleased lien can be a released one whose release cites no reel/frame. `citation_issued` can follow a final refusal whose text Signa has not extracted yet.
* **Portfolios.** Managed marks count on the daily monitoring meter, free during the beta.

## October 10, 2026: `office_action.issued` fires only for USPTO examiner actions

For USPTO marks, `office_action.issued` now fires only when the office issues
an action, for example a non-final or final refusal, a suspension letter or
inquiry, an examiner's amendment or a post-registration audit action (63 USPTO
codes in all). Before, 206 other USPTO codes that the office files under the
same heading could raise it too: your own filings (a voluntary amendment or a
response received), transactions with WIPO such as a grant of protection,
suspension checks, abandonment notices and decisions on petitions (grants,
denials, dismissals). A docketing system that opens a response deadline per event would
have opened one for each.

Those rows still appear on `GET /v1/trademarks/{id}/events` with the same
`event_type`. An abandonment still raises `trademark.status_changed`. Other
offices are unchanged.

## October 9, 2026: US deadline corrections

Five corrections to US deadlines. All of them apply to every response from
today, on trademark detail and search (`derived`), `GET /v1/deadlines`,
portfolio deadlines and the calendar feed, `POST /v1/deadlines/compute` and
`POST /v1/reconcile`.

* **WIPO records of US designations no longer show Section 71 or Section 15
  deadlines.** A WIPO record designating the US (`office_code: "WO"`,
  `jurisdiction_code: "US"`) showed `us_madrid_declaration_of_use_s71_first`,
  `us_madrid_declaration_of_use_s71_at_renewal` and
  `us_madrid_declaration_of_incontestability_s15` dated from the WIPO
  registration date. The law runs them from the date the USPTO issued its
  certificate of extension of protection, which only the USPTO's own record of
  the designation carries. Where the US is the home office of the
  international registration there is no US designation at all. These records
  now show no rows for those rules and list each of them in
  `unsupported_rules` with the new reason `computed_on_office_leg`. The USPTO
  record of the same designation keeps its rows, dated from the US
  registration date. If you imported these rows from WIPO records, remove
  them and use the USPTO record's rows instead.
* **Supplemental Register registrations no longer show Section 15.** The
  Section 15 declaration of incontestability is not available on the
  Supplemental Register (15 USC 1094), so
  `us_declaration_of_incontestability_s15` no longer appears for a US
  registration whose `register.type` is `supplemental`.
  `POST /v1/deadlines/compute` takes an optional `register_type`
  (`principal` or `supplemental`) for the same result.
* **Extended response and statement of use deadlines are counted from the
  issue date.** An extended Office action response deadline now runs six
  months from the Office action issue date, and each statement of use
  extension runs from the notice of allowance (37 CFR 2.62(a)(2), 2.89), so
  extended dates that landed after a short month move 1 to 3 days later, to
  the correct date. For example, an Office action issued 30 November 2025 and
  extended is due 1 June 2026, not 28 May 2026.
* **Maximum dates move off weekends and federal holidays.** A deadline that
  reaches its maximum (the six-month response limit, the 36-month statement of
  use limit) on a Saturday, Sunday or federal holiday is now due on the next
  open day (37 CFR 2.196), with `due_date_adjustment: "moved"`. Before, it was
  served on the closed day with `not_checked`. For example, a refusal of a
  Madrid designation sent on 25 June 2026 is due 28 December 2026, not on
  Christmas Day.
* **An unrenewed US registration shows its term ended.** When a US
  registration's renewal (the combined Section 8 and Section 9 filing) is not
  on record and its six-month grace period has passed, `derived.term.state`
  now reads `renewal_not_on_record` and `derived.expiry_date` is the term end
  that passed, instead of `in_term` with the next ten-year renewal. The next
  cycle's renewal row is no longer shown. This applies only when Signa has the
  registration's USPTO prosecution history, that history records at least one
  Section 8 or Section 9 filing (such as the six-year Section 8; a Section 15
  filing does not count), and the USPTO status does not say the registration
  was renewed. For a past computation date, only filings and a status dated
  by then count. The USPTO keeps such a registration
  "registered" until it cancels it, usually within a few weeks; Signa's
  `status` keeps the office's value until then.

## October 8, 2026: an attorney's firms come from its filings

An attorney's firm and address are now what its filings say.
[`GET /v1/attorneys/{id}`](https://docs.signa.so/api-reference/parties/retrieve-attorney) gains
`firms`: the firms named on the attorney's current filings, with `id`, `name`,
`trademark_count` (the attorney's trademarks filed with that firm) and
`latest_filing_date`, most trademarks first, at most 20 (the firm in `firm_id`
is added when it ranks lower).

`firm_id` and `firm_name` on the attorney detail, and `firm_name` on
[`GET /v1/attorneys`](https://docs.signa.so/api-reference/parties/search-attorneys), are now the firm
on the attorney's most recent current filing (by filing date) that names a
firm, `null` when none does. Before, they were the firm on whichever filing
first created the attorney record, which could be a firm the attorney filed
with once, years ago: an attorney with thousands of filings could show a firm
named on three of them. `firm_id` and `firm_name` on the detail are deprecated
in favour of `firms`.

`address` on the attorney detail is the address on the attorney's most recent
current filing that carries one, `null` when none does. For WIPO, Sweden and
Italy attorneys, whose older filings were stored without an attorney address,
the address the office holds for the representative is shown when no filing
address is on record.

`GET /v1/attorneys?firm_id=` lists the attorneys with at least one current
filing that names the firm, so an attorney appears under every firm its filings
name. Searching attorneys by `q` also matches the names of every firm on their
filings once the attorney search index has been rebuilt, which follows this
release; until then `q` matches the attorney's name.

The firms and the address are recomputed with the nightly statistics, so a
filing that changes them shows up the next day.

## October 10, 2026: a listed GLEIF ultimate parent passes its ticker to subsidiaries

An entity now inherits a listed company's ticker when GLEIF reports that company as its ultimate parent, even when its direct-parent chain never reaches a listed company. Before, only the direct-parent chain counted, so an IP-holding subsidiary whose direct parent is an unlisted holding company carried no ticker. For example, NIKE INNOVATE C.V., which holds about 1,960 Nike marks, did not match `ticker=NKE`.

What changes for such an entity:

* **Entities.** [Retrieve Entity](https://docs.signa.so/api-reference/parties/retrieve-entity.md) shows a `listing` block with `status: "subsidiary_of_listed"`, the ultimate parent's ticker and LEI, `publicly_traded: true`, and the ticker in `tickers`. `ticker=` on [Search Entities](https://docs.signa.so/api-reference/parties/search-entities.md) and [Search Owners](https://docs.signa.so/api-reference/parties/search-owners.md), and `publicly_traded=true` on Search Entities, match it.
* **The direct chain still wins.** When the direct-parent chain reaches a listed company, that company stays the listed ancestor.
* **The newest ultimate parent counts.** GLEIF reports one ultimate parent at a time. When older ultimate-parent records are still present, only the most recent one is used: if it is not listed, the entity gets no inherited ticker. If two or more different companies share the most recent start date, the entity gets none.
* **Timing.** Listing data refreshes with the weekly GLEIF update, so these entities change after the first weekly refresh following this release. Entity search and detail, and owner search and detail, show the change the same day. The `owner_ticker` and `owner_publicly_traded` filters on trademark search and on watches follow within about a week, when the weekly public-company check refreshes each affected trademark.
* **Unchanged.** [Entity Family](https://docs.signa.so/api-reference/parties/entity-family.md) and `include_family` still follow direct parent and subsidiary edges only.

## October 8, 2026: a filing shows only the firm it names

A trademark's attorney now shows the firm named on that filing, and no other.
Before, when a filing named no firm, Signa filled in a firm the same attorney
was named with on some other filing. That borrowed firm could be a firm the
attorney had never filed this mark through, so trademarks, search results and
firm totals credited marks to the wrong firm.

What changes:

* **Trademark detail and batch.** `attorneys[].firm_id` and `firm_name` are the
  firm the filing names, and `null` when it names none. `attorneys[].address`
  is the address on the filing, and `null` when the filing has none. WIPO, PRV
  (Sweden) and Italian records never carry the address on the filing, so they
  keep showing the attorney's address on file.
* **Madrid international registrations.** The detail lists an attorney once
  per firm its designations name, each row with the `territory_codes` of the
  designations naming that firm. Designations that name the attorney with no
  firm get their own row with `firm_id: null`; they no longer take another
  designation's firm.
* **Search.** The `firm_id` filter and facet match only filings that name the
  firm. `attorney_firm_name` matches the firm name the filing states for its
  first (primary) attorney.
* **Watches.** A watch filtered by `firm_id` or `attorney_firm_name` matches
  fewer marks once the search index is rebuilt: only the filings that name the
  firm.
* **Firm totals.** `trademark_count`, `active_count` and the other counts on
  `/v1/firms` and `/v1/firms/{id}` count the filings that name the firm.
  `attorney_count` counts the attorneys on those filings. Firms credited
  through borrowed firms drop sharply: a firm that showed 5,031 trademarks can
  now show 3. A firm that no filing names has no counts.
* **Firm attorneys.** `GET /v1/firms/{id}/attorneys` and
  `GET /v1/firms/{id}?include=attorneys` list the attorneys with at least one
  current filing that names the firm. Each row's `trademark_count`,
  `grant_rate` and `latest_filing_date` cover the filings naming this firm,
  not the attorney's whole portfolio, and the list is sorted by that count.
  `firm_name` is this firm's name.

If you group or join trademarks by `attorneys[].firm_id`, expect more `null`
values and smaller groups. A `null` firm means the filing names no firm. It
does not mean the attorney practises alone. Search results and firm counts
switch over after the next search rebuild and stats recompute.
This supersedes the firm-of-record fallback described on October 4 and
September 29.

## October 7, 2026: renewal deadlines for Madrid designations without their own rules

A Madrid designation of a jurisdiction that has no deadline rules of its own now
gets its renewal. That covers the EU designated through Madrid (WIPO's code
`EM`), Russia, Ukraine, Austria and 93 more codes in Signa's data, historic ones
such as `DT`, `SU` and `YU` included: about 3.2 million WIPO designation records,
2.1 million of them live. The EUIPO's own records of Madrid designations of the
EU (`EU` on the Madrid route) take the same rule. One new `rule_id`:
`madrid_designation_renewal`, named "International Registration Renewal (Madrid
designations)" in the catalog.

The rule serves only codes WIPO designates: the WIPO Standard ST.3 codes of the
117 Madrid members (the EU as `EM`, Belgium, Luxembourg and the Netherlands as
`BX`), the territories the Protocol extends to (Curaçao, Sint Maarten, Bonaire,
Sint Eustatius and Saba, Guernsey, Jersey), and the historic codes WIPO's
register still carries (`DD`, `DT`, `CS`, `SU`, `YU`, `AN`). Any other code,
`UK` or `ZZ` for example, stays `unsupported_jurisdiction`.

The designation renews with the international registration at WIPO. Renewal is
one payment there, it takes effect on the same date for every designation, and
a later designation ends with the international registration (Madrid Protocol
arts. 3ter(2), 6(1) and 7(1); Regulations Rules 30 and 31(2)). So the rule runs
10 years from the international registration date, or from the expiry WIPO
states, with a window opening 6 months before the due date, a 6-month grace
period against a surcharge, and due dates moved off WIPO's closed days. Every
value cites the Madrid texts, the WIPO Guide or the EUIPO guidelines with a
verbatim quote and a pinned snapshot.

These records now carry `derived.expiry_date`, `derived.term` with its
`grace_expiry_date`, and the next renewal in `derived.next_deadline`. Before,
`derived.deadlines` read `supported: false` with
`reason: "unsupported_jurisdiction"` and `derived.term.state` read
`not_applicable`. The rule's rows carry the designated jurisdiction's code
(`RU` on a Russian designation). [`GET /v1/deadline-rules`](https://docs.signa.so/api-reference/reference/deadline-rules)
lists the rule once, under `WO`, and also returns it for `?jurisdiction=` any
code it serves.

Points to know:

* A Madrid designation of the EU, WIPO's `EM` record or the EUIPO's `EU`
  record on the Madrid route, is not served the EUIPO renewal (`eu_renewal`)
  or restitutio (`eu_restitutio`): the EUIPO does not handle renewal of
  international registrations designating the EU. On the EUIPO's records this
  changes the rule, the consequence (`lapse_of_international_registration`),
  the calendar (`wipo_geneva`, so a due date on a EUIPO-only closure day such
  as 1 May no longer moves) and drops the restitutio row. Both EU rules now
  read `applies_to: "domestic"` in the catalog. EU trade marks filed at the
  EUIPO keep their dates; their rows' `last_verified` reads `2026-10-07` and
  the EU `sources` no longer list the Madrid Protocol citation that covered
  the designation case.
* On the EUIPO's records of Madrid designations of the EU, each renewal row's
  `occurrence_key` starts with the new `rule_id` (`madrid_designation_renewal:`
  where it read `eu_renewal:`), so it changes once. The portfolio iCal feed
  builds its event UID from that key, so a subscribed calendar sees each such
  event replaced once: the old event removed and a new one added. Any
  `eu_restitutio` event is removed.
* [`POST /v1/deadlines/compute`](https://docs.signa.so/api-reference/reference/compute-deadlines)
  and [`POST /v1/reconcile`](https://docs.signa.so/api-reference/reference/reconcile) apply the same
  rule to the items you send. `{"jurisdiction": "EU", "filing_route": "madrid"}`
  now returns `madrid_designation_renewal` where it returned `eu_renewal` (and
  no `eu_restitutio` row), and a Madrid-route item coded `RU`, `EM` or any
  other served code is now `supported` where it read
  `reason: "unsupported_jurisdiction"`. The prosecution half (`facts[]`) is
  unchanged: an EU Madrid item keeps the EUIPO provisional-refusal rule
  (`euipo_madrid_oa_response`), and a jurisdiction without its own rules still
  reads `unsupported_jurisdiction` for every prosecution type, with no
  `stated_deadline` row served.
* The schedule is the renewal only. Cabo Verde, Cambodia and Mozambique also
  require use declarations filed with their national office (WIPO Information
  Notices 27/2022, 11/2016 and 37/2016), which are not computed; every row of
  such a designation says so in its `note`, naming the notice.
* A designation whose status stage is `unknown` (about 143,000 of these WIPO
  records, 49,681 of them Jersey) is served like any other record, with one
  exception: it is not credited a later expiry WIPO published for its
  international registration, because that renewal may have left it out. It
  keeps the term WIPO states for it, lapsed or not. On 7 October that held
  back 211 records whose family showed a later term. The same now applies to
  Madrid designations in jurisdictions with their own rules (16 records on
  7 October).
* A designation that WIPO reports refused, invalidated, surrendered, cancelled
  or expired past its grace gets no later renewal. One with neither an
  international registration date nor a stated expiry returns
  `reason: "requires_office_date"`. National records of these jurisdictions,
  and every other jurisdiction that has its own rules, are unchanged.

See [Madrid designations without their own rules](https://docs.signa.so/guides/deadline-rules.md#madrid-designations-without-their-own-rules).

`ruleset_version` on `derived` changes once, so trademark detail and portfolio
ETags change once on deploy. Maintenance rows are computed per request and need
no reindex.

## October 6, 2026: Portugal (INPI) deadline rules

New jurisdiction. `PT` now has a maintenance config, so `GET /v1/deadline-rules`,
`POST /v1/deadlines/compute` and the `derived` block on a trademark answer for
Portuguese marks. Two new `rule_id` values: `pt_renewal`, `pt_renewal_madrid`.

Renewal runs 10 years from the FILING date (Código da Propriedade Industrial
art. 247.º), with the fee due in the last 6 months of the term and a 6-month
window after it against a surcharge; missing both ends the right (arts. 368.º
n.º 6-7 and 36.º). That is `pt_renewal`, for national marks. A Madrid
designation of Portugal has no Portuguese term to renew: the international
registration is renewed at WIPO, so `pt_renewal_madrid` runs the same 10 years
and 6-month windows from the international registration date (Madrid Protocol
arts. 6(1) and 7(1), Regulations Rule 30(1)) and moves off WIPO's closed days.
Every value cites the Code, the Madrid texts or the INPI service page it comes
from, with a verbatim quote and a pinned snapshot.

Every Portuguese mark Signa holds today is a Madrid designation, about 78.7k
marks. They now carry `derived.expiry_date`, `derived.term` with its
`grace_expiry_date`, and the next renewal in `derived.next_deadline`. Before,
`derived.deadlines` read `supported: false` with
`reason: "unsupported_jurisdiction"` and `derived.term.state` read
`not_applicable`.

Two limits are documented rather than hidden. A national mark granted before
1 July 2019, when the current Code took effect, keeps the previous Code's term,
which ran from the grant (Decreto-Lei n.º 110/2018 art. 7.º n.º 2), and the
text does not say whether its later renewals run from the filing anniversary or
from the end of that term. Until INPI clarifies the transitional rule, such a
mark, or one with no grant date filed before that day, returns
`supported: false` with `reason: "requires_office_date"` unless the office's
term end is supplied. And no closed-day roll is applied to national dates: the
Code's periods are continuous (art. 27.º), so every adjustment reads
`not_checked`. Madrid designations are affected by neither. See
[Portugal (INPI): Marks Granted Before 2019-07-01](https://docs.signa.so/guides/deadline-rules.md#portugal-inpi-marks-granted-before-2019-07-01).

`ruleset_version` on `derived` changes once, so trademark detail and portfolio
ETags change once on deploy. Maintenance rows are computed per request and need
no reindex.

## October 6, 2026: class suggestion and goods and services on every plan

[Suggest Classifications](https://docs.signa.so/api-reference/reference/suggest-classifications.md), [Suggest Goods and Services](https://docs.signa.so/api-reference/reference/suggest-goods-services.md) and [Validate Goods and Services](https://docs.signa.so/api-reference/reference/validate-goods-services.md) are now available on Free. On a Free key, `POST /v1/classifications/suggest`, `POST /v1/goods-services/suggest` and `POST /v1/goods-services/validate` no longer return `403 plan_gated`. Credit prices are unchanged: 10, 100 and 50 credits per request, spent from the plan's monthly pool. [Get Credit Pricing](https://docs.signa.so/api-reference/administration/get-credit-pricing.md) reports `min_plan: "free"` for all three.

## October 5, 2026: rank many spellings in one search with a list in `q`

`q` on `GET` and `POST /v1/trademarks` and the MCP `search_trademarks` tool takes a list of up to 100 names, such as every spelling of a mark you are clearing. Each name runs through the same `similarity` channels in one search. A mark that resembles several names appears once, ranked by its closest name, and `match.terms` lists the names that found it. With `include=match_details`, `match.details.terms` gives each name's own tier and channels.

```bash theme={null}
curl -G "https://api.signa.so/v1/trademarks" \
  -H "Authorization: Bearer $SIGNA_API_KEY" \
  --data-urlencode 'q=["onyx","0nyx","onix"]' \
  --data-urlencode "similarity=identical,lookalike"
```

* **Look-alikes are found.** With `lookalike`, `onyx` also finds 0NYX (a zero for the O). Before, a list in `q` was an alias of `mark_text_is`, an exact filter, and missed such marks.
* **Limits.** Up to 100 names when `similarity` is only `identical` and `lookalike`. With `fuzzy`, `embedded` or `phonetic`, including the default, up to 10 names and 30 words in total. More than 100 names returns `400` with `code: too_big`. More than 10 names or 30 words with those channels returns `400` with `code: too_complex`, naming the fix.
* **Same price.** A list costs 10 credits per page, like any search.
* **Not a number lookup.** A list is never read as a trademark number; use [Batch Retrieve](https://docs.signa.so/api-reference/trademarks/batch-trademarks.md).
* **Pages are ranked across all names.** A name with no hits on the pages you read is not proof that it is clear: page to the end, or search that name on its own.
* **Owner, attorney, firm and entity `/trademarks` lists** take one search text; a list there returns `400`.

**Breaking:** a list in `q` without `similarity` now runs the default channels, so a list over 10 names returns `400` until you add `similarity=identical,lookalike`, and its rows are ranked, with `relevance_score`. For an exact, letters-as-written filter over several spellings, send `mark_text_is`. A list with `match=exact`, `starts_with`, `ends_with` or `contains` returns `400`. Cursors issued for a list before this change return `400 cursor_invalid`.

**Removed:** `match_explanation` and `matched_terms` on results, and `search_meta.strategies_used` and `search_meta.match`. Read `match` and `search_meta.similarity_applied`. See the [migration guide](https://docs.signa.so/guides/migrating-search-parameters#response-fields).

## October 5, 2026: owner list `ticker` and `lei` match the owner's detail

`GET /v1/owners?ticker=` and `?lei=` now match the tickers and LEIs [Retrieve Owner](https://docs.signa.so/api-reference/parties/retrieve-owner.md) returns. Before, they matched only the owner's own company matches, so an owner linked to an entity was missing from `?ticker=` of the ticker its detail showed.

* **`ticker`** matches any of the owner's tickers: its own SEC tickers or, when it has none, its entity's (the members' SEC tickers and the entity's listing ticker, which can be inherited from a listed parent). This is how `GET /v1/entities?ticker=` matches an entity. The `ticker` on the owner's detail is always one of them. Matching is case-insensitive.
* **`lei`** matches any of the owner's LEIs: its own GLEIF LEIs or, when it has none, its entity's (the entity's own LEI and the members' LEIs). The `lei` on the owner's detail is always one of them. Matching is case-insensitive.
* A ticker on a GLEIF record is not one of the owner's tickers, as on detail, so it no longer matches `?ticker=`.
* An owner whose entity is no longer served (`GET /v1/entities/{id}` returns 404) is matched on its own company matches only.

`?publicly_traded=` and `?has_lei=` are unchanged: they read the owner's own company matches, so for an owner linked to an entity they can disagree with the `publicly_traded` and `has_lei` its detail shows. No fields are added or removed.

## October 5, 2026: MCP `list_proceedings` filters by party owner or entity

The MCP `list_proceedings` tool takes the party filters `GET /v1/proceedings` already supports: `party_owner_id` (`own_...`) returns proceedings where a party is linked to that owner, and `party_entity_id` (`ent_...`, alias `entity_id`) where a party is linked to any member owner of that entity. Validation matches REST: a malformed or wrong-type ID returns `validation_error` (or `id_type_mismatch`), an entity with more member owners than the cap returns `entity_too_large`, and an unknown entity returns an empty page. Parties without an owner link are not matched; use the REST `q` filter to match by name.

## October 4, 2026: Deadline coverage for expired, cancelled and dead marks; reconcile names declined rules

* **Expired and cancelled marks name the rules they can still be served.** A mark that has expired or been cancelled was registered, so a deadline that runs from the registration date (or, in Brazil and Mexico, the grant date) is owed when the mark's status can still be served it. An expired mark is served any rule whose miss costs something while that rule's window is open; a one-off declaration of a national mark (such as US Section 8, the Philippine third- and fifth-year declarations or the Mexican third-year declaration) closes before its term ends, so an expired national mark is not asked for its date, while a Madrid designation granted late in the term of its international registration still is. A cancelled mark is served only a renewal that opens a restoration window Signa models (EU, GB, IS, IN, JP, SG); of those renewals only Japan's runs from the registration date, so a cancelled Japanese mark is the one that can lack it. With `include_missed: true` on `POST /v1/deadlines/compute`, and always on `POST /v1/reconcile`, both statuses also owe every rule whose missed cycle would be returned, provided some date the mark could carry would return its row as of the computation date (a US mark cancelled three years after filing owes no Section 8 or 9 date yet). A Mexican national mark registered before 10 August 2018 is never asked for its grant date for the third-year declaration, which covers later grants only; a Madrid designation still is, since its statement of grant can come years after its international registration. Before, such a mark with no `registration_date` or `grant_date` was reported as `supported: true`, and the rules it could not compute were not named. Now, when it owes such a rule and no rule can be computed, it reads `supported: false` with `reason: "requires_office_date"` on `POST /v1/deadlines/compute` and in `derived.deadlines`, and it is listed in `unsupported_marks` on `GET /v1/deadlines` and portfolio deadlines, as a registered mark with the same missing date already was. For example, the compute item `{ "jurisdiction": "US", "status": "expired", "filing_date": "2010-01-04" }` returns `supported: false`. With `"status": "cancelled"` it stays `supported: true` with no deadlines, because the US models no restoration; add `"include_missed": true` and it returns `supported: false`, since its missed Section 8 and Section 9 cycles need the registration date. When some rule can still be computed, the mark stays supported and the owed rules it cannot compute are listed in `unsupported_rules` (and the mark in `unsupported_marks`). For example, a Philippine national mark with status `expired`, a filing date and no registration date lists `ph_renewal`, `ph_declaration_of_use_renewal` and `ph_declaration_of_use_mid_renewal` (and, with `include_missed: true`, `ph_declaration_of_use_5yr`, whose missed row would be returned). Send the registration date and those rules are computed; an office-stated expiry date anchors only the renewal.

* **A mark whose status ends every obligation is never declined.** An abandoned Australian direct mark filed before 1996, a withdrawn Singapore direct mark filed before 15 January 1999, and a refused Madrid designation with no international registration date read `supported: false` with `reason: "requires_office_date"` on compute and in `derived`, while `unsupported_marks` left them out. They now read `supported: true` with no deadlines, like other abandoned, withdrawn, surrendered, invalidated or refused marks in a jurisdiction Signa models. A mark in a jurisdiction Signa does not model still reads `unsupported_jurisdiction`, and a refused WIPO international registration record keeps its renewal deadline, because a provisional refusal does not end the international registration.

* **`POST /v1/reconcile` names declined rules.** With `verdict_detail: true`, `computed` now carries `unsupported_rules`, in the same shape as on `POST /v1/deadlines/compute`: the rules that apply to the record but were declined because it lacks the date the statute anchors them on. For example, a Philippine Madrid designation with no statement of grant of protection lists `ph_declaration_of_use_5yr_madrid` with `missing_field: "protection_grant_date"`. Before, `computed` said `supported: true` with no trace of the declined rule, and a docketed deadline for it could not be told apart from one with no matching occurrence. The field is additive and is `[]` when nothing was declined. See [Reconcile](https://docs.signa.so/api-reference/reference/reconcile.md).

No served date moves: the first two changes decide only whether a mark is reported as supported and which rules it names as declined. `ruleset_version` on `derived` changes once, so trademark detail and portfolio ETags change once. No watch alerts or webhooks fire.

## October 4, 2026: portfolio events include status corrections

When Signa corrects a status it published for a mark in one of your portfolios (for example a renewed mark shown as expired, or office records that reached Signa late and show the mark lapsing or a dead mark returning), you now receive a direct `trademark.corrected` event. No configuration change is needed:

* **Webhooks.** Every endpoint subscribed to `trademark.status_changed` also receives `trademark.corrected`. The envelope `type` is `trademark.corrected`, so you can tell a correction from news. `trademark.corrected` is also accepted in `enabled_events` on its own (corrections only). Your stored `enabled_events` does not change.
* **Events feed.** `GET /v1/events` lists `trademark.corrected` events by default, and, as on webhooks, `event_type=trademark.status_changed` includes them. `event_type=trademark.corrected` returns corrections only.
* **Payload.** `data` is the `trademark.status_changed` body plus `correction: { kind }`, where `kind` is `status` or `late_office_record`. See [`trademark.corrected`](https://docs.signa.so/guides/monitoring/webhooks#payload-shape).
* **Watches are unchanged.** A watch still receives `trademark.corrected` only when its `trigger_events` lists it.

The SDK adds `TrademarkCorrectedData`, `TrademarkCorrection` and `TrademarkCorrectedEvent`, and `'trademark.corrected'` to `WebhookEventType`. A receiver with an exhaustive switch on the envelope `type` sees a new value.

## October 4, 2026: firm counts follow the firm of record

Firm counts now count a filing under its firm of record. `trademark_count`, `active_count` and the other trademark counts on `/v1/firms` and `/v1/firms/{id}` count a filing for the firm named on it, else for the attorney's firm. The `firm_id` filter and `/v1/firms/{id}/trademarks` use the same rule, but only on search documents built since it was added. Until the next full search rebuild, a firm's trademark list can include filings counted for another firm and miss some counted for it, so its total can differ from `trademark_count`. `attorney_count` still counts the firm's own attorneys: a firm whose attorneys file only under other firms of record keeps its `attorney_count`, with trademark counts of 0. The new counts appear after the next nightly stats run.

## October 4, 2026: a linked owner's ticker and LEI come from its entity

`GET /v1/owners/{id}` now serves a linked owner's company facts from its entity, the way `GET /v1/entities/{id}` returns them. Before, owner detail combined only the members' company matches. An owner could show another LEI, no ticker or `publicly_traded: false` while its entity showed the entity's own LEI, a listing ticker and `publicly_traded: true`.

* **`publicly_traded` and `has_lei`** equal the entity's. `publicly_traded` is also `true` when the entity is itself listed or is a subsidiary of a listed company.
* **`ticker`** is the owner's own first SEC ticker. When it has none, it is the entity's `ticker`, which can be a ticker inherited from a listed parent.
* **`lei`** is the owner's own first GLEIF LEI. When it has none, it is the entity's `lei`: the entity's own LEI when set, else the first member LEI.
* **`companies[]`** lists the same matches, the owner's own first.
* When the entity was merged into another, the facts are the surviving entity's. When the entity is no longer served (`GET /v1/entities/{id}` returns 404), the owner reports only its own matches and `companies_source` is `direct`.
* `GET /v1/owners/{id}/related` walks the one `lei` the owner's detail shows: its own first LEI, else its entity's. An owner with several LEIs of its own used to walk all of them, and an owner with none walked nothing.
* Owners not linked to an entity are unchanged. The MCP `get_owner` and `get_owner_related` tools return the same values. See [Retrieve Owner](https://docs.signa.so/api-reference/parties/retrieve-owner.md).

Owner, attorney, firm and entity detail ETags change once. An owner's ETag now also changes when its company facts or `companies[]` change, for example when its entity's listing changes. No fields are added or removed, and no webhooks or watch alerts fire.

## October 4, 2026: status corrections arrive as `trademark.corrected`; status basis relabels raise no alert

`trademark.corrected`, an opt-in watch trigger event, used to fire only when a retracted record reappeared. It now also carries status corrections, so watches that did not opt in are not alerted about old news:

* **A status Signa corrects.** When Signa fixes a status it published wrongly, for example a mark shown as expired that the office records as renewed, the status change arrives as `trademark.corrected` instead of `trademark.status_changed` or `trademark.updated`.
* **Late office records.** When office records reach Signa late and show a mark lapsing, or a dead mark returning, the change arrives as `trademark.corrected` too.
* **Other changes in a correction raise no alert.** Any other change Signa stores while correcting statuses or catching up on late office records, including marks Signa first learns of from late records, updates the mark but raises no alert.
* **Status basis relabels raise no alert.** A change that only relabels where a status comes from (`status.basis`, for example `office` to `computed`) no longer raises a `trademark.updated` alert. The mark shows the new basis.
* **MCP.** The `signa_alerts_list` tool's `event_type` filter now accepts `trademark.retracted` and `trademark.corrected`, like `GET /v1/alerts`.

To receive corrections, add `trademark.corrected` to a watch's [`query.trigger_events`](https://docs.signa.so/guides/monitoring/watches#trigger_events). For portfolio marks, corrections now also arrive as direct `trademark.corrected` events (see the entry above). No request or response fields change.

## October 4, 2026: `derived.term` and no projected term past a lapsed expiry

When the expiry an office states had passed its grace period (and restoration window) with no renewal on record, `derived.expiry_date` showed the next cycle ten years on, with a renewal "due" then. A UK mark surrendered in 2014 with a 2024 expiry showed 2034; a French mark whose 2026-02-05 term ended in August showed 2036. The term on record now ends where the office's record ends it, and a new block says where it stands.

* **New `derived.term`** on trademark detail, batch lookup, search results, portfolio rows, watch previews and the MCP `get_trademark` tool: `state` (`in_term`, `in_grace`, `in_restoration`, `renewal_not_on_record` or `not_applicable`), `grace_expiry_date`, `restoration_expiry_date` and `renewed` (whether a renewal on record began the term). See [The term on record](https://docs.signa.so/guides/deadline-rules.md#the-term-on-record).
* **`derived.expiry_date`** stays at the office-stated term end once it has passed, while the mark can be renewed late or restored and, with `term.state: "renewal_not_on_record"`, after that. While a restoration window is open it is that date (it was `null`). A renewal the office recorded (including one recorded after the grace period), or for a Madrid designation (a national office's record, IP Australia's for example, or WIPO's own) an expiry WIPO published for the international registration in a later term, moves it forward to the next term, which reads `in_term`. The rows of that designation keep the office's own dates as their trigger.
* **No renewal is computed past such a term end.** `derived.deadlines.items` and `derived.next_deadline` drop the projected later cycles and the restoration of a cycle that never began. The same applies to `GET /v1/deadlines`, portfolio deadlines and the iCal feed, `POST /v1/deadlines/compute` and `POST /v1/reconcile`, whose computed `expiry_date` is now the lapsed term end.
* An office that reports a mark expired the day after its term ends (the UK office does) reads `in_grace`, then `in_restoration`, until those windows close.
* `status` and the top-level `expiry_date` are the office's and do not change. A schedule with no office-stated expiry (every USPTO record) keeps the statutory term end.

`ruleset_version` on `derived` changes once, and every detail and portfolio ETag changes once (the block has a new key). No watch alerts or webhooks fire.

## October 3, 2026: China Madrid opposition windows from 2027; Philippine and Mexican office closures

* **China, Madrid designations: two months from the WIPO publication from 2027.** CNIPA published draft transitional measures for the revised Trademark Law for comment on 28 September 2026. They are not in force yet. Under the draft, an international registration whose WIPO Gazette date is on or after 1 January 2027 gets the revised two-month opposition period counted from the publication. The draft does not say whether the current start, the first day of the month after the Gazette, survives, so `cn_opposition_madrid` serves those registrations the window both readings allow: it opens on the first day of the following month and closes two months after the Gazette date. For a Gazette date of 15 January 2027 the window is now 1 February to 15 March 2027, where it was 1 February to 30 April. Registrations published before 2027 keep three months from the first day of the following month. The shorter window is served first because a close that turns out early costs an opponent nothing, while one that turns out late costs the right. If the final measures differ, the rule follows them. On `GET /v1/opposition-rules` the rule now has `window_change_date: "2027-01-01"`, `window_months: 2`, `window_starts_on: "day_after"` and the new field `window_opens_on: "first_of_following_month"` (`null` on every other rule), and the earlier era moved to `window_months_before: 3` and `window_starts_on_before: "first_of_following_month"`. The draft measures and the consultation notice are among its sources. See [opposition rules](https://docs.signa.so/api-reference/reference/opposition-rules.md).
* **Philippines: IPOPHL closed on 16 to 18 November 2026.** Proclamation No. 1447 declares those days non-working in Metro Manila, where IPOPHL has its office. A Philippine opposition window that closed on 14 to 18 November 2026 now closes on Thursday 19 November 2026, with `close_adjustment: "moved"`.
* **Mexico: four IMPI suspensions of time limits.** IMPI suspended all its time limits on 8 and 9 August 2024, 18 May 2026, 23 September 2026 and 25 September 2026, by notices in the Diario Oficial de la Federación. Mexican opposition windows and renewal deadlines that fell on those days now move to the next working day. `GET /v1/deadline-rules` lists the four notices among the Mexican sources.

The Philippine and Mexican corrections only move dates later, so nobody who acted on the earlier date acted late. The Chinese change moves closes earlier, but only for Gazette dates from 1 January 2027, so no affected window has opened yet. `ruleset_version` on `derived` changes once, so trademark detail and portfolio ETags change once. Opposition windows stored in search for Philippine and Mexican marks are recomputed after this release, so for a short time search results can still show the earlier close. No watch alerts or webhooks fire.

## October 3, 2026: proceedings party owner and entity filters return linked proceedings

Proceeding parties are now linked to owners. A party is linked to an existing owner when the marks in the dispute show which owner it is and the names match with the same company suffix. This covers USPTO, CIPO and IP Australia proceedings, and new proceedings are linked daily.

* **`party_owner_id`, `party_entity_id` and `entity_id` on `GET /v1/proceedings` return results.** They match proceedings where a party is linked to the owner, or to any member owner of the entity. Combined with `q` or `party_role`, every filter still has to match the same party.
* **Parties without a link are not returned by these filters.** They include most Canadian and Australian opponents (they are matched only against the owner of the mark under challenge), a name that matches only without its company suffix, and a recorded name that lists several parties. Search with `q` to match by name or for complete coverage. See [Search Proceedings](https://docs.signa.so/api-reference/proceedings/search-proceedings.md).
* **`parties[].owner_id`** on proceedings now carries the linked owner, reported as its current owner record (the surviving owner if it has since been merged), and stays `null` for a party without a link.
* **Owner and entity analytics count challenger disputes.** `litigation.as_challenger` on [owner](https://docs.signa.so/api-reference/parties/owner-analytics.md) and [entity](https://docs.signa.so/api-reference/parties/entity-analytics.md) analytics counts the disputes where a linked party is the opponent or petitioner, so it is no longer always `0`. `proceedings_total` changes with it.

A proceeding's detail ETag changes whenever a party's link is added, moved or cleared: first for the proceedings linked with this release, then as the daily sweep updates links. The sweep can later move a link to another owner or clear it, so `parties[].owner_id` is not fixed once set. No webhooks or watch alerts fire for link changes.

## October 2, 2026: USPTO post-registration office actions get their response deadline

When the USPTO mails a post-registration office action on a Section 8, Section 71 or combined Section 8 and 9 filing, the mark now carries a deadline to respond. Before, the filing closed its cycle and nothing replaced it, so a registration that will be cancelled without a response within weeks showed its next renewal, years away, as `derived.next_deadline`.

* **New row type `post_registration_oa_response`** (`rule_id: "uspto_post_registration_oa_response"`), due six months after the action or at the end of the filing period the action examined, whichever is later, moved off weekends and federal holidays. It appears in `derived.deadlines` and `derived.next_deadline` on trademark detail, batch lookup, search results and portfolio rows, on `GET /v1/deadlines` (new `type=post_registration_oa_response` filter value), on portfolio deadlines and the iCal feed, on `POST /v1/deadlines/compute` for stored trademark ids, and on the MCP `get_trademark` tool. `occurrence_key` is `uspto_post_registration_oa_response:<action date>:<action code>`.
* **New optional `note` field on deadline rows.** Present only when the record adds something the dates do not say: the action a response row answers (`PRA8 2026-04-19`), a "no response required" notice, or a prosecution history that could not be read.
* The row closes only on explicit evidence: a response naming the action's section, the office's acceptance of the examined filing, or a cancellation of it. A notice of incomplete response reopens every row it could concern. If the response date passes with nothing on record, the row stays as `missed`; the examined declaration or renewal row is not changed. Whether the registration was lost is read from its USPTO status.
* When the record cannot settle the date (the examined filing is not on record, a notice of incomplete response has no action on record, or the action predates 2015), the row stays open on the earliest date Signa can defend, with a `note` to check TSDR. A served date never moves later.
* An action still open more than a year after its response date, on a registration whose current status is registered, is treated as resolved by the USPTO: no row.
* On `POST /v1/deadlines/compute` for a stored id, `prosecution_deadlines` now dates this rule from the stored history, and `support` reports it as `computed` instead of `facts_required`. `due_date` is six months from the action, as on caller-supplied facts, and `effective_due_date` is the date the row serves. A closed one carries `closed_by: "response"`, or the new value `closed_by: "acceptance"` when the office accepted the examined filing, with `closed_by_fact_id: null` (the closing entry is named in `notes`).
* `GET /v1/deadline-rules` and the MCP `get_deadline_rules` tool list the rule. See [USPTO post-registration office actions](https://docs.signa.so/guides/deadline-rules.md#uspto-post-registration-office-actions).

`ruleset_version` on `derived` changes once, so trademark detail and portfolio ETags change once. SDK clients with an exhaustive switch on `type` see a new value. No watch alerts or webhooks fire.

## October 1, 2026: records from offices that are not live yet are no longer served

Signa ingests some offices before they go live. `GET /v1/offices` already left those offices out, but their trademark records were still returned. They are now absent everywhere a customer reads trademarks, until the office goes live and appears on `GET /v1/offices`:

* **Search and list.** `GET` and `POST /v1/trademarks`, in both grouped and expanded views, leave these records out of rows, `pagination.total_count` and aggregations. The same applies to `GET /v1/trademarks/suggest`, screening, image search, the owner, attorney, firm and entity trademark lists, and the MCP search and list tools. An office number or a `tm_` id in `q` does not return such a record.
* **Lookups by id.** `GET /v1/trademarks/{id}` and its `events`, `proceedings`, `assignments`, `documents`, `citations` and `cited-by` sub-resources return `404`. `POST /v1/trademarks/batch` lists the id in `not_found`. `GET /v1/deadlines?trademark_id=`, `POST /v1/deadlines/compute`, `POST /v1/compare` and screening by `trademark_id` treat the record as not found. The MCP `get_trademark`, `batch_get_trademarks`, `get_trademark_events` and `get_trademark_proceedings` tools answer the same way.
* **International registrations stay.** A WIPO international registration is a record of a live office, so it is still returned with all of its designations, including designations of a country whose national office is not live yet. The national office's own record of that designation is not returned on its own. Image search matches images by their own record's office, so an international registration whose only image sits on such a national record is not found by image search.
* **Unchanged.** `offices=` and `office_code` filters naming an office that is not live still return `400` with `unknown_office_code`. `jurisdictions=` with such a country still returns the rights of live offices that protect it, such as EU trade marks and Madrid designations. Marks already in a portfolio stay in it, are listed, and keep their portfolio deadlines.

Responses for records of live offices are unchanged, and so are their ETags. No webhooks or watch alerts fire.

## October 1, 2026: proceedings `q` matches the party you filter by; party owner filters return no results (superseded October 3, 2026)

* **Behavior change.** On `GET /v1/proceedings`, `q` and `party_role` now have to match the same party. `q=Acme&party_role=opponent` returns proceedings where a party named Acme is the opponent. Before, it also returned proceedings where Acme was the respondent and anyone else was an opponent, so the list could include cases from the wrong side. `q` combined with `party_owner_id` or `party_entity_id` follows the same rule. `q` alone and `party_role` alone are unchanged.
* **`party_owner_id`, `party_entity_id` and `entity_id` return no results today.** Proceeding parties keep the name the office recorded but are not yet linked to owner or entity records, so these filters match nothing. They stay accepted. Search by name with `q`, which matches a case-insensitive substring of the recorded party name; it does not add related companies or subsidiaries. [Search Proceedings](https://docs.signa.so/api-reference/proceedings/search-proceedings.md) and the [opposition guide](https://docs.signa.so/guides/opposition-dispute-intelligence.md) now use `q` in their examples. *Superseded October 3, 2026:* these filters now return proceedings whose parties are linked; see that entry.
* The `party_role` aggregation still counts every role on the matching proceedings, so `q=Acme&party_role=respondent` can show an `opponent` bucket: it counts the cases that had an opponent.

## October 1, 2026: Australia, Norway, Sweden and Singapore show as covered on `/v1/jurisdictions`

`GET /v1/jurisdictions`, `GET /v1/jurisdictions/{code}` and the MCP `list_jurisdictions` and `get_jurisdiction` tools returned `office_code: null` and `office_status: "not_covered"` for `AU`, `NO`, `SE` and `SG`, although their offices are live on `GET /v1/offices`. They now return `office_status: "live"` with `office_code` `AU`, `NO`, `SE` and `SG`. `ES` now returns `office_status: "roadmap"`, like the other offices that are planned. No other jurisdiction changed.

## October 1, 2026: USPTO declarations and renewals on record close their deadlines

Deadlines for USPTO marks now read the Section 8, Section 15, Section 71 and Section 8 and 9 filings in the mark's prosecution history. Before, a declaration the USPTO had already accepted was still served as `in_grace` and then `missed`, a US designation under the Madrid Protocol with both Section 71 affidavits accepted showed both as missed, and a renewal filed on time showed as `in_grace`.

* A filing made inside its window (from the day the window opens to the end of the grace period) closes that cycle on `derived.deadlines` and `derived.next_deadline` on trademark detail, batch lookup, search results and portfolio rows, on `GET /v1/deadlines` (including `urgency=missed`), on `POST /v1/deadlines/compute` for stored trademark ids, on portfolio deadlines and the portfolio iCal feed, on `POST /v1/reconcile` and on the MCP `get_trademark` tool.
* The combined Section 8 and 9 renewal closes only when both parts are on record.
* `derived.expiry_date` and the term end `POST /v1/reconcile` compares against move to the next term only when the USPTO accepts the renewal, not when it is filed.
* A Section 15 declaration whose window has passed is no longer listed as missed (`urgency=missed`, `include_missed`). Section 15 is optional and can be filed after any later five-year period of continuous use, so nothing is lost. An open Section 15 window is still served.
* A US designation ingested from WIPO's records is not covered yet. See [USPTO filings already on record](https://docs.signa.so/guides/deadline-rules.md#uspto-filings-already-on-record).

`ruleset_version` on `derived` changes once, so trademark detail and portfolio ETags change once. No watch alerts or webhooks fire.

## October 1, 2026: offices declare proceedings support; proceedings come only from those offices

Every office on `GET /v1/offices`, `GET /v1/offices/{code}` and the MCP `list_offices` and `get_office` tools now carries `capabilities.proceedings`: `available` when Signa ingests and serves the office's oppositions, cancellations and appeals, `in_progress` when a pipeline is being built, and `not_available` otherwise. See [List Offices](https://docs.signa.so/api-reference/reference/list-offices.md).

* `GET /v1/proceedings` and the MCP `list_proceedings` tool with an `office_code` for a live office whose `capabilities.proceedings` is not `available` return `422 capability_not_available`, with `capability`, `capability_state` and `office_code` in the error. No query runs and no credits are charged. Before, such a request could time out, or return an empty list that looked like "this office has no proceedings".
* A `proceeding_type` that no served office records (today `court_action`), or that the requested `office_code` does not record, returns an empty page and empty `aggregations` buckets straight away, charged as a normal page. [Search Proceedings](https://docs.signa.so/api-reference/proceedings/search-proceedings.md) lists the types each office records.
* The proceedings endpoints return proceedings only for offices whose `capabilities.proceedings` is `available`: the `GET /v1/proceedings` list, `GET /v1/proceedings/{id}` (a proceeding from any other office is `404`), `GET /v1/trademarks/{id}/proceedings`, the `proceedings_count` on a trademark's detail, and the MCP `get_proceeding`, `list_proceedings` and `get_trademark_proceedings` tools. Two signals catch up after this release:
  * `aggregations` counts on `GET /v1/proceedings` may still include proceedings held for offices that are not live yet, until a follow-up backfill completes. The listed proceedings never include them.
  * Search and screening signals that come from the search index (the `has_proceedings` filter, the ranking boost for marks with proceedings, and screening's prior-proceedings and litigious-owner signals) apply the same rule to each mark when it is next indexed. Until then, an international registration whose only proceedings sit on a designation in an office that is not live yet can still match `has_proceedings=true`.
* The `GET /v1/offices` and `GET /v1/offices/{code}` ETags change once, because the body gained a field.

## October 1, 2026: any API key can connect to MCP

The MCP server at `/mcp` (and `/mcp2`) now accepts every valid API key and OAuth token, whatever its scopes. Before, a key needed one of `read`, `search`, `trademarks:read`, `billing:read`, `portfolios:manage` or `admin` to connect at all, so a key with only `events:read` got `403` and could not call `signa_events_list` or `get_account`.

* `initialize` and `tools/list` work for every session. `tools/list` returns every tool, including ones the session can't call.
* `get_account` and `get_credit_pricing` work for every key.
* A tool the key lacks a scope for returns the `forbidden` error REST returns (status `403`, naming the scope) with the call's `request_id`, and is not charged.
* An invalid, revoked or expired credential still gets `401`. Rate limits and the batch cap are unchanged.

See [MCP scopes](https://docs.signa.so/ai/mcp-tools.md#scopes).

## October 1, 2026: deadline corrections for lapsed marks and Swiss, Polish and Norwegian opposition windows

* **A lapsed mark is no longer served a renewal ten years out beside its restoration window.** Some offices keep a mark at `registered` for months after its stated term ended. When the office states a term end that has passed and a restoration window is on the schedule, `derived.deadlines` on trademark detail, `GET /v1/deadlines`, `POST /v1/deadlines/compute` and `POST /v1/reconcile` now serve the lapsed renewal while it is in grace and the restoration window, but no later renewal cycle. After the grace period, and while the restoration window is open, `derived.expiry_date` is null. The office's `status` and `expiry_date` are unchanged. A mark the office has renewed, or whose stated term end is still ahead, is not affected. See [Restoration periods](https://docs.signa.so/guides/deadline-rules.md#eu-uk-singapore-iceland-japan-and-india-restoration-periods).

* **Switzerland: no opposition window before the Trade Mark Protection Act.** Opposition in Switzerland exists since the Act commenced on 1 April 1993 (MSchG art. 79). A Swiss publication dated before that day, national or Madrid, now has no window instead of a three-month one. A national mark filed before that day has none either, whatever its publication date (MSchG art. 76(2)(c)); this needs the filing date, which trademark detail uses and which `POST /v1/oppositions/compute` reads from `filing_date`.

* **Poland: no opposition window before pre-grant opposition existed.** Pre-grant opposition in Poland began on 15 April 2016 (Dz.U. 2015 poz. 1615 art. 3). A national publication dated before that day, or a national application filed before it (art. 2 ust. 1 of the same Act), now has no window instead of a three-month one. Madrid designations of Poland were already gated at that date.

* **Norway: two months for registrations published before 1 July 2010.** The repealed 1961 Trademarks Act § 21 gave two months from publication, so the window for a registration published before 1 July 2010 now closes one month earlier and cites that Act. Every window in that group closed years ago.

* **New reason `before_commencement` on `POST /v1/oppositions/compute`.** When an opposition rule exists for the office but the item was published, or filed, before the date the rule applies from, the item now returns `reason: "before_commencement"` with the rule's `rule_id` and `effective_from`. Before, it read `unsupported_office`, which suggested no rule existed for the office. The same applies to rules that already had a start date, such as Vietnam's and Mexico's. `unsupported_office` now means only that no rule exists for the office and route. Trademark detail is unchanged: `derived.opposition_window.reason` keeps `unsupported_office` for these records.

`GET /v1/opposition-rules` shows the new `applicable_from_date` and `effective_from` values on `ch_opposition`, `ch_opposition_madrid` and `pl_opposition`, the era fields on `no_opposition`, and the added sources. `ruleset_version` on `derived` changes once. Trademark detail and portfolio ETags change once. Opposition windows stored in search for Swiss, Polish and Norwegian marks are recomputed after this release, so for a short time search results for those marks can still show the earlier window. No watch alerts or webhooks fire.

## October 1, 2026: MCP search tools take date ranges and field projection

`search_trademarks` and `list_trademarks` now accept every date filter that `GET /v1/trademarks` has, in the `POST` range shape: `filing_date: {"gte": "2024-01-01", "lt": "2025-01-01"}`, and likewise `registration_date`, `expiry_date`, `renewal_due_date`, `publication_date`, `status_effective_date`, `priority_date`, `first_use_anywhere_date`, `first_use_in_commerce_date`, `termination_date`, `updated_at` and `family_updated_at`. They also accept `fields` and `include`, which project rows as on REST; `id` and `object` are always kept. Validation is REST's: conflicting bounds or an unknown field return `validation_error`, and the call is not charged. `include: ["match_details"]` on `list_trademarks` adds `match.details`, as it does on `GET /v1/trademarks`. See [MCP tools](https://docs.signa.so/ai/mcp-tools.md).

One REST change comes with this. On `POST /v1/trademarks`, a date range with no bound, such as `"filters": {"filing_date": {}}`, is now ignored like an absent range. It used to reach the search as an empty filter, which forced one row per record (an `expanded_fallback` warning) or returned `400` when `international_registrations` was `grouped`. A cursor from such a request now returns `cursor_invalid`; restart from the first page.

## October 1, 2026: documentation corrected against the API

No API behavior changed apart from the `harmonised_only` parameter description. These pages said more than the API does:

* **Freshness.** Trademark detail reads were described as strongly consistent. They carry `Cache-Control: private, max-age=300`, so a cached detail read can trail a write by up to five minutes, and they may be served from a database read replica. See [Data Freshness](https://docs.signa.so/guides/data-freshness.md#consistency-model).
* **Search depth.** Not every search stops at 10,000 rows: a grouped request without `q` pages by key, with no fixed depth. A position walk can repeat or skip a row when records change between pages, so deduplicate on `id`. See [Search depth](https://docs.signa.so/api-reference/pagination.md#search-depth).
* **Harmonised terms.** `is_harmonised: true` marks a term from a harmonised list (the EUIPO Harmonised Database, or a TM5-harmonised USPTO ID Manual entry). It does not mean every TM5 office accepts the wording; read `accepted_offices`. The `harmonised_only` description on `GET /v1/goods-services` and the MCP tool now says the same.
* **Entity portfolios.** `GET /v1/entities/{id}/trademarks` returns the marks of owner records linked to the entity, and `include_family=true` adds GLEIF-reported subsidiaries. Neither is a complete-portfolio guarantee.
* **Trademark events.** USPTO timelines include the office's e-mail notifications as their own `office_action` events; EUIPO timelines are publication events, not a lifecycle history. See [Trademark Events](https://docs.signa.so/api-reference/trademarks/trademark-events.md).
* **Restoration rules.** The EU's restitutio in integrum rule (`eu_restitutio`) is one of the restoration rules in `GET /v1/deadline-rules`; the reference listed only the others.
* Guides no longer state jurisdiction counts for deadline and opposition rules. `GET /v1/deadline-rules` and `GET /v1/opposition-rules` are the current lists.

## October 1, 2026: a TTAB interlocutory order is no longer a proceeding's decision

On US proceedings, an interlocutory order of the Trademark Trial and Appeal Board, such as a ruling on a motion to compel, suspend or amend, no longer counts as the proceeding's decision: it sets no `decision_date` or `decision_outcome` and does not change `status` or `outcome`. Before, such an order could be stored as the decision, so a proceeding that was still running showed a `decision_date` and a `duration_days`. Only orders recognised as interlocutory change. Every other TTAB ruling, including a summary judgment that sustains an opposition and an order that vacates a decision, is handled as before. Affected proceedings are corrected in place and their ETags change once.

## October 1, 2026: owner and entity names show accented characters

Some owner names came from office feeds with HTML character references still in them, so `&#352;KODA INVESTMENT a.s.` showed instead of `ŠKODA INVESTMENT a.s.`. The API, MCP tools and webhooks now decode these references when they return a name:

* `owners[].name` on trademark rows and trademark detail.
* `name`, `name_original_script` and `aliases[].name` on owners, and the names on `/v1/owners/{id}/related`.
* Entity names, member names and family names.
* Attorney client names, proceeding `parties[].name` and assignment `parties[].name`.
* `trademark.owner_name` on alerts and `alert.created` webhooks, and `subject.name` on party analytics.

`canonical_name` is unchanged. `trademark.status_changed` event payloads keep `owner_name` as it was recorded. Stored records are not rewritten, so no webhooks or watch alerts fire.

`owner_name` filters, on search and on watches, find a record by its decoded name as well as by the name the office sent, including names where only some characters were escaped. Names escaped with other named references, such as `&apos;` or `&nbsp;`, may still need the name as the office sent it. `owner_id` stays the exact way to filter by owner.

ETags on trademark, portfolio, owner, attorney, firm, entity, proceeding and assignment detail change once, so a cached body revalidates to the decoded name.

## October 1, 2026: MCP errors match REST; proceedings cursors are bound to their query

MCP tool errors now carry the same JSON error body as the REST API, plus the call's `request_id`. See [MCP errors](https://docs.signa.so/ai/mcp-tools.md#errors).

* A database statement timeout is `timeout` (504, retryable). It used to read "invalid or malformed ID".
* A lost database connection, failover, or overloaded search cluster is `service_unavailable` (503, retryable).
* Any other server failure is `internal_error` with a `reference_id`. Database and search-engine messages no longer appear in tool results.
* Invalid arguments and unknown tool names return `validation_error` and `not_found` bodies instead of plain text. `errors[]` names each field.
* A `preview_watch` that runs out of time returns `preview_timeout` (504), as `POST /v1/watches/preview` does.
* `get_account` works for any key that can connect to MCP, not only keys with `billing:read`. Without `billing:read` it returns identity with `plan: null`.
* `list_trademarks` and `list_proceedings` with only blank filters (`""`, `"  "`, `[]`) return `validation_error` without running or charging.
* `search_trademarks`, `list_trademarks` and the party trademark tools reject an unknown `status`, `status_stage` or jurisdiction code instead of matching nothing.
* A JSON-RPC batch of more than 10 messages in one POST is rejected with JSON-RPC error `-32600`.
* A body that is not valid JSON or not a JSON-RPC message, or a request with malformed method parameters, returns a fixed JSON-RPC error with `error.data.request_id`, instead of the parser's error text.

`GET /v1/proceedings` and `list_proceedings` cursors now carry the filters and sort that produced them. Reusing a cursor with a different filter or `sort` returns `cursor_invalid`, and an empty `cursor` returns `cursor_expired`. A cursor issued before this change is rejected with `cursor_invalid`; start the list again.

## October 1, 2026: Benelux, Malta and OAPI member states resolve on `/v1/jurisdictions`

`GET /v1/jurisdictions/NL` and the MCP `get_jurisdiction` tool returned `404` for member states that `jurisdictions=` already accepts as a search filter. `NL`, `BE`, `LU`, `MT` and the 17 OAPI member states now resolve and appear in `GET /v1/jurisdictions`. Each has `scope_type: "national"`, `madrid_member: false` and `office_status: "not_covered"`: BOIP (`BX`) and OAPI (`OA`) are the register and the Madrid party for their members, and Signa does not connect to Malta's register yet. See [Retrieve Jurisdiction](https://docs.signa.so/api-reference/reference/retrieve-jurisdiction.md) for how `jurisdictions=NL` and `jurisdictions=BX` differ on search.

## October 1, 2026: opposition, proceeding status and owner corrections

Three data corrections. Records ingested from now on carry the corrected values; stored records are corrected by a backfill that follows this release. The backfill fires no watch alerts. A small number of records whose stored data we cannot fully verify may produce one update event on their next update from the office.

* **A mark in its opposition window is no longer reported as opposed.** USPTO marks with status code `686` (published for opposition), EUIPO marks with `APPLICATION_PUBLISHED` or `START_OF_OPPOSITION_PERIOD`, and INPI France marks in `opposition_period` no longer carry `opposition_pending` in `status.challenges`. An opposition that was actually filed still does (USPTO `774` and `801`, EUIPO `OPPOSITION_PENDING`). A USPTO request for an extension of time to oppose (`802`) is now `other_pending`. `status.stage` is unchanged: `pending_opposition` means published for opposition or opposed, and the stage alone does not tell you which. Check `status.challenges`.
* **Proceeding `status` is no longer inferred where the office did not report it.** For EUIPO, CIPO and IP Australia, `status` is now an exact mapping of the office's own label, still returned unchanged in `status_raw`.
  * EUIPO: `CLOSED` is `other`, not `decided_rejected`: the office reports that the case is closed but not who prevailed. Cases still running (`RECEIVED`, `ADMISSIBILITY_CHECK`, `COOLING_OFF_PERIOD`, and pending Board of Appeal or General Court cases) are `pending`. `decision_date` stays null for EUIPO proceedings.
  * CIPO: `Active`, `Pending`, `Awaiting Decision` and the appeal states are `pending`; `Deemed Abandoned` is `decided_granted`; `Closed` is `other`. `proceeding_number` is the office's per-record sequence identifier (1, 2, ...), not a Board docket number, and the API reference now says so.
  * IP Australia: `Consent court order` is `settled`, and `Extension of time granted` is `other` rather than `decided_granted`.
  * `status.challenges` follows: a CIPO or IP Australia mark whose opposition or cancellation is now `pending` carries the matching challenge, and one whose proceeding is no longer `pending` drops it.
* **A live mark no longer loses its owners.** An office update that arrived without owner data used to retire the current owners of a registered or pending mark, so it was served with no owner. A live mark now keeps its current owners in that case, and marks already affected get their owners back where our stored office data shows who the current owner is.

## September 30, 2026: TTAB filing dates backfilled

`filed_date` is now set on about 99% of US oppositions and cancellations, historical dockets included, loaded from the USPTO TTAB bulk feed. `filed_date_gte`, `filed_date_lt`, `sort=filed_date` and the `filed_year` aggregation now count those TTAB dockets. Ex parte appeals do not carry a filing date yet, and `decision_date` is still set on only a small share of US rows; the proceedings pages say so.

## September 30, 2026: party detail ETags change once

`/v1/attorneys/{id}`, `/v1/owners/{id}`, `/v1/firms/{id}` and
`/v1/entities/{id}` ETags change once. Stored attorney rows got their
`country_code` without a change to the row, so a client revalidating a cached
attorney body gets one `200` with the current `country_code`, then `304`s
again.

## September 30, 2026: a trademark number in `q` returns that record

Searching `/v1/trademarks` with a trademark number in `q` now returns the record the number names. Before, a US designation of an international registration (a 79-series serial such as `q=79412883`) returned no rows, and its US registration number returned the international registration's family row instead of the US record.

* A number in `q` returns one row per record, each shown as on its detail page, with an `identifier_lookup` warning in `search_meta.warnings`.
* A labelled international registration number, such as `IR 1939139`, returns one row for the international registration and its designations.
* A bare number returns the records it names. Records whose own number is the searched number come first, such as an international registration and any national record with that number. Designations that only carry the international registration number follow.
* A `tm_` ID of a designation returns the designation, not its family.
* Text searches are unchanged.
* A pagination cursor issued before this change for a number in `q` is rejected with `cursor_invalid`. Start the search again.

## September 30, 2026: USPTO assignments February to April 2026 complete

USPTO assignments recorded February to April 2026 are now complete (about
19,000 recordations reloaded). They are available on `/v1/assignments` and
`/v1/trademarks/{id}/assignments`. The USPTO's 6 to 10 week recordation lag
still applies to the most recent two months.

## September 30, 2026: USPTO attorneys of record are `representative`

USPTO attorneys of record now carry `role: "representative"` in
`attorneys[].role`, the value every other office uses for the attorney of
record. They were `correspondent`. Stored rows were relabelled in place. The
first time we re-observe each USPTO mark after the change, that mark emits one
`trademark.updated` webhook and watches see an attorney change; this happens
once per mark. If you filter USPTO attorneys by `correspondent`, filter by
`representative` instead.

## September 29, 2026: firms on Madrid families

* **Grouped `firm_id` search matches every designation's firm.** When one attorney appears on several designations of an international registration under different firms, a grouped search filtered by `firm_id` now matches the family for any of those firms, not only the first, as expanded search already did. Existing families pick this up when they are next re-indexed.
* **IR detail attorney order follows designation sequence.** On an international registration with several designations, `attorneys[]` in trademark detail is ordered by the designation's attorney sequence, not by designation office, so it matches the search index. Multi-designation IR details may list attorneys in a new order.
* **Detail `firm_name` uses the firm's display name.** When the filing gives no firm name of its own, `attorneys[].firm_name` in trademark detail is now the firm's display name (for example `Smith & Jones, L.L.P.`) instead of its upper-case matching key.

## September 29, 2026: the primary image is always an image

`primary_image_url` and `media[].is_primary` now only ever point at an image
file, never a document, audio or video file. A mark whose only media are
office documents (for example USPTO office actions) or sound, video or 3D files
has `primary_image_url: null` and no primary item; those files stay listed in
`media[]`. Previously such a mark could point `primary_image_url` at a PDF.
Trademark detail now lists the primary item first in `media[]`; the other
items keep their order. Trademark detail applies this now; search results and
portfolio rows pick it up as marks are re-indexed, and fully with the next
index rebuild. `has_media` is unchanged.

## September 29, 2026: one search request model: `q`, `similarity`, text filters and exclusions (breaking)

Search on `GET` and `POST /v1/trademarks`, the owner, attorney, firm and entity
`/trademarks` lists, similarity watches and the MCP `search_trademarks` tool
now share one request model. `q` always ranks, `similarity` picks the channels
it runs, the `mark_text_*` filters decide which marks qualify, and exclusions
leave marks out. The old parameters keep working until December 28, 2026. The
[migration guide](https://docs.signa.so/guides/migrating-search-parameters) maps each one.

**New parameters**

* **`similarity`** picks the channels `q` runs: `identical`, `fuzzy`,
  `embedded`, `phonetic` and `lookalike`. The default is
  `identical,fuzzy,embedded,lookalike`, and `identical` is always on. See
  [Similarity](https://docs.signa.so/api-reference/trademarks/search-trademarks.md#similarity).
* **Text filters** `mark_text_is`, `mark_text_word`, `mark_text_starts_with`,
  `mark_text_ends_with`, `mark_text_contains` and `mark_text_pattern` test the
  mark text. On `GET`, repeat the key for several values; commas are never
  split. On `POST`, send them under `filters.mark_text`. Alone they list marks
  in text order; with `q` they narrow the ranked results. See
  [Text filters](https://docs.signa.so/api-reference/trademarks/search-trademarks.md#text-filters).
* **Exclusions**: `mark_text_not_*` for every text filter, plus
  `owner_id_not`, `entity_id_not`, `entity_group_not` and
  `trademark_ids_not`. On `POST`, send them under `exclude`.
* **`allow_partial`** (`options.allow_partial` on `POST`) returns the rows
  found so far when a search times out, with `search_meta.complete: false`.
* **`include=match_details`** adds `match.details` with the matching methods
  that fired and the ranking factors applied.

**New response fields**

* **`match` on every hit** of a request with `q` or a text filter:
  `{ tier, via, terms }`. `tier` is `identical`, `near_identical`, `similar`
  or `related`, a retrieval category rather than an edit distance. `via`
  lists the channels that found the hit. `terms` lists the text filter values
  it satisfied. See [Match tiers](https://docs.signa.so/api-reference/trademarks/search-trademarks.md#match-tiers).
* **`search_meta`** gains `similarity_applied`, `order` (`relevance`,
  `mark_text_fit`, `filing_date` or your `sort`), `complete`,
  `incomplete_reason`, `identifier`, `ranking_version` and `deprecations`.

**Trademark numbers in `q`.** An application, registration or international
registration number pasted into `q` (`97134218`, `US 97134218`,
`EUTM 018123456`, `IR 1939139`, `TMA911339`, a `tm_` ID) is looked up in
addition to the text search. Its records lead the page under any `sort`,
labelled `via: ["identifier"]`, and `search_meta.identifier` shows how `q` was
read. A bare international registration number now returns the WIPO record.
See [Search Trademarks](https://docs.signa.so/api-reference/trademarks/search-trademarks.md#similarity).

**Behavior changes**

* **Default recall is wider.** A `q` without `similarity` now also finds
  marks with a different first letter, look-alike characters and prefix
  phrases. `embedded` finds the term as a word, a phrase or the start of a
  word in a longer mark; a term in the middle of a word is not matched yet. Abbreviation matching and owner-name matching are removed from
  search; use [Search Owners](https://docs.signa.so/api-reference/parties/search-owners.md) for owner
  names. `search_meta.ranking_version` is `v12`.
* **Every search cursor restarts.** Cursors issued before this release return
  `400 cursor_invalid`. Cursors are now bound to the translated request, so an
  old-style request and its new-style equivalent share cursors.
* **Scores survive `sort`.** With `q`, `relevance_score` and `match` are on
  every hit whatever the `sort`. Under a `sort` the score keeps the status
  factor and drops the length factor.
* **Text filters list in text order.** Without `q`, a text filter orders marks
  equal to the value first, then marks starting with it, then shorter marks,
  then newer filings. The old `match` modes returned the newest filings first.
* **A search that times out is a `503`.** A search that hits its time limit or
  loses a shard returns a retryable `503` unless `allow_partial=true`. Sorted
  searches too; they returned `400`. The `partial_results` warning is removed.
* **No quiet downgrades.** A query too expensive to run returns
  `400 validation_error` with `errors[].code: too_complex`, naming `q` or the
  text filter with the most values. It used to be retried with fewer
  strategies. The `strategies_reduced` warning is removed.
* **New limits.** `q` takes at most 22 words. `contains`, `ends_with` and
  wildcard-leading `pattern` values need 3 characters after folding, and one
  request takes at most 4 of them (`400 too_big`). Each such value takes one
  slot of the [search concurrency](https://docs.signa.so/api-reference/rate-limits.md#rate-limits)
  budget.
* **`mark_text_not_contains` ignores punctuation and spacing**, like the other
  text filters, and takes repeated keys on `GET`.
* **Explicit `international_registrations=grouped`** on a request the grouped
  view can't serve returns `400 unsupported_on_grouped`. Leaving it unset
  keeps the expanded fallback and its warning.
* **Skip warnings are per channel**: `<channel>_skipped` with a `channel`
  field.

**Watches and alerts**

* **Similarity watches** take `version: "v3"`, `q`, `similarity` and
  `min_tier` (`identical`, `near_identical`, `similar` or `related`; default
  `related`). Existing watches keep alerting exactly as before until their
  query is changed. See [Watches](https://docs.signa.so/guides/monitoring/watches.md).
* **New watch queries use the new matching** (added later on September 29).
  Create, bulk create, preview and an update that changes the query store it
  as `version: "v3"` whatever `version` you send: a plain
  `{ "version": "v2", "q": "acme" }` gets the default channels and
  `min_tier: "related"`. Only a query with `strategies` or `min_match_tier`
  is kept as a legacy watch. An update that sends the stored query back
  unchanged still changes nothing.
* **Alerts gain `match.tier`**, on REST and in webhooks. `match.score` is
  still the engine score for the watch query, not the 0 to 100
  `relevance_score`.
* Watch preview and diagnostics report `similarity_applied` and `min_tier`.

**Deprecated until December 28, 2026.** `strategies`, `match`,
`ranking_profile`, a list in `q`, POST `query` and POST top-level
`mark_text_not_contains` are translated and listed in
`search_meta.deprecations`. A translated request never returns fewer marks
than before. On watches, `strategies` and `min_match_tier` are not translated:
a watch written with them is kept as a legacy watch until it is re-saved
without them, and they are listed in the response's `deprecations`. From
December 28, 2026, each returns
`400 unknown_parameter` naming its replacement. The response fields
`match_explanation`, `matched_terms`, `search_meta.strategies_used` and
`search_meta.match` are filled from the new data until the same date.

**TypeScript SDK 0.17.0** takes the new keys on `trademarks.list()`,
`trademarks.search()`, the party `trademarks()` methods and watches, removes
the old keys from the types, sends text filters as repeated keys, and exports
`Presets.knockout` and `Presets.clearance`.

## September 28, 2026: party counts agree, Madrid designations show their serials, USPTO address and date fixes

Fixes from a customer's attorney-onboarding test. Nothing here removes or renames a field.

* **List and detail counts agree.** `GET /v1/attorneys`, `/v1/owners` and `/v1/firms` list rows now read `trademark_count` and `active_count` from the same stats rows as the detail endpoints, not from the search index document. The monthly stats recompute now refreshes the search index for every party whose counts changed, and a repair pass is re-syncing attorney, firm and owner search documents (some attorneys and firms were missing from `q` search).
* **Party detail ETags follow the stats.** `/v1/attorneys/{id}`, `/v1/owners/{id}` and `/v1/firms/{id}` ETags change when the stats are recomputed; `/v1/entities/{id}` folds its live counts in. A client revalidating a cached body gets `200` with current counts instead of `304`.
* **Counting grain is in the spec.** Every party stats field now says what it counts: `trademark_count` and `active_count` count marks (a Madrid IR and its designations once); `registered_count`, `pending_count`, `expired_count`, `cancelled_count`, `abandoned_count`, the rates and `avg_prosecution_days` count office records, so the status counts can sum to more than `trademark_count`. `jurisdiction_count` counts offices.
* **Madrid designations carry their own numbers.** `coverage.territories[]` on grouped IR rows (search, party trademark lists, Retrieve Trademark, Batch Trademarks) adds the designation record's `office_code`, `application_number` and `registration_number`, so a US designation shows its 79-series serial on the grouped row. Pass `international_registrations=expanded` for one row per designation; the party trademark list pages now document it.
* **IR detail lists its designations' attorneys.** Retrieve Trademark for a Madrid IR (`grain: mark`) now rolls up `attorneys[]` from its designations, as it already did for owners and classifications. Each attorney on such a row carries `territory_codes`, the designations whose record names them. IR-parent ETags change once.
* **Attorneys show their office.** Attorney list and detail rows carry `source_office_code`. Attorney records are per office: the same person appears once per office they file at.
* **`firm_id` matches the firm of record.** The `firm_id` filter and facet on trademark lists match the firm of record on the filing when known, else the attorney's current firm, the same firm detail rows show. Index documents built before this change pick it up on the next index rebuild. USPTO filings stored before April 2026 are getting their firm of record from the correspondence address line.
* **USPTO addresses.** USPTO attorney addresses are parsed into `lines`, `city`, `region`, `postal_code` and `country_code` (a trailing "United States" line no longer lands in `lines`), and USPTO attorneys get `country_code: "US"`, so `GET /v1/attorneys?country_code=US` returns them. Rows whose office gave no country stay `null` and are excluded from a `country_code` filter. USPTO owners now carry their street lines in `address.lines`. New and updated marks get these from this release; stored rows are repaired in place without webhooks or alerts.
* **TTAB filing dates.** US proceedings now take `filed_date` from the USPTO TTAB feed; before, only about 2% had one, so `filed_date_*` filters, `sort=filed_date` and `filed_year` undercounted. Historical rows are being backfilled; until the warning on the proceedings pages is removed, do not rely on TTAB date filters.
* **Assignments.** The assignment pages state their scope: USPTO only, refreshed daily, with the USPTO's 6 to 10 week recordation lag. Assignments recorded February to April 2026 are being reloaded.
* **USPTO event codes in the catalogue.** `GET /v1/event-types?office_code=US` lists about 1,000 USPTO prosecution history codes with the `event_type` each maps to. `SUNA` (notice of acceptance of a statement of use) is now always `approval`; the mailed form was typed `response_filed`.
* **Clearer `office_code` error.** `office_code` on `GET /v1/trademarks` still returns `400`; the message now names `offices` (one or more codes) and `office` (one code).
* **Docs.** Per-tier `include` values (`full_goods_services` on lists, `office_extensions` on detail and batch), webhook examples on `payload_version: 2` with real status values, a portfolios walkthrough for status-change notifications, current plan limits, and `ETag`/`If-None-Match` change detection on List Deadline Rules.

## September 28, 2026: OpenAPI nullability checked against live responses

No request or response changes. The September 27 nullability fix is now
enforced by a test that validates real API responses against the published
OpenAPI document, so a field the API returns as `null` cannot again be
published as non-nullable. A nullable field that references a
shared schema is declared as `anyOf: [{$ref}, {type: "null"}]`.

## September 28, 2026: one primary media item; document fields on media

* **`media[].is_primary` marks exactly one item** whenever `primary_image_url`
  is set: the item that URL points at. A detail with several images and no
  office-flagged primary used to show every item as `is_primary: false` while
  `primary_image_url` still pointed at one of them; the two now come from one
  rule. When `primary_image_url` is null, no item is primary.
* **`media[].document_kind`, `media[].official_date`, `media[].page_count`**
  are added (additive, nullable) for a document item: `document_kind` is the
  same kind `GET /v1/trademarks/{id}/documents` serves for that document
  (Signa's classification of the office's document type), and `official_date`
  and `page_count` are the office's document date and page count, null when
  the office published none. All three are null for images. TypeScript SDK
  types regenerated.

## September 28, 2026: Madrid family `status.challenges` is the union across designations

On an international registration whose designations disagree, `status.challenges`
used to read `["mixed"]`. It now lists the real challenges: the de-duplicated
union across the designations, in a fixed order (for example
`["opposition_pending"]` when one designation is opposed), and `[]` when no
designation is challenged. `status.primary` and `status.stage` still read
`mixed` on such a family; `mixed` is never a challenge value. The same value
appears on the trademark detail, search hits, portfolio rows and watch
previews. No request or response field is added or removed.

## September 28, 2026: Entity trademarks list scopes like the `entity_id` filter

`GET /v1/entities/{id}/trademarks` now returns every mark whose owner is
linked to the entity, read from the same resolved entity ids as the
`entity_id` filter on `GET /v1/trademarks`. The two return the same set for
the same entity.

* **No member-owner cap.** The endpoint no longer answers
  `422 entity_too_large` with `reason: member_owners_too_large` for an entity
  with more than 10,000 member owners; it lists. The only remaining `422` is
  `family_graph_too_large` from `include_family=true`.
* **Resource semantics are unchanged.** A missing entity is still `404`, a
  merged one `410`, and `include_family=true` still walks the entity's own
  subtree. The MCP tool `get_entity_trademarks` follows the same scope.

## September 28, 2026: filter-only order on expanded rows, `mixed` on the record grain

* **Filter-only requests served one row per record are newest first.** A
  request without `q` or `sort` that is answered with expanded rows
  (`international_registrations=expanded`, or a grouped fallback such as
  aggregations or an unsupported sort) now returns the newest filings first
  (`-filing_date`), as documented and as the grouped view already did. Cursors
  minted by such requests before this release may no longer continue; start
  the walk again.
* **`status_primary=mixed` and `status_stage=mixed` are grouped-view filters.**
  A request served one row per record that carries them is now a
  `400 validation_error` (`unsupported_on_record_grain`, naming what forced
  the record grain) instead of an empty page.
* **`debug_timing` is no longer documented** on `GET /v1/trademarks`. It never
  changed a response; it is still accepted and ignored.
* **TypeScript SDK 0.16.0** drops `ranking_profile` and
  `options.include_profile` from the search types.

## September 28, 2026: `q` takes a list of exact terms; `q_any` is replaced (breaking)

Search has one text field again. `q` on `GET` and `POST /v1/trademarks`
takes either one term, as before, or a list of exact terms. `q_any` is
replaced by the list form.

* **The list form.** On `GET`, write the whole value as a JSON array of
  strings: `q=["limber","bimber"]`. On `POST`, send an array in `query` or
  `q`: `{"query": ["limber", "bimber"]}`. The response is the same as
  `q_any` gave: every mark that is an exact match for any term, each hit
  carrying `matched_terms`, and `search_meta.match` reads `exact`. See
  [Several exact terms](https://docs.signa.so/api-reference/trademarks/search-trademarks.md#text-filters).
* **One term is unchanged.** A string `q` works exactly as before. It is
  always literal text: no operators, no comma splitting. Only a value that
  parses in full as a JSON array of strings is a list, so `[RE]BUILD` and
  `[1,2]` are still searched as written.
* **A list runs `match=exact`.** It is the default for a list, and sending it
  is allowed. Any other `match`, or `strategies`, with a list is a
  `400 validation_error`.
* **100 terms per list** (`q_any` took 200), each up to 200 characters. Long
  lists go by `POST`: a `GET` query string over 2 KB is rejected before it
  reaches the API.
* **`q_any` is replaced.** A request that sends `q_any` gets a
  `400 validation_error` whose message names the list form; move the terms
  into `q` unchanged. The request is not billed and never runs as a
  filter-only search. Cursors minted by `q_any` requests may no longer
  continue; start the walk again.
* **Pricing is unchanged.** One page of a list search costs the same as one
  page of any search, regardless of how many terms it carries.
* **TypeScript SDK 0.15.0** types `q` as `string | string[]` on
  `trademarks.list()` and `trademarks.search()` and removes `q_any`. SDK
  0.14.0 sends `q_any`, so upgrade before relying on the list form.

## September 28, 2026: `mixed_script` search warning

`match=exact`, `starts_with`, `ends_with`, `contains` and `q_any` compare
letters as written, so a look-alike spelling such as `τιmbεr` (Greek letters
among Latin ones) finds only marks spelled with those exact characters. When a
term mixes Latin with Greek or Cyrillic letters, `search_meta.warnings` now
includes `mixed_script` (`severity: info`, `affected_filter: q` or `q_any`)
naming the spelling. Results are unchanged. Ranked search with
`strategies=exact` folds look-alike letters and carries no warning.

## September 27, 2026: Provenance release follow-ups

* **CIPO status labels.** Signa used to join CIPO's status and its internal
  description into one `status.raw.label` (for example `Registration
  published - Registered`). The label is now CIPO's internal description alone
  (`Registered`), the same text a record without a status code carries.
  `status.raw.code` is unchanged, and
  [List Status Codes](https://docs.signa.so/api-reference/reference/list-status-codes.md) still serves
  `null` labels for CIPO.
* **EUIPO event labels.** On EUIPO trademark events whose text Signa composed
  from several fields (bulletin publications, oppositions, cancellations,
  appeals, seniorities, decisions), `raw` is `null` and the text stays in
  `description`. EUIPO's own recordal kinds, such as `Proprietor - Total
  transfers`, are still served as `raw.label`.
* **IPOS event labels.** The filing and registration events Signa adds to IPOS
  trademarks (`Application filed`, `Registration procedure completed`) use
  Signa's wording, so `raw` is `null` and the text stays in `description`.
  IPOS's own event entries are unchanged.
* **INPI event codes.** Signa used to join INPI fields into one event code,
  such as `Inscription: Changement d'adresse` or `Transaction: Correction`.
  `raw` now carries the fields alone: a recordal is `{"code": "Inscription",
  "label": "Changement d'adresse"}` and a weekly transaction is
  `{"code": null, "label": "Correction"}`. Events from INPI's status history
  are unchanged.
* **OpenAPI nullability.** Enums documented as nullable now list `null` among
  their values, and `derived.next_deadline` is declared as a `DeadlineRow` or
  `null`. Responses are unchanged; a strict OpenAPI validator now accepts the
  `null` values the API already returned.
* **Event types.** [List Event Types](https://docs.signa.so/api-reference/reference/list-event-types.md)
  catalogues IP Australia (`AU`) and WIPO (`WO`) codes today. Other offices
  return an empty list.

## September 27, 2026: The provenance release (breaking)

This is one breaking release, announced ahead of time to beta accounts. After
it, every value says whether the office published it or Signa computed it, in
one vocabulary, and a search hit is the same row as the detail. Update your
integration and the TypeScript SDK (0.14.0) together.

**The headline:** top-level dates are the office's own. `expiry_date` and
`renewal_due_date` are `null` when the office published none. The USPTO
publishes no expiry date, so `expiry_date` is `null` on every USPTO record; the
rulebook's term end is `derived.expiry_date`. Filters and sorts on
`expiry_date` and `renewal_due_date` follow the office values too.

### Trademark rows (search, lists, portfolios, screening, batch, detail)

* **Status.** `status.source` is now `status.basis` (`office`,
  `event_derived`, `dispatch_derived`, `computed`). `status.raw_code` and
  `status.raw_label` are now `status.raw: {code, label}` (or `null`). The label
  is the office's verbatim text only: `null` when the office publishes none,
  or when the stored text was the code itself or wording Signa wrote. Decode
  codes with the new [List Status Codes](https://docs.signa.so/api-reference/reference/list-status-codes.md).
* **Dates.** `expiry_date_basis` and `renewal_due_date_basis` are removed;
  top-level dates are office-published only. New `last_renewal_date`, the last
  renewal the office recorded (USPTO, CIPO and OEPM today). In every
  jurisdiction the rulebook treats the renewal cycle it paid, and every earlier
  cycle, as paid, so an early-renewed registration no longer lists the paid
  cycle as due. `last_renewal_date` on a `POST /v1/deadlines/compute` item works
  the same way (it used to be read for Canada only). A paid cycle is not a missed
  one either: `urgency=missed` on `GET /v1/deadlines` and portfolio deadlines, and
  `include_missed: true` on `POST /v1/deadlines/compute`, no longer list a cycle
  before the office-stated expiry, the cycle a recorded renewal paid, or the
  restoration window such a cycle would open. `include_missed` used to return that
  paid history.
* **An IR's `expiry_date`** is the expiry WIPO published on its designations.
  Known limitation: the search index does not hold it on the IR's own record
  yet, so `expiry_date_*` does not match the IR's own record and
  `sort=expiry_date` orders it with rows that have no office expiry. The next
  index rebuild fixes this. Also known: a renewal WIPO publishes on the
  designations moves the IR's `expiry_date` without raising a
  `trademark.updated` event or alert for the IR's own id
  ([Watches](https://docs.signa.so/guides/monitoring/watches.md)).
* **`derived` on every row**, search hits included: `ruleset_version` (an
  `rs_` content hash, was a date), `as_of_date`, `expiry_date`,
  `deadlines {supported, reason, unsupported_rules, items[]}`,
  `next_deadline` and `opposition_window`. The detail's
  `deadlines_supported`, `deadlines_unsupported_reason` and
  `deadlines_unsupported_rules` move into `derived.deadlines`, and the deadline
  array into `derived.deadlines.items`.
* **Opposition window.** The top-level `opposition_window` on search rows is
  removed. `derived.opposition_window` is always present and two-state:
  `{supported: true, rule_id, trigger_event, publication_date, window_opens,
  window_closes, close_adjustment, office_time_zone, common_extension}` or
  `{supported: false, reason, rule_id}` with no null date keys. `rule_version`
  is removed.
* **Deadline rows**, everywhere a deadline appears (the `derived` block,
  `GET /v1/deadlines`, portfolio deadlines, `POST /v1/deadlines/compute`):
  `grace_expiry` is `grace_expiry_date`, `recurring` and `optional` are
  `is_recurring` and `is_optional`, `name` and `description` are removed, and
  new `occurrence_key`, `due_date_adjustment`, `grace_expiry_adjustment`,
  `holiday_calendar` and `consequence_if_missed` appear on every row.
  `occurrence_key` identifies one occurrence within one trademark across
  reads: `rule_id:<cycle>`, or `rule_id:t<term>-<n>` when the record has no
  statutory anchor date or the row is anchored on an office-stated expiry. The
  calendar event UID namespaces it by trademark:
  `tm_<uuid>:<occurrence_key>@signa.so`.
  Madrid rules report `jurisdiction_code: "WO"`, and a `POST /v1/deadlines/compute`
  item sent as `WIPO` echoes `WO`.
* **Office record.** `source_primary_id` is replaced by `office_record_id`
  (the office's own id; `null` only when Signa composed the key: an IR's own
  record and its WIPO-feed, Finnish and Polish designation records; other
  national-office records of a designation keep the office's id) and
  `office_url`.
* **IR numbers** lose Signa's `WO` prefix in `registration_number`
  (`"WO921642"` is now `"921642"`), and the `registration_number` filter
  matches the unprefixed number.
* **Owners.** `owners[].entity_id` is the resolved entity or `null`;
  `owners[].entity_id_type` is removed; `owners[].entity_type` is now
  `owners[].legal_form`. Detail owners gain `is_current`.
* **Attorneys** (detail): `attorneys[].firm_id` and `firm_name` are the firm of
  record on the filing, falling back to the attorney's current firm (they used to be
  the current firm). The `firm_id` filter and facet still match the
  attorney's current firm, so about 5% of records can be returned for a
  different firm than their detail shows, until the next index rebuild.
* **Addresses** have one shape everywhere (owners, attorneys, assignment
  parties): `{lines[], city, region, postal_code, country_code, formatted}`.
  `state` is `region`, lines are verbatim, and components appear only when the
  office supplied them.
* **Register.** The detail's `us_register` is replaced by `register {type,
  acquired_distinctiveness, is_incontestable, standard_characters_claimed}`
  (or `null`), on every row and for every office that states one.
* **Grain.** New `grain: "mark" | "record"`. `coverage`, `source_records` and
  `owners_mixed` appear only on Madrid IR family rows (they were `null` on
  others). Coverage territories are `{territory_code, status {primary, stage},
  trademark {id, href}, office_url, ...}` (they were `territory`, `primary`,
  `stage` and `trademark_id`), and the IR's own `WO` record is no longer
  listed as a territory.
* The row-level `office_updated_at` is removed (the detail keeps it under
  `provenance`, which gains `snapshot_date`).
* `territory_matches[].basis` is `matched_via_kind`.
* `classifications[].class_status_raw` is `classifications[].status_raw {code, label}`.
* `design_codes[]` are `{system, code, edition}` (were `code_system`,
  `code_value`), with `system` one of `vienna`, `us_design_search`, `other`.
* `filing_bases[].nice_class_number` is `filing_bases[].nice_class`.
* `classifications[].scope` (`as_filed`) appears only on IR family rows.
* `classifications[].nice_class` can be `null` (a class the office stated
  without a Nice number).
* The detail's `jurisdiction_code` can be `null`, as on the row.

### Search and filters

* **Removed filters** (now a `400`): `renewal_due_before` (use
  `renewal_due_date_lt`), `us_register_type` (use `register_type`, which works
  for any office register), `opposition_closes_before` and
  `opposition_closes_after` (use `opposition_closes_lte` and
  `opposition_closes_gte`). This applies to `GET` and `POST /v1/trademarks`
  and to the owner, attorney, firm and entity trademark lists.
* **Grouped per-leg filters.** On the default grouped view, the office, route,
  filing, registration and expiry date and registration number filters stay
  grouped, alone or together: they must hold on the same record, and they
  match the leg shown in `coverage.territories[]` for each territory. When a
  territory has both a WIPO-feed and a national-feed record, the one Signa does
  not show (listed in `source_records`) does not match.
* `search_meta.fallback_reason` is removed. A grouped request served one row
  per record now carries an `expanded_fallback` entry in
  `search_meta.warnings[]` with `affected_filters` or `affected_option`.
* `search_meta.index_generation` is new. **Cursors are bound to the index
  generation**, so a cursor minted before this release is rejected with
  `400 cursor_invalid`: restart pagination. The same applies after any future
  index rebuild.
* New `result_window_reached` warning (`severity: info`) in
  `search_meta.warnings[]`: the last page of a relevance-ranked or sorted walk
  that the 10,000-row window ended while more rows match. A walk that ran out
  of matches never carries it.
* `entity_id` and `entity_group` with an owner id in entity form
  (`ent_<owner-uuid>`) return an empty page. Filter a single owner with
  `owner_id`.
* `entity_id` and `entity_group` on trademark search no longer return
  `422 entity_too_large` for an entity with more than 10,000 member owners;
  they match the entity directly. `entity_group` keeps its family-walk bound,
  and the other entity surfaces keep the member cap
  ([errors](https://docs.signa.so/api-reference/errors.md)).
* `opposition_status` and `opposition_closes_*` match only while the mark is
  at a stage it can be opposed at (the gate behind `stage_not_opposable`); they
  used to match at any stage.
* If Signa cannot load a result page's stored data (owner entity links, record
  facts, IR family facts), a trademark search or list request now fails with a
  retryable `503 service_unavailable` and `Retry-After: 5` instead of serving a
  degraded page. An owner-lookup failure used to serve `owners[].entity_id:
  null` and show owners that are normally hidden.

### Other endpoints

* **Batch** (`POST /v1/trademarks/batch`): identifiers name their office with
  `office_code` (`office` is now a `400`). `data` answers every submitted item
  in input order, and a repeated id repeats.
* **Events** (`GET /v1/trademarks/{id}/events`): rows are
  `object: "trademark_event"` with their own `hst_` ids (were `event` and
  `evt_`); `raw_code` and `raw_label` are `raw {code, label}`;
  `nice_class_number` is `nice_class`; `sequence_no` is `sequence_number`;
  `description` is `null` unless it adds office text to `raw.label`. The org
  feed `GET /v1/events` keeps `object: "event"`.
* **USPTO event types.** A USPTO notice of suit (`NOSU`) is
  `event_type: "litigation"` (was `office_action`), so it no longer matches
  `event_type=office_action` and no longer raises `office_action.issued`.
  Attorney and domestic-representative appointments, revocations and
  withdrawals are `representative_changed` (were `address_change`); a
  representative's own address change stays `address_change`. Labels naming
  the USPTO's examining attorney, which the old rule also filed as
  `address_change`, take the type their label maps to (for example `other` or
  `correction`). Stored history is retyped after the release, with no webhook
  or alert.
* **Entities.** `entity_id_type` is removed. Entities are materialized
  entities only: `GET /v1/entities/{id}` (and `/trademarks`, `/family`) with an
  owner id in entity form is a `404`, linked owner or not; a merged entity
  stays `410`. `GET /v1/entities` no longer lists unlinked owners, and
  `members[].link` is never `null`.
* **Owners, attorneys, firms.** Owner `entity_id` (list and detail) is the
  linked entity or `null`, and `entity_id_type` is removed. Stats
  `earliest_filing` and `latest_filing` are `earliest_filing_date` and
  `latest_filing_date` (sort values are unchanged). `aliases[].source_office`
  is `source_office_code`. `address` is the one address shape.
* **`legal_form` on every party.** `entity_type` is `legal_form` on owners
  (list and detail), attorney clients (`GET /v1/attorneys/{id}/clients`),
  entities (list and detail) and entity `members[]`, as on trademark
  `owners[]`. The `GET /v1/owners` and `GET /v1/entities` filter
  `entity_type` is `legal_form` (the old name is a `400` that names it).
* **Proceedings** (`GET /v1/proceedings/{id}`): `trademark.serial_number` and
  `trademarks[].serial_number` are `application_number`.
* **Event types** (`GET /v1/event-types`): `raw_code` and `raw_label` are
  `raw {code, label}`, as on a trademark event. The label follows the
  verbatim-label rule, so WIPO rows (whose catalogue labels are Signa's
  descriptions) have `label: null`. The `office` filter is `office_code`, as on
  `GET /v1/status-codes`; `office` and any other unknown parameter are a `400`.
* **Assignments.** `reel_no`, `frame_no`, `release_of_reel_no` and
  `release_of_frame_no` end in `_number`; new `office_code`. Parties:
  `nationality` is `country_code` (ISO alpha-2, `null` when the office named no
  country) and `address` is the one address shape. Properties: `serial_no`,
  `registration_no`, `intl_reg_no` and `mark_name` are `application_number`,
  `registration_number`, `ir_number` and `mark_text`.
* **Deadlines.** `unsupported_marks[].unsupported_reason` is `reason` on
  `GET /v1/deadlines` and portfolio deadlines. Compute items:
  `unsupported_reason` is `reason`. Opposition compute items fold
  `unsupported_reason` into `reason`. Reconcile `computed.unsupported_reason`
  is `computed.reason`. `GET /v1/deadline-rules` reports Madrid rules as
  `jurisdiction_code: "WO"` (was `WIPO`); its `jurisdiction` filter still
  accepts `WIPO` as an alias.
* **Calendar** (`GET /v1/portfolios/{id}/deadlines.ics`): event UIDs are
  `tm_<id>:<occurrence_key>@signa.so`. A file downloaded after the release
  adds every event again beside the ones imported earlier (delete those once);
  afterwards, re-importing updates an event whose due date was corrected
  instead of duplicating it.
* **Offices** (`GET /v1/offices`, `/v1/offices/{code}`): `coverage` and
  `coverage_computed_at` are `data_completeness` and
  `data_completeness_computed_at`. `data_completeness.expiry_date` counts
  office-stated expiries only.
* **Citations:** `disposition_as_of` is `disposition_at`.
* **Goods and services suggest:** `accepted_terms[].harmonised` is
  `is_harmonised`.
* **Office analytics:** `earliest_filing` and `latest_filing` are
  `earliest_filing_date` and `latest_filing_date`.
* **Screening:** `screening_id` values change once (the receipt now binds the
  served index generation) and again after each index rebuild.
* **ETags** change format. Every cached validator gets one `200`, then `304`s
  resume.
* **Watches:** a watch filtering on `expiryDate` or `renewalDueDate` now
  matches office-published values, like the search filters. New watches
  cannot target an owner id in entity form; use `filters.ownerId`. Existing
  watches that did are migrated to `filters.ownerId`, or to the entity the
  owner has since been linked into, and keep evaluating the same marks.
* New: [List Status Codes](https://docs.signa.so/api-reference/reference/list-status-codes.md)
  (`GET /v1/status-codes?office_code=US`; unknown query parameters are a
  `400`).

### Webhooks and `GET /v1/events` (`payload_version` 2)

New events are `payload_version: 2` from September 27, 2026. Every event keeps
the version it was stored with: automatic retries, `/redeliver` and
`GET /v1/events/{id}` of an event recorded before then stay v1, so handle both
for the 30-day replay window. See [Webhooks](https://docs.signa.so/guides/monitoring/webhooks.md#payload-versions).

* `office_action.issued` v2 replaces the flat `event_date`, `event_code`,
  `event_label`, `scope`, `status_after_event`, `nice_class_number`,
  `description`, `source_identifier` and `sequence_no` with
  `trademark_event`, the same row `GET /v1/trademarks/{id}/events` returns.
* Change field names in v2 (`trademark.status_changed` `changed_fields` and
  `changes`, `alert.created` `alert.event.diff[].path`, and REST alert
  `event.diff[].path`): `source_primary_id` is `office_record_id`,
  `status_source` is `status_basis`, and `expiry_date_basis` and
  `renewal_due_date_basis` are no longer published.
* Change values in v2 follow the row: `status_basis` uses the `status.basis`
  vocabulary; an `expiry_date` or `renewal_due_date` side that is not
  office-stated is `null`, and a change between two computed values is
  omitted; `status_raw_label` follows the verbatim-label rule;
  `registration_number` has no `WO` prefix.
* No alert is raised for a change whose every changed field is one the row
  does not publish (a Signa-computed date moving, or a basis change that stays
  non-office), so an alert never arrives with an empty diff.
* Both versions: on events emitted from the release on, `data.occurred_at`
  is ISO 8601 UTC with milliseconds (`2026-09-26T22:23:21.191Z`), as
  `GET /v1/events` serves it. It used to carry Postgres text
  (`2026-09-26 22:23:21.191924+00`), which strict ISO parsers rejected.
  Retries and redeliveries of events emitted before the release keep the old
  text.

### MCP

`get_trademark_events` returns the `trademark_event` rows;
`get_entity`, `get_entity_trademarks` and `get_entity_family` treat an owner id
in entity form as not found; `batch_get_trademarks` answers in input order;
`search_trademarks` and the party trademark tools take `register_type`,
`opposition_status` and `opposition_closes_gte/_lte`; `list_offices` and
`get_office` carry `data_completeness`; `get_trademark`,
`get_trademark_events` and the new `list_status_codes` declare an output
schema. `list_event_types` returns `raw {code, label}` rows, pages with the
REST cursor and takes `office_code` (was `office`). `list_owners` and `list_entities` take `legal_form` (was
`entity_type`). `get_deadline_rules` reports Madrid rules as
`jurisdiction_code: "WO"` (was `WIPO`). Every tool rejects an argument it does
not declare (a retired filter name or a typo) with an input validation error
instead of ignoring it. A tool result that fails its declared output schema is
an unbilled tool error (it used to be charged, then rejected by the client).
A tool that fails with an API error (for example not found or a validation error) returns
the typed JSON error body REST returns (`{"error": {"type", "status",
"title", "detail", ...}}`, including `errors[].code`) instead of a
`Failed to get X: ...` message.

### Upgrading

1. Read expiry as `expiry_date ?? derived.expiry_date`, and deadlines from
   `derived.deadlines.items`.
2. Rename the filters above, and batch `office` to `office_code`.
3. Treat `owners[].entity_id` as nullable, and use `owner_id` for unlinked
   owners.
4. Restart any paginated walk that spans the release.
5. Branch webhook handlers on `payload_version`.
6. Upgrade to `@signa-so/sdk` 0.14.0 ([TypeScript SDK](https://docs.signa.so/sdk/typescript.md)).
   Its types follow every change above. The old filter names are not
   translated: the API answers them with a `400` naming the replacement, and
   `list()`'s POST fallback throws a `SignaError` that names it. `toArray()` never returns a short array silently: it throws
   `SignaTruncatedError` past its 10,000-item default cap, and also when the
   API ended a relevance-ranked or sorted search at its 10,000-row window
   while more rows match (the last page's `result_window_reached` warning).

## September 24, 2026: Declined deadline rules and uncomputed marks are now listed

When a deadline rule applies to a mark but the record lacks the date the law
runs it from, Signa declines to guess and computes no row for it. Until now
that rule simply did not appear. Three surfaces now name it. All changes are
additive.

* `POST /v1/deadlines/compute`: every item carries `unsupported_rules`, an
  array of `{ rule_id, reason, missing_field, consequence_if_missed }`. It is
  `[]` when nothing was declined, and `[]` when `supported` is `false`, because
  `unsupported_reason` already covers every rule.
* `GET /v1/trademarks/{id}`, `POST /v1/trademarks/batch` and the MCP
  `get_trademark` tool: the `derived` block carries
  `deadlines_unsupported_rules` in the same shape, next to
  `deadlines_supported` and `deadlines_unsupported_reason`.
* `GET /v1/deadlines` and `GET /v1/portfolios/{id}/deadlines`: the list
  envelope carries `unsupported_marks`, one entry per mark in the requested
  portfolio or trademark that was not computed at all (`supported: false`) or
  was computed only in part (`supported: true` with `unsupported_rules`).
  Marks with a status that ends every obligation, such as `abandoned` or
  `withdrawn`, are not listed.
  Entries use the same `supported`, `unsupported_reason` and
  `unsupported_rules` fields as a compute item. The list covers the whole
  scope, not just the page, ignores the `type`, `urgency` and `due_before`
  filters, and is sorted by `trademark_id`. It is reported on the first page
  (a request without `cursor`) and is `null` on continuation pages.

A rule is declined when the date it runs from is owed but absent: a filing
date, an international registration date, or, once the mark is registered, its
registration or grant date. A registration-anchored rule on an application not
yet registered is not declined, because nothing is due yet. The Philippine
fifth-year declaration of use for a Madrid designation
(`ph_declaration_of_use_5yr_madrid`) runs from the statement of grant of
protection (IPOPHL MC 17-011 Rule 20(2)); send `protection_grant_date` on a
compute item and the row is computed. When every rule a mark owes is declined
this way, the whole mark declines with `supported: false` and
`unsupported_reason: "requires_office_date"`, for example a Swedish mark
registered on or after 2019-01-01 with no filing date, or a registered US mark
with no registration date. Such a mark was previously reported as supported
with no deadlines. Whole-mark declines in `unsupported_marks` also cover the
existing `unsupported_reason` cohorts, such as Australian direct marks filed before 1996, Singapore direct
marks filed before 15 January 1999, Madrid designations without an
international registration date, and jurisdictions Signa does not model.

The portfolio calendar download (`deadlines.ics`) cannot carry this list,
since a calendar file holds only dated events. Read `unsupported_marks` from
the JSON list to see which marks and rules are missing from it.

## September 24, 2026: Older Nordic registrations without a filing date get deadlines

Swedish, Icelandic, Norwegian and Finnish direct (non-Madrid) records with no
filing date and a registration date before the office's renewal-anchor change
(SE 2019-01-01, IS 2020-09-01, NO 2010-07-01, FI 2019-05-01) got no
maintenance deadlines. They now compute from the registration date, which the
transitional provisions key on for marks registered before the change (SE SFS
2018:1652 p. 4 a; IS L. 71/2020 39. gr. 3. mgr.; FI 544/2019 107 § 4 mom.; NO
Varemerkeloven § 83 with § 22). A record registered on or after the change
date with no filing date now declines with `unsupported_reason:
"requires_office_date"`, since its term runs from the filing date it lacks.
`GET /v1/deadline-rules` publishes the rule as `trigger_before_proves_cohort`,
`true` on the SE, NO, FI and IS rules. See
[Nordic countries](https://docs.signa.so/guides/deadline-rules.md#nordic-countries-trigger-field-change).

## September 24, 2026: Monitoring and reconcile on every plan; internal actions leave Get Credit Pricing

Mark watch, Filing watch (`similarity`, `owner` and `class` watches) and marks
under management are available on every plan, Free included, and cost no
credits during the beta. Creating a `similarity`, `owner` or `class` watch on
Free no longer returns `403 plan_gated`. The watch, portfolio and managed-mark
count limits for each plan are unchanged.

[Reconcile](https://docs.signa.so/api-reference/reference/reconcile.md) is available on Free as well.
`POST /v1/reconcile` no longer returns `403 plan_gated` on a Free key. It still
costs 1 credit per record, spent from your monthly pool.

[Get Credit Pricing](https://docs.signa.so/api-reference/administration/get-credit-pricing.md) no
longer lists internal-only actions (screening, compare and listing check) for
any key, and `status` is now one of `available`, `beta` or `planned`. They were
never available to self-serve keys and have no published price yet. SDK users:
`CreditPrice.status` no longer includes `'internal'`, so code that compares
against it or switches exhaustively on `status` needs updating.

## September 24, 2026: `offices` and `filing_route` keep Madrid results grouped

An `offices` or `filing_route` filter switched a default (`grouped`) search to
one row per Madrid designation: `offices=WO` returned several rows per IR, no
`coverage`, and a `total_count` that counted designations. Once the rebuilt
search index is live, `offices`, `office` and `filing_route` stay grouped when
they are the request's only filters. An IR then appears once, with its
`coverage`, when its WIPO record or any record linked to it matches. Combined
with another filter (`status_primary`, `jurisdictions`, `nice_classes`, …) they
still fall back to `expanded`, because the grouped row cannot tell whether both
conditions hold on the same designation; `search_meta.fallback_reason` names
the pair, for example `filter:offices+status_primary forced expanded`.
`owner_name` stays grouped from the same point. The per-designation date
filters (`filing_date_*`, `registration_date_*`, `expiry_date_*`) still fall
back.

`offices` matches the office a record comes from. `offices=CA` returns CIPO
records, not the WIPO records of IRs designating Canada. To find every IR
designating Canada, use `jurisdictions=CA`, which stays grouped.

The [search reference](https://docs.signa.so/api-reference/trademarks/search-trademarks.md) described
the fallback the wrong way round. A `grouped` request the grouped view can't
serve falls back to `expanded`, never the reverse.

## September 24, 2026: Canadian and Brazilian date rolls, six-month Madrid renewal windows, and new deadline fields

**Some maintenance dates move.** Maintenance deadlines are recomputed on every
request (`POST /v1/deadlines/compute`, the trademark `derived` block,
`GET /v1/deadlines`), so these apply now, with no reindex.

* **Canada:** `grace_expiry` rolls to the next day the office is open
  (Trademarks Act s. 66(1)), reported in `grace_expiry_adjustment`. `due_date`
  never rolls: it is the end of the term. `window_opens` on the office-anchored
  ladder no longer falls before the registration date (Regulations s. 159(a)).
* **Brazil:** `due_date` and `grace_expiry` roll to the next business day
  (Lei 9.784/99 art. 66 § 1º, applied by art. 69), reported in
  `due_date_adjustment` and `grace_expiry_adjustment`.
* **Madrid designations in CH, FR, AU, FI, PL, BR, IN, KR and CN:** the
  renewal `window_opens` is six months before the due date, the period the
  International Bureau accepts a renewal in (Common Regulations Rule 30(1)(b)).
  It was 12 months. `due_date` and `grace_expiry` do not change.
* **India:** the 7-year or 10-year first term is chosen by the Indian filing
  date, not the convention priority date. A convention application filed on or
  after 15 September 2003 with an earlier priority date now gets the 10-year
  term (filed 1 October 2003, priority 1 May 2003: due 1 May 2013, was 1 May
  2010\). The priority date is still the date the term is counted from.

`GET /v1/deadline-rules` reflects the Canadian and Brazilian rolls:
`ca_renewal` has `holiday_calendar: "cipo_canada"`, `weekend_holiday_rule:
true` and `grace_expiry_rolls_only: true`; `br_renewal` has
`holiday_calendar: "br_brazil"` and `weekend_holiday_rule: true`.

**Alerts say whether their deadline was checked against a holiday calendar.**
An alert's `must_act_by` is fixed when the alert is created. The new
`deadline.must_act_by_adjustment` (`moved`, `unchanged`, `not_checked` or
`month_end_overflow`) travels with it on `GET /v1/alerts`, the alert detail,
the signed webhook body and the MCP alert tools. `not_checked` means no office
calendar was consulted, so the date may be early but never late: act on it,
but confirm it before you record it as final. The field is `null` on alerts
created before this release and on alerts with no opposition window; `null`
means no marker was recorded, not that the date was verified.

**Watch diagnostics publish both opposition close dates.** The `opposition`
block of `GET /v1/watches/{id}/diagnostics` now carries `recomputed_close`,
the close computed at request time, next to `must_act_by`, the alert's fixed
date. The two can differ, for example after an office publishes a closure
notice. `close_adjustment` now always describes the date in `must_act_by`.

**`GET /v1/opposition-rules` gains three fields.**
`window_change_comparison_field` (`publication_date`, `filing_date` or `null`)
names the date compared against `window_change_date`; `tr_opposition` compares
the filing date. `extension_change_date` and `common_extension_before` give the
common extension that applied before a change, which the catalog did not
publish: CIPO went from 4 months to 2 on 2023-12-01, and IPOPHL from three
30-day extensions to one 45-day extension on 2022-06-30.
`POST /v1/oppositions/compute` already used the earlier figure for an earlier
close.

**`office_code` on `GET /v1/opposition-rules` is filled in for every office.**
It was `null` for offices Signa has rules for but no register from (`JP`,
`IN`, `TR`, `KR`, `CN`, `BX`, `DK`, `FI`, `PL`, `TH`, `VN`, `PH`, `IS`), so
joining it to `POST /v1/oppositions/compute` found nothing for them. `?office=`
now accepts those codes; it returned `400` before.

## September 24, 2026: `q_any` checks up to 200 exact terms in one request

`GET` and `POST /v1/trademarks` accept `q_any`: a list of up to 200 terms,
each at most 200 characters. It returns every mark that is an exact match for
any term, under the same rules as `match=exact` (case, accents, punctuation
and spacing), and each hit carries `matched_terms`, the terms it matched as
you spelled them. A clearance check over every one-letter variant of a word
becomes one request instead of hundreds. One page costs the same as any
search page.

Mind the request size: the API rejects a `GET` query string over 2 KB (`403`)
and a `POST` body over 8 KB (`413`) before your request reaches it. Send more
than a few short terms with `POST`, and split very large batches. The
TypeScript SDK keeps 700 bytes free for the page cursor: `trademarks.list()`
runs the walk by `POST` once the `GET` query string passes about 1.3 KB, and
`trademarks.search()` refuses a body over about 7.3 KB. See
[Several exact terms](https://docs.signa.so/api-reference/trademarks/search-trademarks.md#text-filters).

## September 24, 2026: One-character searches on owner, attorney and firm trademark lists

`GET /v1/owners/{id}/trademarks`, `/v1/attorneys/{id}/trademarks`,
`/v1/firms/{id}/trademarks` and `/v1/entities/{id}/trademarks` accepted a
one-character `q` but always returned an empty page. They now return that
party's marks whose whole text is that character (ignoring case, accents and
punctuation), and
`search_meta.warnings` lists the strategies that cannot run on one character
(for example `fuzzy_skipped`). `GET /v1/trademarks` still requires at least two
characters for `match=similar`, except a single Chinese, Japanese or Korean
character.

## September 24, 2026: Watches accept one- and two-character CJK keywords

A watch or watch preview whose `q` contained a Chinese, Japanese or Korean word
of one or two characters (`愛`, `東京`) was rejected with
`400 validation_error` ("Keyword too short"), although search accepts it.
Such words now pass; the 3-character minimum still applies to every other
keyword.

## September 24, 2026: Mixed-alphabet and accented queries match like the index

Exact matching and the normalized search lanes folded some queries differently
from the index, so they missed marks or matched the wrong ones. A mixed-script
query such as `τιmbεr` was reduced to `mbr` and returned MBR marks, and
`MÜLLER` lost its `Ü` and missed MÜLLER. Greek and Cyrillic accents
(`άλφα`, `Й`) were stripped although the index keeps them, and full-width
characters (`Ａ`) were not folded. Queries now fold exactly as the index does,
so these match the marks you would expect.

## September 24, 2026: Screening and compare no longer reject brackets and parentheses

`GET /v1/screening` rejected any `q` containing `[ ] ( ) { } | \` or a regex
pattern such as `.*` with `400 validation_error`, and so did
`GET /v1/screening?trademark_id=…` whenever the register mark itself contained
them, which no caller could work around. Screening and `/v1/compare` now
remove these characters before the search runs, the same way trademark search
does. The identical registered mark is still found under `sensitivity=strict`:
the exact-match check compares the text as sent. A screening `q` or a compare
`mark` with nothing left afterwards still returns `400`; a trademark whose mark
text has nothing searchable returns `422 not_screenable`. Listing checks are
unchanged.

## September 23, 2026: Long name searches return 400, not 503

**A search query that is too long to run now fails fast with a 400 you can
fix, instead of a 503 that invited retries.** A long `q`, such as a full legal
line pasted from a filing, could exceed the search engine's limit on how many
clauses one query may contain. We answered that with a retryable 503, so
clients retried a request that could never succeed.

On very long name searches for entities, owners, attorneys, and firms,
typo-tolerant matching now uses only the leading terms of `q`. Exact
whole-name matching still uses the full text.

If a query still exceeds the search engine's limit, the API returns
`400 validation_error` with `retryable: false` instead of a retryable 503.
When the request has a `q`, the `errors[]` array names it:
`{ "field": "q", "code": "too_complex" }`. Shorten `q` and send it again. A 503
now means only that search is temporarily unavailable. Trademark search first
retries once with the cheap strategies you requested (`exact`, `prefix`) and
reports that in `search_meta.strategies_used`; if you requested neither, or
used a `match` mode or `q_any`, it returns the same 400.

## September 23, 2026: Abandoned searches stop, and partial pages say so

When a client disconnects from `GET` or `POST /v1/trademarks` or
`POST /v1/watches/preview`, the search it started is now cancelled instead of
running to completion. Other endpoints (owners, attorneys, firms, entities,
suggest, screening, image search) are not covered yet. Searches on the request
path no longer retry a timed-out query at the transport level, and a trademark
search stops within about 20 seconds with a retryable `503` rather than
stacking retries. A page whose search hit its time limit or lost a shard now
carries a `partial_results` entry in `search_meta.warnings`.

## September 23, 2026: Relevance search pages to 10,000 rows on both views

A relevance search (`q`, no `sort`) now pages to 10,000 rows on both the
default grouped view and the expanded per-designation view. The expanded view
(used for `international_registrations=expanded`, `aggregations`, or a filter
the grouped view does not support) used to stop after 400 rows with
`400 cursor_window_exceeded`. The grouped view used to switch to a different
ordering after row 400, so a long walk could repeat or skip marks. Now every
page on either view runs the same query, the walk has no gaps or repeats, and
the last page inside 10,000 is served in full or in part and ends with
`has_more: false`.

**Restart long grouped walks.** A grouped relevance cursor minted before this
release for a page past row 400 now fails with `400 cursor_window_exceeded`,
and its `detail` says the cursor is from an older release: restart from the
first page. Cursors for the first 400 rows, and expanded-view cursors, keep
working. See [Pagination windows](https://docs.signa.so/api-reference/pagination.md).

## September 23, 2026: `entity_count` and `owner_count` aggregations

Trademark search accepts two new count aggregations, each returning
`{ "value": n }` for the result set. `entity_count` counts distinct parties
across offices (resolved entities; an owner not resolved to an entity yet
counts once per office record) and is the one to use for "how many owners
hold this phrase".
`owner_count` counts distinct owner records, which are per office, so one
company filing in four offices counts four times. The `owner_id` aggregation
lists only the top 50 owners, so it could not answer either question. Both
counts are close to exact up to 40,000 and approximate above that.

## September 23, 2026: `match=exact` ignores punctuation and spacing

`match=exact` compared punctuation literally: `LET'S GO` found the marks
written `LET'S GO` but `LETS GO` found nothing, and text normalized with
apostrophes turned into spaces (`I DON T`) never matched `I DON'T`. Exact
matching now folds case, accents, punctuation and spacing, so those spellings
all find the same marks. As a consequence, `KIT KAT` and `KITKAT` are also an
exact match for each other. `q_any` follows the same rules, so a batch of
terms finds every mark the matching single-term requests would. `starts_with`,
`ends_with` and `contains` are unchanged.

## September 23, 2026: Single CJK characters are valid search queries

The default `match=similar` mode required `q` to be at least 2 characters,
which rejected single-character Chinese, Japanese and Korean marks such as
`愛` or `馬` with `400 validation_error`. A single CJK ideograph, Hangul
syllable or Kana character is now accepted and searched. Other one-character
queries still need a deterministic `match` mode.

## September 23, 2026: Brackets and parentheses in `q` no longer return 400

`GET` and `POST /v1/trademarks` (default `match=similar`) and
`GET /v1/trademarks/suggest` used to reject any `q` containing
`[ ] ( ) { } | \` or a regex pattern such as `.*` with `400 validation_error`.
That also rejected real mark text such as `EAT. SLEEP. FIGHT. (REPEAT)` and
`DON'T SCARE ME [I POOP EASILY]`. These characters are now removed before the
search runs. A `q` with nothing left afterwards, such as `.*`, still returns
`400`. The deterministic `match` modes (`exact`, `starts_with`, `ends_with`,
`contains`) do not remove them. Screening and compare followed on
September 24 (see above); listing checks still reject these characters per
candidate with a `400`.

Watch queries follow the same rule. A watch `q` with these characters is
accepted and stored exactly as sent; searches ignore the characters, and an
exact-tier watch still matches a mark written with them. Watches that were
previously skipped because their stored `q` contained such characters are now
evaluated and may start alerting.

## September 23, 2026: Watch preview evaluates the watch query

**Fixed: `POST /v1/watches/preview` ignored `q` on large scopes.** When the
changes in the trial window exceeded an internal cap (common for a similarity
watch over US or EU), the preview returned the number of changed marks, for
example 659,662, as `estimated_match_count`, and `results` held marks that did
not match `q`. `min_match_tier` and `trigger_events` did not change the
number, and some queries hit `504 preview_timeout` after 10 to 15 seconds.

A watch with a text query is now evaluated query first: the preview searches
for `q` with your `strategies` and `filters`, applies `min_match_tier`, keeps
the marks that changed in the window with a matching `trigger_events` type,
and returns those. `results` are ordered by relevance. When the count cannot
be exact, `estimate_basis` says which way it can be off: `query_upper_bound`
(can only be too high) or `lower_bound` (can only be too low). For a watch
with a text query, a timeout returns `200` with `partial: true` and the
verified count instead of a `504`, and an unavailable search returns a
retryable `503`. See [Preview Watch](https://docs.signa.so/api-reference/monitoring/watches/preview.md).

## September 23, 2026: `strategies` now runs only the strategies you pass

**Fixed: `strategies=exact` also ran a synonym lane.** A request such as
`q=OUTER BANKS&strategies=exact` returned OUTER BANKS BIKE WEEK and HIDDEN
GUARDIANS OF THE OUTER BANKS with `strategies_matched: ["synonym"]`, while
`search_meta.strategies_used` reported `["exact"]`. Synonym matching is part of
the `fuzzy` strategy and now runs only when `fuzzy` is requested (it is in the
default `exact,fuzzy` set). Serial and registration number lookups now belong
to `exact`. Every `strategies_matched` label now belongs to a strategy in
`strategies_used`.

The same applies when OpenSearch rejects a query as too expensive: the
single retry runs only the cheap strategies among those you asked for
(`exact`, `prefix`; `exact` for the default set), and
`search_meta.strategies_used` reports what was served, and a
`strategies_reduced` warning lists the `dropped_strategies`. If you asked for
neither, for example `strategies=phonetic`, the response is
`400 validation_error` with `{ "field": "q", "code": "too_complex" }` when the
query was too large (shorten `q` or add `exact`/`prefix`; retrying does not
help), or a retryable
`503 service_unavailable` with `Retry-After` when the cluster was overloaded.
The default grouped view now follows the same rule; before, such a failure
there was a `500`.

Watches are unchanged: live watches and watch previews keep the lanes they
had before this change, so no stored watch starts or stops alerting.

## September 23, 2026: Aggregation counts now respect your filters

**Fixed: `aggregations` on `GET` and `POST /v1/trademarks` ignored every filter.**
Bucket counts were computed on `q` alone, so `offices`, `nice_classes`,
`status_primary`, date ranges and the other filters narrowed the results but
not the counts. A search filtered to `offices=US,EM,WO` still returned
`office_code` buckets for GB, FR and CA, and the buckets could add up to
several times `total_count`. `aggregations_only=true` had the same problem.

Aggregations now count the same set as the results: `q`, the match mode and
every filter. For single-valued fields such as `office_code` and
`status_stage`, the buckets add up to `total_count`. If you cached counts from
an aggregation request, refresh them.

For a drill-down sidebar, pass `aggregation_mode=exclude_own_filter`
(`options.aggregation_mode` on `POST`). This is not the old behavior, which
ignored every filter.
Each aggregation then applies every filter except its own, so the
`office_code` buckets ignore `offices` but still respect `nice_classes` and
`status_primary`. See
[Statistics view](https://docs.signa.so/api-reference/trademarks/search-trademarks.md#count-results).

## September 23, 2026: pre-reform Nordic records without a registration date are declined

Swedish, Icelandic, Norwegian and Finnish direct (non-Madrid) records filed before the office's renewal-anchor change date (SE 2019-01-01, IS 2020-09-01, NO 2010-07-01, FI 2019-05-01) that carry no registration date now decline with `supported: false` and `unsupported_reason: "requires_office_date"`, unless the mark is finally dead. Previously SE and IS served a filing-anchored renewal date, which could be years late after its first cycle, and NO and FI returned an empty schedule reported as supported. Madrid designations are unaffected (they renew on the international registration date), and an office-stated `expiry_date`, or a `renewal_due_date` with basis `reported`, still anchors and computes the schedule. See [Nordic countries](https://docs.signa.so/guides/deadline-rules.md#nordic-countries-trigger-field-change).

## September 23, 2026: `close_adjustment` gains `month_end_overflow`

**`opposition_window.close_adjustment` can now be `month_end_overflow`.** It
was `moved`, `unchanged` or `not_checked`; there is a fourth value, and it
appears only on Mexican windows. Reglamento LPI art. 4o, párrafo segundo sends
a period whose day number does not exist in the target month into the
**following** calendar month, «el plazo concluirá el primer día hábil del
siguiente mes de calendario», so a Mexican application published 31 May 2026
closes on 1 July 2026, not 30 June. The field reported `unchanged` for that,
which reads as "a calendar was consulted and this date was already a business
day" about a date that had just been moved a month.

Read it exactly as you read `moved`: the close was checked against the office's
calendar, and something moved it. The maintenance engine's
`due_date_adjustment` and `grace_expiry_adjustment` have carried this value
since 3 September; the opposition field is now the same four-value set.

**`not_checked` still wins where the calendar could not be consulted.** The
overflow's "following month" half is arithmetic and always applies; its "first
business day" half needs the office calendar. A Mexican close past the last
pinned IMPI acuerdo therefore stays `not_checked` rather than claiming an
overflow was resolved to a business day. The 20 day indeterminate band is
keyed on that same check, so it is unaffected. Maintenance rows follow the same
precedence: a Mexican `due_date_adjustment` or `grace_expiry_adjustment` past the
last pinned acuerdo now reports `not_checked` where it reported
`month_end_overflow`, and the date still carries the overflow.

**Reindex required.** `close_adjustment` is baked into search documents at
index time, so Mexican records whose window closes on a month-end overflow
carry `unchanged` in search until a full reindex runs. No DATE moves: this is
a marker correction only.

## September 23, 2026: an absolute statutory ceiling is never rolled past

**A prosecution due date clamped to a statutory ceiling is served on the
ceiling, closed day or not.** When an office states a response deadline later
than its own statutory maximum, Signa emits the maximum instead. That date was
then put through the weekend and holiday roll, which could walk it PAST the
wall: a §66(a) provisional refusal issued 28 May 2026 has a six-month ceiling
of Saturday 28 November 2026 under 37 CFR 2.62(a)(1)(ii), and
`POST /v1/deadlines/compute` returned Monday 30 November, a day on which the
response is already untimely. It now returns 28 November. The same guard
already applied to the extended due date; it applies to the base date now too,
which is the served date for any rule with no extension ladder.

**`due_date_adjustment` reports `not_checked` when the roll result was
discarded.** A date served on the ceiling did not take the roll, so reporting
`moved` (or `unchanged`) claimed a business-day finding that was never made.
This affects the statement-of-use chain, where the ladder lands exactly on the
36 month wall: a notice of allowance issued 10 January 2023 with all five
extensions still has an effective due date of Saturday 10 January 2026, and now
reports `not_checked` rather than `moved`.

## September 23, 2026: the Australian Christmas close-down is no longer projected

**IP Australia's Christmas close-down is declared one year at a time, and Signa
now treats it that way.** The Canberra office's excluded days come from the
Director General's annual declaration under Trade Marks Act 1995 s 223A, the
Office Manual Part 2.1 defines them as exactly that. Signa held one such
declaration, for 2026, and was computing its 25 December to 1 January
close-down for every year through 2101.

**Dates in a year with no pinned declaration move EARLIER.** Only the days a
declaration actually names are closed now; the ACT public holidays this
calendar computes from the law (New Year, Australia Day, Canberra Day, Easter,
ANZAC, Reconciliation Day, King's Birthday, Labour Day, Christmas and Boxing
Day with their in-lieu Mondays) are unaffected and still roll. A renewal grace
expiry on 24 December 2027, for example, is served on Wednesday 29 December
2027 rather than on 3 January 2028.

**`due_date_adjustment`, `grace_expiry_adjustment` and `close_adjustment`
report `not_checked` for Australian dates outside 2026.** That is the honest
answer: the deterministic roll still applies and can only be early, but no
declaration has been read for that year. Australian renewal DUE dates are
unaffected either way, they never roll.

**Reindex required.** One opposition window in our frozen sample of real
Australian oppositions moves (publication 29 October 2024, close 2 January 2025
to 30 December 2024). Opposition windows are baked into search documents at
index time, so Australian marks published in late October to December carry the
old close in search until a full reindex runs.

## September 23, 2026: EUIPO closure days are pinned back to 2023

**Signa now holds the EUIPO Executive Director's non-working-day decision for
every year from 2023.** Decision EX-22-10 of 20 December 2022 (for 2023) and
EX-23-11 of 20 November 2023 (for 2024) join EX-24-9 and EX-25-16 in the
evidence pack, so the `euipo_alicante` calendar covers 2023 to 2026 instead of
2025 to 2026.

**EU dates in 2023 and 2024 now roll against the decision that governs them.**
Until today those years sat outside the calendar's pinned coverage and took the
deterministic roll, weekends only, reported `not_checked`. They are verified
now, and `due_date_adjustment` / `close_adjustment` report `moved` or
`unchanged` for them. Dates move LATER where a decision closes the office:
Article 1 lists 1 April 2024 (Easter Monday), 11 April 2024 (Santa Faz),
23 to 31 December 2024 and 25 to 29 December 2023, none of which the weekend
rule sees. Later is the correct direction here, the Office states it was shut,
so the earlier answer named a day on which nothing could be filed. In our
frozen sample of 8,000 real EU oppositions, 358 window closes move (all later,
by 1 to 11 days) and 81 oppositions that had been scored as filed after the
close are now correctly inside it.

**Reindex required.** Opposition windows are computed at index time and baked
into search documents, so EU marks published in 2023 and 2024 carry the old
close date in search until a full reindex runs.
`POST /v1/oppositions/compute` and `POST /v1/deadlines/compute` are recomputed
per request and are correct immediately.

Nothing outside 2023 and 2024 changes: the 2025 and 2026 lists are untouched,
and 2027 onward still has no pinned decision and still takes the deterministic
roll. No `rule_id` changed.

## September 23, 2026: dates outside a calendar's pinned coverage are rolled again

**A deadline that falls outside an office calendar's pinned coverage is no
longer served on a Saturday.** Bounding the annual-notice calendars to the
instruments actually pinned (Mexico through 30 November 2026, Denmark and
France through 31 December 2026, EUIPO to its two Executive Director decisions,
WIPO to Information Notice 40/2025) was right about the data and wrong about
the answer: every engine read "outside coverage" as "do not roll at all". A
Mexican renewal reached from an ordinary 2025 filing came back due on Saturday
16 June 2035.

Those dates now take a **deterministic roll**: weekends, plus the fixed
holidays the calendar computes from the law rather than from a yearly notice.
An annual notice or acuerdo can only ever ADD closure days, so the office's true
reopening is at or after the deterministic answer, which means this can be early
and never late. The Mexican renewal above is now due Monday 18 June 2035.

**`due_date_adjustment` and `close_adjustment` still report `not_checked` for
these dates**, and that is the point: the date is the best safe date, the marker
says nobody verified the year's closure list. Nothing changes inside a
calendar's pinned coverage, where the answer is still `moved` or `unchanged`.

**Opposition windows: an unverified close inside the 20 day rollover band is
indeterminate, however it got there.** A close date that sits inside coverage
but whose rollover walk runs past the end of the pinned data is now treated
exactly like one outside coverage: within 20 days of an unverified close,
`POST /v1/oppositions/compute` reports the window `open` under
`serve_not_checked` and raises the coverage error otherwise. Before this, a
EUIPO window published 30 September 2026 read `closed` on 31 December while the
same document was bucketed open in search.

**Denmark and France gained a coverage FLOOR.** Their closure lists are pinned
for 2026 only and neither is computable from the statutory holidays, so 2024 and
2025 dates now report `not_checked` (and take the deterministic roll) instead of
claiming a verified `unchanged` on days those offices were in fact shut.

**The legacy US office-action rule cites a point-in-time URL.**
`uspto_domestic_oa_response@legacy` and
`uspto_domestic_suspension_inquiry_response@legacy` quote the 2015 text of 37
CFR 2.62(a), the six month period. Their `url` pointed at the current eCFR
section, which reads three months and contradicted the quote beside it. It is
now the immutable govinfo 2015 annual edition, which is the document the
evidence pack actually pins.

## September 21, 2026: Proceedings now link both sides, with `trademark_role` (breaking)

**An opposer's own marks now list the oppositions that opposer filed.** Until
now a TTAB proceeding was attached only to the mark it was filed against, so
`GET /v1/trademarks/{id}/proceedings` on a prior registrant's mark returned
nothing about the cases that registrant brought. The USPTO's TTAB feed names
the marks on both sides, and we now link all of them.

Every proceeding row carries a new required field, `trademark_role`:

* `contested`: the proceeding targets this mark (the opposed application, the
  registration under cancellation, the appealed application).
* `asserted`: the opposer or petitioner pleaded this mark as a prior right.

It appears on `GET /v1/proceedings` items, `GET /v1/proceedings/{id}`, and
`GET /v1/trademarks/{id}/proceedings` items, and all three accept a
`trademark_role` filter. `GET /v1/proceedings` also takes `trademark_role` as
an aggregation. `GET /v1/proceedings/{id}` gains `trademarks[]`, every mark in
the case with its role, so the opposed application and the pleaded
registrations come back in one call. The `trademark` stub on that response
carries `trademark_role` too. The MCP tools `get_trademark_proceedings` and
`list_proceedings` take the same filter.

Two consequences to plan for. A mark's proceedings list now includes cases
where the mark was asserted, not only cases against it; pass
`trademark_role=contested` for the filed-against view. And one proceeding
returns one item per linked mark, so a search by `proceeding_number` can
return several rows that differ only in `trademark_id` and `trademark_role`.

On the trademark detail, `proceedings_count` is now an object with the same
split, `{ "contested": 1, "asserted": 40 }`, instead of a single integer. Both
keys are always present. A single total would have hidden which side a mark
is on, which is the question a clearance memo is asking.

`trademark_role` is distinct from `parties[].role` and the `party_role`
filter, which describe the named parties (opponent, petitioner, respondent),
not the marks. Existing rows read as `contested`. The asserted side is
populated for USPTO from the TTAB bulk feed; other offices link the opposed
mark only.

## September 21, 2026: One row per TTAB opposition on US marks

**A US mark opposed or petitioned for cancellation no longer shows two rows for
the same proceeding.** The USPTO bulk feed carries only a flag for a pending
opposition or cancellation, and we stored it as a numberless `pending` row.
When the TTAB docket for the same mark arrived with its proceeding number and
parties, both rows stayed. `GET /v1/trademarks/{id}/proceedings` and
`GET /v1/proceedings?trademark_id=` returned the pair, and
`proceedings_count` on the detail counted both.

The flag row is now dropped as soon as a numbered proceeding of the same type
exists on the record, and it is not written again while the flag stays set. A
mark whose docket we have not matched yet still shows the flag row, since it is
the only signal until then. The existing pairs in production are removed in
the same release.

## September 18, 2026: TTAB proceedings, and how to read them

**Trademark Trial and Appeal Board proceedings have been in the API since
March.** Oppositions, cancellations, ex parte appeals, concurrent-use cases and
extensions of time to oppose are ingested daily from the USPTO's TTAB bulk
feed, with rendered decisions from the TTAB Reading Room and per-proceeding
detail from TTABVUE. Today that is about 85,000 US proceeding rows on 69,000
marks, refreshed every morning, alongside opposition records from EUIPO,
CIPO, IP Australia, IPOS and the Spanish office. This entry exists because the
feature shipped without one.

Three endpoints serve it, all on `trademarks:read`:

* `GET /v1/trademarks/{id}/proceedings` lists the proceedings a mark is party
  to, each with `parties[]`.
* `GET /v1/proceedings` searches across marks. One filter is required:
  `trademark_id`, `proceeding_type`, `status`, `q` (a party name),
  `party_owner_id`, `party_entity_id`, `party_role`, `office_code`,
  `contested_class` or a `filed_date` / `decision_date` range. Aggregations
  cover `outcome`, `party_role`, `nice_class`, `office_code` and
  `filed_year`.
* `GET /v1/proceedings/{id}` returns one proceeding with its parties and the
  mark it belongs to.

Each proceeding carries `proceeding_type`, `proceeding_number` (the TTAB case
number, for example `91278456`), `status`, `outcome`, `filed_date`,
`decision_date`, `decision_outcome` (the Board's own wording) and
`duration_days`. On the trademark detail, `proceedings_count` says how many
there are on each side, and `has_proceedings=true` narrows a search or list to
marks with at least one. The [opposition tracking
guide](https://docs.signa.so/guides/use-cases/opposition-tracking.md) walks through a monitoring
setup.

Four limits to know before you build on it:

* **A proceeding is attached to the mark it was filed against.** An opposer's
  own marks do not list the oppositions that opposer filed. To profile an
  opposer, search by party instead: `GET /v1/proceedings?q=<name>` or
  `party_owner_id=<own_…>` (correction, October 1, 2026: `party_owner_id`
  returns no results until parties are linked to owners; use `q`). Decided cases from the Reading Room are attached
  to every party's marks.
* **Many `filed_date` values are null.** We trust only the filing date TTABVUE
  states for the proceeding; the bulk feed's dates proved unreliable, so rows
  we have not yet confirmed carry no date and no `duration_days`. The default
  sort is `-filed_date`, so those rows come last.
* **Parties are names.** `parties[].name` is the name as filed, and
  `owner_id` is set only where it resolved to a known owner.
* **There is no filing-by-filing docket, and no events or webhooks fire on a
  proceeding change.** Poll the list, or watch the mark's `status.challenges`
  for `opposition_pending` and `cancellation_pending`.

To look up a proceeding by USPTO serial number, resolve the mark first:
`GET /v1/trademarks?application_number=90839902&offices=US`, then call
its proceedings list. `POST /v1/trademarks/batch` accepts many serials at
once.

## September 14, 2026: Consistent trademark detail validators

Trademark detail responses now label their `ETag` as weak (`W/"…"`) on both
`200 OK` and `304 Not Modified`. The validator tracks equivalent trademark
content; the per-request `request_id` means two otherwise unchanged responses
can contain different bytes. This also gives edge-compressed and identity
responses the same semantic validator without guessing the selected encoding.

Send the full returned value in `If-None-Match`. Previously issued strong
validators still match unchanged resources, and the response returns the weak
form. Projections, language, derived data and citation counts continue to
participate in cache invalidation. See [Caching & ETags](https://docs.signa.so/guides/caching.md).

## September 14, 2026: Renewed marks stop showing the paid renewal cycle on US §8/§9 and MX renewals

**A registration whose register expiry has already moved to the next term no
longer carries its previous renewal as a live deadline, on every rule that ends
a term.** The maintenance engine has always dropped a renewal cycle that the
office's own stated expiry says was paid, but it recognised only the plain
`renewal` and `international_renewal` rules. Two rules end a term under a
different name and were missed:

* **US Section 8 plus Section 9** (`combined_renewal_and_use`). A mark
  registered in 2016 and renewed through 2036 still listed the satisfied 2026
  cycle, in grace and apparently actionable, for the six months after that date.
* **Mexico's combined renewal and declaration of use** (`mx_renewal`, also
  `combined_renewal_and_use`, national route). A renewed Mexican registration
  lost its paid cycle the same way. Madrid designations of Mexico renew
  centrally at WIPO under `renewal` and were never affected.

The suppression now covers every term-end rule, so the paid cycle disappears
from `derived.deadlines` on `GET /v1/trademarks/{id}`, from `GET /v1/deadlines`,
from the portfolio deadline list and its iCalendar view, and from
`POST /v1/deadlines/compute`. `POST /v1/reconcile` already scored the stated
cycle rather than the paid one, so the two now name the same occurrence instead
of disagreeing.

**Rows typed `renewal` and `international_renewal` are unchanged.** They were
already inside the suppression, and their output is byte for byte what it was.
Section 8 declarations on their own, Section 15 affidavits and every other
non-renewal row are untouched too, since they are filings inside a term rather
than the term end. A mark that has **not** been renewed still shows its open
cycle for the whole window and grace period, because the register expiry is
that cycle's own due date and there is nothing to suppress. And
`POST /v1/deadlines/compute` with `include_missed: true` still returns the full
history, including the paid cycle, which is what a docket audit needs.

**One thing to check in your integration.** Every deadline surface computes
over a finite horizon: ten years for `derived.deadlines` on trademark detail,
five for `GET /v1/deadlines`, for the portfolio deadline list and for its
iCalendar feed, and whatever you ask for on `POST /v1/deadlines/compute`. For a
mark renewed within the last few months, the paid cycle is now gone and the next
term end can sit beyond that horizon, so any of those can come back empty where
they previously held one row. A mark registered 1996-06-01 and renewed through
2036-06-01, read on 2026-05-01, is the shape: the paid 2026 cycle is suppressed,
and the 2036 cycle falls on 2036-06-02 after the weekend roll, thirty two days
past the ten year horizon, and roughly five years past the five year one. It is
a well formed empty array, not a missing field, and the mark's own `expiry_date`
still carries the register's stated term end. Read the array rather than
assuming it has at least one entry.

**A register value we cannot place on the statutory ladder no longer suppresses
a US or Mexican combined cycle.** A stated expiry has to fall within about a
month of a real cycle on the rule's ladder for us to treat it as evidence that
the cycle before it was paid. On `combined_renewal_and_use` (US Section 8 plus
Section 9, and the Mexican renewal), that recognition is the whole test: a
register carrying a term end the rules cannot produce, for example fifteen years
after registration on a ten year ladder, used to delete the mark's real, live
obligation from the schedule, and now leaves it alone. Those marks show the
cycle actually in force, and `POST /v1/reconcile` reports the register value as
differing from the computed one.

**Rows typed `renewal` and `international_renewal` behave differently here, and
deliberately so.** For that family an office-stated renewal date is
authoritative, so the engine rebuilds the rule's cycle series through it rather
than measuring it against the statutory ladder: an Indian mark whose priority
date no register field carries legitimately sits months off that ladder. Taking
the office at its word means every cycle before the stated date is still treated
as paid, whether or not the date lands near a computed cycle. A register value
far enough out that the schedule cannot reach it therefore yields an empty
forward schedule on those rules, and `POST /v1/reconcile` reports the renewal
field with a `computed_value` of `null` and a `computed_unavailable_reason` of
`insufficient_data`, rather than a disagreement. The verdict on that field is
then scored against the register alone: `agrees` when your docketed date
matches the register, `caller_differs` when it does not. (`insufficient_data`
is the verdict itself only when the register carries no date either.) That is
unchanged from before this release.

## September 14, 2026: Docs and spec alignment: rule catalogs join Filing & docketing, alpha labels, monitoring status reference

**The rule catalogs now live next to the endpoints that use them.**
`GET /v1/deadline-rules` is tagged `Deadlines` and `GET /v1/opposition-rules`
is tagged `Oppositions` in the OpenAPI spec, both inside the Filing & docketing
tag group, and their reference pages sit under Filing & docketing, Deadlines,
after the compute pages. Nothing about the endpoints changed: same paths, same
responses, same free reference pool. Only the grouping moved, so a client
generated from the spec files the catalogs with the deadline and opposition
workflows instead of a separate Reference Data section. If your generator
names client classes after tags (openapi-generator, NSwag), the two catalog
operations move from the Reference Data class to the Deadlines and Oppositions
classes; regenerate and update those two call sites.

**`GET /v1/monitoring/status` has a reference page.** The endpoint had a guide
but no API-reference entry. The new page, under Monitoring, Watches, documents
the two halves of the response: `watches` is your organization's rollup by
health state, and `offices[]` is Signa's global per-office ingestion freshness,
identical for every organization. It also states plainly that `slo_hours` is
Signa's internal freshness target for computing `pipeline_status`, not a
published SLA.

**Screening, compare, listing checks and image search are labelled alpha.**
These endpoints run on internal and design-partner keys during internal
testing and their request and response shapes can still change. The docs
pages and spec descriptions now say alpha rather than beta. Email
[support@signa.so](mailto:support@signa.so) to join the early access list.

**Analytics navigation is simpler.** Market, office and classification
analytics are grouped under one Filing Analytics heading; party analytics stay
their own group. The rate-limits page inventory lists only published
endpoints.

## September 11, 2026: Reconcile becomes a docket audit (`verdict_detail`)

**`POST /v1/reconcile` gains an opt-in three-source verdict.** Send
`verdict_detail: true` and every found item adds `field_verdicts[]` (your
value vs the register vs the rulebook-computed schedule, with a seven-value
verdict and full rule provenance), a `computed` context block and a `lookup`
identifier echo. A new `your_fields.docketed_deadlines[]` input lets you audit
the maintenance deadlines you have on docket against the computed schedule.
See [Three-source verdicts](https://docs.signa.so/api-reference/reference/reconcile.md#three-source-verdicts-verdict-detail).

Entirely additive and off by default: without the flag, responses are
byte-identical to before, and with the flag, `fields[]`, `result` and
`mismatch_count` keep their exact v1 values. Billing is unchanged either way
(1 unit per item). One error-path nuance: sending `docketed_deadlines` without
the flag is a 400 `validation_error` whose message now names the flag
requirement instead of the generic unknown-key rejection.

## September 11, 2026: Fee endpoints accept platform office codes

**`GET /v1/fees` and `POST /v1/fees/estimate` now take the office spellings the
rest of the API takes.** `office`, and each entry in `designations[]`, used to
accept only the WIPO ST.3 codes `WO`, `US`, `EM` and `GB`, plus `WIPO` as an
alias for `WO`. They now also accept the platform office codes `USPTO`, `EUIPO`
and `UKIPO`, and the permanent `EU` alias for `EM`, in any case, exactly as
`GET /v1/trademarks?offices=` does. A request that used to come back with an
empty catalog or an `unsupported_office` line only because of how the office was
spelled is now priced.

The change is additive, and it is resolution only. Responses echo the canonical
ST.3 code, so `{"office": "USPTO"}` returns `"office": "US"`. No amount,
currency or catalog row changes for any request that was already priced, and an
office with no verified schedule still returns an empty catalog or an explicit
`unsupported_office` line rather than a guessed amount. Two spellings of one
designated office now collapse to one code before the uniqueness check, so
`designations: ["EM", "EU"]` is rejected as a duplicate instead of being counted
twice.

`dataset_version` on `POST /v1/fees/estimate` changes with this release because
it covers estimator semantics as well as the fee rows. The rows themselves are
untouched.

## September 6, 2026: Portfolios are public

**Portfolios are now a documented resource family.** The nine portfolio
operations (create, list, retrieve, update, delete, add and remove trademarks,
and the JSON and iCalendar deadline views) appear in `GET /v1/openapi.json`
under a `Portfolios` tag, grouped under "Filing & docketing", with
[reference pages](https://docs.signa.so/api-reference/portfolios/create.md) and a
[portfolios guide](https://docs.signa.so/guides/portfolios.md). Nothing about the endpoints themselves
changed: same paths, same `portfolios:manage` scope, same `read` metering
(1 unit per request, including writes and calendar downloads), same plan gating.
The catalog operations `GET /v1/classifications`,
`GET /v1/classifications/{classNumber}` and `GET /v1/goods-services` also move
from the `Reference Data` tag to `Classification`, which is where the rest of
the classification surface already lives. Tags are documentation grouping only;
no path, parameter or response shape changed.

Three error types that portfolios return are now in the
[error reference](https://docs.signa.so/api-reference/errors.md): 409 `resource_quota_exceeded`,
409 `portfolio_in_use` and 422 `portfolio_too_large`.

**The TypeScript SDK's generated types caught up with the published spec.**
`@signa-so/sdk` 0.12.0 regenerates `api-types` from the same spec build the
published document uses. That adds 37 operations and 21 schemas, and removes
nothing. Nine operations and two schemas are the portfolios surface. The other
28 operations and 19 schemas (watches, alerts, webhooks, events, monitoring
status) were already published in `GET /v1/openapi.json` but were missing from
the generated types, because SDK codegen ran against a narrower build of the
spec than the one we publish. No existing operation or type changed, so the
update is additive for every consumer.

## September 5, 2026: Auth hardening, `plan_gated`, prefixed watch filters

Three visible changes from the auth and tenancy hardening pass (the first and
third have been live since 2 September; the watch-filter change lands today).
None of them alter a successful response.

**`403 plan_gated` replaces a misleading `429`.** Calling a capability that is
not on your plan at all (for example image search on `free`) used to return
`429 rate_limited` with `retry_after: 0`, which sent SDK retry loops spinning.
It now returns `403 plan_gated` with `endpoint_type` naming the capability and
`retryable: false`. See the [error reference](https://docs.signa.so/api-reference/errors.md).

**Watch filters echo prefixed IDs.** `query.filters.trademarkIds`, `ownerId`,
`attorneyId` and `firmId` on every watch response (REST and MCP) now come back
as `tm_*` / `own_*` / `att_*` / `firm_*`, whichever form you sent. Input still
accepts both, so nothing you send changes.

**Stricter bearer parsing, clearer outages.** `Authorization` is single-valued
(RFC 7235): a comma-joined pair of credentials is a `400`. The scheme is
case-insensitive. A missing header gets a bare `WWW-Authenticate: Bearer`
challenge with no error code (RFC 6750 §3.1). When Signa's own token verifier
or JWKS is unreachable, MCP requests get a retryable `503 service_unavailable`
instead of being told the token is invalid.

## September 4, 2026: nine deadline rules relabelled, and `consequence_if_missed` is an open string

**Nine maintenance rules changed the `consequence_if_missed` string they
serve.** No due date moved, no window opened or closed differently, and no rule
was added or removed. Only the label changed: each of these now names the
mechanism the office's own pinned provision prescribes. The rules are
`bx_renewal` and `mx_declaration_of_use_3yr` (`irrevocable_loss` to
`expiration`), `ch_renewal` (`irrevocable_loss` to `removal_from_register`),
`de_renewal`, `dk_renewal` and `eu_renewal` (`cancellation` to
`removal_from_register`), `fr_renewal` and `pl_renewal` (`cancellation` to
`expiration`), and `th_renewal` (`removal_from_register` to `expiration`).

This shipped with no deprecation window, deliberately. `consequence_if_missed`
is an open string and not an enum: the documented vocabulary is the set of
values in use today, and Signa edits a rule's wording whenever a better reading
of the primary source arrives. Branch on `rule_id`, which is stable and
permanent, or on the structured fields that carry the arithmetic, and treat this
field as display text. The full before-and-after table is in the
[deadline rules guide](https://docs.signa.so/guides/deadline-rules.md#consequence-relabels-4-september-2026).

## September 3, 2026: prosecution deadlines are served outside calendar coverage

**A prosecution deadline is no longer withheld because its office calendar
cannot check the date.** Bounding `euipo_alicante` to the two Executive
Director decisions actually pinned (2025 and 2026) had a consequence we did
not intend: every EU office action mailed before 2025, and every one mailed
from 1 November 2026 onward (a two-month period lands in 2027), came back
`insufficient_facts` with reason `holiday_calendar_out_of_coverage` and no
date at all. The same held for US office actions before 1985. Silence on a
deadline whose miss abandons the application is the worst possible answer.

Those cases now return `outcome: "computed"` with the raw statutory
anniversary. An office action mailed 10 January 2024 at the EUIPO reports
`due_date: "2024-03-10"`; one mailed 1 November 2026 reports
`"2027-01-01"`. This matches what the renewal engine
(`due_date_adjustment`) and the opposition engine (`close_adjustment`) have
always done. An un-rolled date is never LATER than the true deadline, so it
can be early but cannot cause a missed one.

**New field: `due_date_adjustment` on every prosecution deadline.** It tells
you which kind of date you are holding, using the same vocabulary as the
renewal rows:

* `moved`: the office calendar was consulted and rolled the date forward off
  a day the office is shut.
* `unchanged`: the calendar was consulted and the date was already an open
  day.
* `not_checked`: no calendar verdict stands behind the date. Either the date
  falls outside the calendar's pinned coverage, the rule ships without a roll
  rule, or the office stated the date itself (a stated deadline is published
  exactly as stated and never rolled). Treat it as the raw statutory
  anniversary and confirm before relying on the exact day.
* `null`: no date was produced at all.

`holiday_calendar_out_of_coverage` remains in the reason vocabulary but is now
a backstop for a calendar-integrity failure rather than the ordinary
out-of-coverage answer. No shipped rule reaches it today.

**`close_adjustment` on a trademark summary row is no longer nullable.** The
same marker was declared nullable on the summary row's `opposition_window` and
non-nullable on the detail tier's `derived.opposition_window`. It can never be
absent while its parent object exists, so the two now agree and the
nullability sits where it belongs, on `opposition_window` itself. If you
null-checked the field, that check is now dead code; nothing you were reading
changes value.

**Search and opposition-compute deliberately disagree for 20 days after an
un-rolled close.** Where `close_adjustment` is `not_checked` and the close is
within the last 20 days, search buckets the mark as **open** (a rollover could
only have pushed the close later) while `POST /v1/oppositions/compute` reports
`closed` against the un-rolled date. Search's reading is the safe one. Search
documents in that band also now carry the un-rolled close rather than no
opposition window at all; a full reindex populates them.

## September 3, 2026: France, Finland and Poland Madrid designations get opposition windows

**Three new rules.** An international registration designating France, Finland
or Poland had no opposition window at all: `POST /v1/oppositions/compute`, the
`derived` block, `GET /v1/opposition-rules` and watch diagnostics all returned
nothing for those rows. `fr_opposition_madrid`, `fi_opposition_madrid` and
`pl_opposition_madrid` now answer.

**France, two months from the WIPO Gazette.** CPI art. R. 717-1 applies the
whole domestic opposition procedure to an international registration extended
to France, and art. R. 717-5 names the publication the art. L. 712-4 two
months run from: WIPO's Gazette, not any INPI republication. There is no
first-of-the-following-month offset, unlike Switzerland and Germany. INPI's own
directives work the case: an IR published in Gazette 2019/23 of 20 June 2019
has an opposition period expiring 20 August 2019. Every other mechanic is the
domestic one by statute rather than by copy, including the art. R. 718-2
same-numbered-day arithmetic, the roll off weekends, jours fériés and INPI
closure days, and the extra month for the exposé des moyens.

One documented limit, and it is a hard gate. The rule reports
`effective_from: "2009-01-01"` and `applicable_from_date: "2009-01-01"`. The
version of art. R. 717-5 in force before that date ran the period from the
first day of the month after INPI received the Gazette, an internal office
event that no register record carries. We decline that era rather than guess
it, so a French designation published before 2009 comes back
`supported: false` with `unsupported_reason: "unsupported_office"` and the
rule identity intact, `effective_from` naming the boundary.

**Finland, two months from PRH's own publication.** Tavaramerkkilaki 544/2019
92 § 5 mom. has PRH publish the international registration in the same words
24 § uses for a national one, and 88 § 2 mom. applies the Act to an IR except
where the Madrid chapter says otherwise, which it does not for opposition. So
32 §'s two months from the publication day carry over, with the 150/1930
month-end clamp and the roll off a pyhäpäivä or a working Saturday. PRH's own
gazette confirms it: record W202600336 is an international registration with
`oppositionPeriodStartDate` equal to its publication date.

**Poland, three months from the BUP announcement.** PWP art. 152^1a ust. 1 has
UPRP announce a designation of Poland in the Biuletyn Urzędu Patentowego
without delay, and art. 152^6a ust. 1 gives an earlier-right holder three
months from that announcement, non-restorable. The Biuletyn states the same
sentence for both routes. The window opens the day after publication and closes
on the same-numbered day three months later under KPA art. 57 § 3, rolling off
statutory non-working days and Saturdays under § 4. The rule reports
`effective_from: "2016-04-15"`, the commencement of art. 152^6a: before that
date there was no pre-grant opposition against a designation of Poland at all.

**Which publication your date is.** All three rules declare it, so the
`publication_date_kind` guard applies. FR needs `wipo_gazette`; FI and PL need
`office_journal`. Send the other one and the response is `status: "unknown"`
with null dates and the rule's identity intact, rather than a date you should
not act on. Omit the field and nothing changes.

**Reindex required after the backfill.** These windows appear on documents that
carry none today, and `opposition_window_opens` / `opposition_window_closes`
are baked into search documents at index time. Request-time surfaces
(`POST /v1/oppositions/compute`, the `derived` block, the SDK) are correct the
moment this deploys. The `opposition_status` and `opposition_closes_after`
filters will not see the new windows until French, Finnish and Polish Madrid
designations carry an office publication date from the designation-publication
backfill and are reindexed. No window we already serve MOVES: no publication
date is reinterpreted and no existing pair of dates changes.

**One thing that does change shape: `opposition_window` stops being null for
WIPO-feed legs.** A designation leg that reaches us through the WIPO feed
carries `office_code = "wipo"` with the designated country in
`jurisdiction_code`, which never matched a rule keyed on the designated
office's code. Those legs now resolve by jurisdiction, so for EVERY Madrid
jurisdiction we model (not only the three new ones), `derived.opposition_window`
turns from `null` into an object. Until the leg carries an office publication
date the object's dates are all null and `status` is `"unknown"`, and
`opposition_rule_version` in search documents becomes non-null after the next
reindex where it was null before. If your integration reads
`opposition_window === null` as "Signa does not model opposition for this
mark", that inference no longer holds: check `status` and `window_closes`
instead. A null `opposition_window` now means only that no rule matched the
office, route and publication era at all.

**No United Kingdom rule yet.** A UK Madrid designation still returns no
window. UKIPO runs its three months from the UK Trade Marks Journal, and the
current Journal source is not available to us to pin, so the rule is held
rather than written from an unverifiable source.

## September 3, 2026: holiday calendars now stop where their evidence stops

**Some deadline dates move EARLIER, and more of them report
`not_checked`.** Every office holiday calendar Signa ships now advertises
coverage only as far as the instruments actually pinned behind it. Eight of
them are built from a document the office issues once a year: a decision, a
notice, an acuerdo, a proclamation, an office memorandum. Those now stop
at the last one pinned instead of projecting forward:

| Calendar | Covered through | The instrument |
| - | - | - |
| `euipo_alicante` | 2025-01-01 to 2026-12-31 | EUIPO Executive Director decisions EX-24-9 and EX-25-16 |
| `mx_mexico` | 2026-11-30 | IMPI *Acuerdo de días inhábiles*, DOF 5769863 |
| `dk_denmark` | 2026-12-31 | DKPTO *lukkedage* |
| `fr_france` | 2026-12-31 | INPI Director-General closure decision |
| `ph_philippines` | 2026-12-31 | Presidential holiday proclamations |
| `cnipa_china` | 2026-12-31 | 国务院办公厅 annual notice |
| `cgpdtm_india` | 2026-12-31 | DoPT holiday Office Memorandum |
| `wipo_geneva` | 2026-12-31 | WIPO Information Notice 40/2025 |

Beyond those horizons nothing is guessed. A renewal or grace date comes back
UNROLLED with `due_date_adjustment` / `grace_expiry_adjustment` set to
`"not_checked"`, and an opposition window is served with its close on the
exact statutory day. Unrolled is never LATER than the true date, so a date we
serve can be early but cannot cause a missed deadline. The calendars that are
genuinely rule-computed (US, Canada, Germany, Australia, Brazil, Benelux,
Switzerland, Finland, UK, Iceland, Norway, Poland, Sweden) still run to 2100,
and the lunar ones (Singapore, Thailand, Vietnam, Japan, Korea, Türkiye) to
2030, unchanged.

**EU: the invented years are gone, and EU dates in 2024 and earlier are no
longer rolled.** *Superseded September 23, 2026: 2023 and 2024 now roll against the EUIPO decisions for those years.* `euipo_alicante` carried closure days for 2024 and for
2027-2030 that no Executive Director decision states. The 2027-2030 entries
were labelled "estimate" and moved deadlines LATER: a day after Ascension,
in-lieu Mondays, 4 January 2027, 6 December in four different years, and the
two decisions that ARE pinned falsify the pattern they were derived from
(EX-24-9 closes for Ascension, Schuman Day and the Assumption; EX-25-16 closes
for none of the three and adds Santa Faz, 12 October and 2 November). The 2024
block said "per prior EX decision" and no such decision exists in our evidence.
All of it is deleted. Consequences, both directions:

* **EU opposition closes for 2027 and later are no longer rolled.** A window
  whose close lands on a weekend or a 2027 Spanish feast is now served on the
  raw statutory date. Example: an EU application published 30 November 2026
  closes **28 February 2027** rather than 1 March 2027.
* **EU dates before 2025 are outside the calendar.** An EU office-action
  response deadline computed from a 2024 mailing is no longer rolled against
  the unpinned 2024 block. *Superseded the same day:* this originally returned
  `insufficient_facts` with reason `holiday_calendar_out_of_coverage`. It now
  returns the un-rolled statutory date with
  `due_date_adjustment: "not_checked"`. See the entry above.

**Mexico: December of the last pinned year is honest about being unknown.**
IMPI declares each year-end shutdown in the FOLLOWING year's acuerdo, so the
acuerdo for 2026 states every día inhábil of 2026 except the December closure.
`mx_mexico` therefore stops on 30 November 2026. Before this change, an MX
opposition window closing 22 December 2026 was served as a working day and
reported `closed` on 24 December, while IMPI was in fact shut. It now reports
`window_not_computable` until the acuerdo covering December 2026 is published.

**A roll can no longer walk off the end of a calendar.** The coverage check ran
only on the date being rolled, not on the days the roll walked through. A
deadline on a listed year-end closure could therefore step into the following,
uncovered year, where every day looks open, and stop on the first of January.
WIPO 31 December 2026 reported 1 January 2027 as `moved`; DKPTO 28 December
2026 reported 4 January 2027. Both now report `not_checked` and serve the
unrolled date.

**Mexico closes on 25 December.** `mx_mexico` was missing Christmas Day from
its recurring días inhábiles, although the one pinned acuerdo that lists a
whole year without folding December into a closure block (DOF 5580658) ends
«DICIEMBRE Viernes 25». MX deadlines falling on 25 December now roll to the
next working day, LATER by one to four days, in every covered year.

**Iceland: no change, and now only one story about why.** 24 and 31 December
stay closed on `is_iceland`, on Hugverkastofan's own December 2020 notice that
the office is shut all day and that deadlines falling there move to the next
opening day. Five documents still described these as open days under a
superseded reading; they now all state the ruling that shipped. Þorláksmessa
(23 December) is declared year by year and stays OPEN. No date moves.

**Reindex needed for EU and MX opposition windows.** Opposition windows are
baked into search documents at index time. EU documents whose window closes in
2027 or later, and MX documents whose window closes in December 2026, carry the
old rolled close until the affected records are reindexed. Request-time
surfaces (`/v1/oppositions/compute`, `/v1/deadlines/compute`, the `derived`
block) are correct on deploy. DK, FR, PH and IN opposition windows move only
for close dates past their horizons; reindex those offices with the same pass.

## September 3, 2026: `close_adjustment` on every opposition window

**New field: was this close date checked against a holiday calendar?**
`close_adjustment` now appears on `POST /v1/oppositions/compute`, on the
`derived.opposition_window` block of a trademark, on the `opposition_window`
of every trademark summary row and on the `opposition` block of
`GET /v1/watches/{id}/diagnostics`. It reads `moved` (the close fell on a day
the office is shut and was rolled forward), `unchanged` (the calendar was
consulted and the close was already a business day) or `not_checked`, meaning **no
calendar stands behind this date**.

`not_checked` is not new behaviour, only newly visible. Several office
calendars are built from an annual notice (China's State Council holiday
notice, India's DoPT memorandum, the Philippine proclamations, Mexico's IMPI
acuerdos, WIPO's non-working-days notice), so a window closing in a year whose
notice is not yet published has always been served with raw statutory
arithmetic. A rollover can only move a close **later**, so an un-rolled close
is correct-or-early: safe to act on, never a reason to file late. Until today
nothing on the wire told the two apart.

**The `opposition_status` search filter now treats an un-rolled close as
indeterminate.** For the 20 days after a `not_checked` close date, a row
counts as `open` and is excluded from `closed`, because the rollover that
could not be applied may still be holding the window open. `not_started` and
`unknown` are unchanged, because the opening date is never rolled.

**Reindex required to populate.** The marker is baked into the search index at
index time (`opposition_close_adjustment`, an additive keyword field, so no
alias swap, no new index generation). Documents indexed before this release
carry no value and are bucketed by their dates alone, exactly as they were
before; the indeterminate band applies to them only after a reindex. Every
request-time surface (`/v1/oppositions/compute`, the `derived` block, watch
diagnostics) recomputes and is correct immediately.

**Also:** `POST /v1/oppositions/compute` declines with
`window_not_computable` in fewer cases. A close falling exactly ON the date
you compute as of is now published un-rolled instead of declined: a rollover
can only push it later, so the window is open either way. The narrow band that
still declines is a close strictly 1–20 days before the as-of date.

## September 3, 2026: opposition rule catalog reports both eras

**Five new fields on `GET /v1/opposition-rules`** (and the MCP
`get_opposition_rules`): `window_change_date`, `window_starts_on_before`,
`window_months_before`, `window_days_before` and `window_ends_on_before`. An
opposition rule models a regime change inside itself, and the catalog used to
serve only the current era, so a client reproducing a close date for a
publication before the pivot got the wrong length, the wrong start convention,
or both, with nothing on the wire to say so. A publication **strictly before**
`window_change_date` is governed by the `_before` fields. All five are `null`
on a rule that has never changed. (A previous entry said era behaviour was
already reported through `window_starts_on` / `window_ends_on`. It was not;
this is the change that makes it true.)

**One existing value changes: `window_ends_on` is now era-scoped.**
`bx_opposition` opens on the first of the following month only in its
pre-1 October 2013 era, so its `corresponding_day_of_start` describes THAT
era. It now reads `window_ends_on: null` with
`window_ends_on_before: "corresponding_day_of_start"` instead of claiming the
current era ends that way. The current BX era anchors the close on the
publication day-number and never reads the field. BX is the only affected
rule; no computed date moved.

## September 3, 2026: rule corrections (CN, PH, CH, CA, DE, MX)

**China: the 2027 straddle is served the SHORTER window.** The 2026 revision
of the Trademark Law cuts the opposition period from three months to two on
1 January 2027 and carries no transitional provision for a period still
running on that day: art. 87(2) speaks only of registered marks. The two
readings differ by up to a month, so Signa serves the shorter one to the
straddling cohort: **every CN publication from 1 October 2026** (the first
whose three months would still be open at commencement) is now given two
months. A mark published 31 December 2026 closes **28 February 2027**, not
31 March 2027. A close published too early costs a would-be opponent nothing
they can lose by relying on it; one published too late costs the right itself.
The call is reversible and will flip the day a transitional notice or the
revised Implementing Regulations settles the question. *(This supersedes the
"Applications published in November and December 2026 keep three months"
sentence in the CNIPA entry below.)* **Reindex:** CN opposition windows are
index-baked, but CNIPA is a planned office with no indexed records, so there
is nothing stale.

**Philippines: the advisory extension is era-aware.** IPOPHL MC 2022-013
introduced the once-only 45-day extension on 30 June 2022. A window that
closed BEFORE that date is now served the earlier regime instead: three
30-day extensions on motion, capped at 120 days from publication (MC 16-007
§ 12). Display-only: `common_extension` never affects `window_closes`.

**Switzerland (Madrid): the served citation is the operative one.** A CH
Madrid window used to cite MSchG art. 31(2), whose words run the period "after
the publication of the registration", the national trigger, which this rule
never uses. It now cites MSchV art. 50(1), the provision that actually fixes
day zero at the first of the month following the WIPO Gazette issue. No date
moved; `source` on the response and `sources[0]` in the catalog change.

**Version stamps.** `ca_opposition`, `bx_opposition`, `ch_opposition_madrid`
and `ph_opposition` report a new `last_verified` / `rule_version` of
2026-09-03. BX and CH had moved served values in an earlier release without
moving the stamp. Canada's two `common_extension` notes also now name the
CIPO practice notice section in full ("section X.1, Extensions of time -
benchmarks") instead of an abbreviation that read as an unfilled placeholder.
No computed date moved for CA, CH, BX or DE.

## September 3, 2026: maintenance deadline corrections (EU, MX, NO, CA)

Five corrections to `POST /v1/deadlines/compute`, the `derived` block on a
trademark, and portfolio deadline rows. Maintenance deadlines are computed at
request time, so every change below is live the moment it deploys, so **no
reindex is needed and no stored date changes**.

**A missed EU renewal's restitutio date moves earlier, by up to two weeks.**
EUTMR Article 104(2) admits an application for re-establishment "within the
year immediately following the expiry of the unobserved time limit", less the
Article 53(3) further six months, so the long stop is the renewal expiry plus
twelve months. Signa counted those months from the grace expiry **as served**,
which had already absorbed one Article 69(1) working-day extension, and then
extended the sum again. A EUTM filed 21 December 2014 was told its restitutio
window ran to 5 January 2026; it runs to **22 December 2025**. EUIPO Guidelines
Part E §7.4.2 states the rule the fix implements: the extension "applies only
once and to the end of the basic period". The window still OPENS on the grace
expiry as served. The same single-extension arithmetic now applies to every
restoration window (EU, GB, IN, IS, JP, SG); where the grace expiry did not
move, nothing changes.

**A Mexican Madrid designation now renews on WIPO's calendar.** The renewal of
an international registration designating Mexico is paid to WIPO's
International Bureau under Madrid Protocol Article 7 (IMPI never receives it),
so Reglamento LPI art. 4o does not reach it. An IR registered 29 February 2004
was given a Mexican due date of 1 March 2014 under art. 4o's month-end
overflow, one day **after** the international registration had actually lapsed;
it is now due **28 February 2014**, exactly matching the `WIPO` row for the
same registration, with `holiday_calendar: "wipo_geneva"`. Mexico's three-year
declaration of use is a Mexican filing on IMPI's own grant date and is
unaffected.

**Norwegian renewal due dates are no longer rolled.** Betalingsforskriften § 6
extends a *frist*, and the § 33 renewal request period is one, so its late end,
the grace expiry, still rolls to the next virkedag. The due date is not a
frist: it is the end of the registration period, and varemerkeloven § 33 første
ledd tredje punktum charges the tilleggsavgift for a request delivered "etter
utløpet av registreringsperioden". A due date served on the Monday would have
advertised a surcharge-free filing day after the period had already ended. A
Norwegian mark filed 1 May 2016 is due **1 May 2026**, not 4 May, and its
`due_date_adjustment` reads `not_checked`. Grace expiries are unchanged. This
is the same treatment Australia has had since the s 223A(1) wiring, and
`GET /v1/deadline-rules` now says so explicitly (below).

**An office-stated 29 February expiry no longer returns an empty schedule.** A
mark whose office-reported expiry fell on a leap day could return **zero**
deadlines: a live EUTM two months from expiry, with nothing to file against.
Three separate pieces of leap-day arithmetic compounded; all three are fixed,
and such a record now returns its renewal row on the office's own date.

**Canada: `last_renewal_date` is a new optional field on compute.** Trademarks
Regulations s. 159 opens the first post-17-June-2019 renewal window at the
period start, and it keys on the **renewal**, not on the cycle's due date. A
registration whose term ended shortly before that day but whose fee was paid in
the grace period on or after it is the s. 159 renewal; the one after it is an
ordinary s. 76 renewal. Send `last_renewal_date` on a compute item and Canada
uses it. Omit it and the previous due-date behaviour is unchanged.

**`GET /v1/deadline-rules` and the `get_deadline_rules` MCP tool gain
`grace_expiry_rolls_only`.** A boolean, present on every row. When `true`, the
jurisdiction rolls its grace expiry but never its renewal due date, which is
what Australia and Norway do. `weekend_holiday_rule: true` alone could not
express that, so a docketing system reading the catalog had no way to tell a
scoped roll from a full one. Additive: no existing field changed.

## September 3, 2026: China (CNIPA) deadlines and opposition windows

**New jurisdiction.** `POST /v1/deadlines/compute`, `POST /v1/oppositions/compute`,
the `derived` block on a trademark, `GET /v1/deadline-rules` and
`GET /v1/opposition-rules` now answer for Chinese marks. Three rules ship:
`cn_renewal`, `cn_opposition` and `cn_opposition_madrid`. `office: "CN"` is
accepted on the oppositions endpoint and comes back as `CN`.

**A Chinese term counts its first day.** Under Implementing Regulations
art. 12(2) a registration's ten years run from the day the office approves it
and INCLUDE that day, so the term ends the day BEFORE the anniversary: a mark
registered 7 March 2016 expires 6 March 2026, not 7 March. The 12-month
renewal window and the 6-month grace period are measured from that expiry.
Where the anniversary day does not exist in the target month, the expiry is
the last day of that month instead: a mark registered 29 February 2020
expires 28 February 2030.

**Opposition: three months now, two from 1 January 2027.** Trademark Law
art. 33 gives three months from the preliminary-approval publication
(初步审定公告, not the registration publication), counted from the day after
and ending on the same day-of-month three months later. The 2026 revision of
the Trademark Law cuts that to two months in its renumbered art. 36 and enters
into force on 1 January 2027 (art. 87). Both eras are modelled now, selected
by the publication date, and each window is served the article that states its
own length. Applications published in November and December 2026 keep three
months: the revision carries no transitional provision, and that is the
non-retroactive reading.

**Madrid designations of China use a different anchor.** CNIPA does not
republish an international registration, so the window runs off the WIPO
Gazette: Implementing Regulations art. 45(1) opens it on the FIRST DAY OF THE
MONTH FOLLOWING the Gazette issue and runs three months to the last day of the
third month, which is CNIPA's own worked example (Gazette 16 January 2012 →
1 February to 30 April 2012). Supply the Gazette issue date as the publication
date for these rows.

**Holiday calendar, and where it stops.** Chinese deadlines roll to the first
working day when they fall on a statutory holiday, and the calendar is built
from the State Council's annual holiday notice, including the make-up working
weekends (调休), where a Saturday or Sunday is declared a working day. A
renewal due on Friday 2 January 2026 is due on **Sunday 4 January 2026**, the
first working day, not Monday the 5th. Because each year's notice is published
only the preceding November, coverage runs 2024–2026: dates beyond it are
reported unrolled rather than rolled against a guess: renewals with
`due_date_adjustment: "not_checked"`, and an opposition window with its close
on the exact statutory day. Unrolled means EARLIER than the true date, never
later. The one exception is a window that closed within the last 20 days
beyond coverage, where a roll could still be holding it open: there the window
is returned `unsupported_reason: "window_not_computable"` with its rule
identity intact rather than risk labelling a live window closed. Coverage
extends when the 2027 notice is published.

**Reindex: none needed today.** Opposition windows are baked into search
documents at index time, so a new rule normally reaches already-indexed
documents only after a reindex. Here there is nothing to reindex: CNIPA is a
planned office and Signa holds no Chinese records in the search index yet, so
no stored value changes and no document can be stale. Request-time surfaces
(`/v1/deadlines/compute`, `/v1/oppositions/compute`, the `derived` block, the
rule catalogs) answer for China on deploy, and the index-time path will pick
the rules up with the first CNIPA ingestion. Should any Chinese records be
indexed before then, they would need a reindex to gain
`opposition_window_opens` / `opposition_window_closes`; nothing they carry
would be wrong.

## September 3, 2026: Benelux opposition windows before 1 October 2013 corrected

**Historical dates change for `bx_opposition`.** The two-month Benelux
opposition period has not always run from the publication day. Until
30 September 2013 BVIE art. 2.14(1) counted it "te rekenen vanaf de eerste dag
van de maand volgende op de publicatie van het depot", from the first day of
the month FOLLOWING publication. The Protocol of 22 July 2010 (art. I.D)
replaced those words with "te rekenen vanaf de publicatie", and it entered
into force on 1 October 2013 (Trb. 2013, 135).

Signa was applying the current text to every publication, however old. A
Benelux application published on 12 June 2013 was reported as open from
12 June to 12 August 2013 when the window the law gave ran from 1 July to
1 September 2013, a close up to 30 days EARLY. Only publications before
1 October 2013 are affected, every one of those windows is long closed, and no
`status` changes; what changes is the pair of dates we report about them,
which matters wherever a historical window is cited in a dispute or an audit
trail. The rule carries both the superseded article text and the amending
Protocol as pinned, era-bounded citations, so a pre-2013 window is served the
text that actually governed it. *(This entry originally said
`GET /v1/opposition-rules` reported the era behaviour through
`window_starts_on` / `window_ends_on`. It did not; those fields describe the
current era only. The era fields shipped on 3 September 2026; see the entry at
the top of this changelog. BX now reports `window_change_date: "2013-10-01"`,
`window_starts_on_before: "first_of_following_month"` and
`window_ends_on_before: "corresponding_day_of_start"`, with
`window_ends_on: null`.)*

Era selection keys on the publication date, which is the only date either text
can be read on. Neither Protocol carries a transitional provision. One
documented limit follows: for a mark published between 1 August and
30 September 2013 the old text's window was still running when the new text
took effect, and no source says which governs. We serve the old rule, and
therefore the later close, which is the conservative direction for anyone
deciding whether they may still oppose.

**Reindex.** Opposition windows are baked into search documents at index time,
so pre-2013 Benelux documents keep the old `opposition_window_opens` /
`opposition_window_closes` values until BOIP records are reindexed. Every
affected window is closed and none of them can change `opposition_status`, so
this is low priority and belongs with the next natural BX reindex rather than
with a dedicated run. Request-time surfaces are correct on deploy.

## September 3, 2026: German Madrid designations get an opposition window

**New rule `de_opposition_madrid`.** An international registration designating
Germany had no opposition window at all: `POST /v1/oppositions/compute`, the
`derived` block, `GET /v1/opposition-rules` and watch diagnostics all returned
nothing for a DPMA Madrid row. That was honest, because the rule could not
be expressed, but silent, and the deadline it hides is a hard statutory bar.

MarkenG § 114(1) puts the WIPO gazette in the place of the Markenblatt
publication of a national registration, and § 114(2) starts the § 42(1) three
months on the **first day of the month following** the gazette's issue month.
Because that start is the beginning of a day, BGB § 187(2) counts it and
§ 188(2) Alt. 2 ends the period the day before the corresponding day: the
last day of the third month. A gazette issued in March 2026 opens the window
on 1 April 2026 and closes it on 30 June 2026; an issue in November 2026 runs
1 December 2026 to 28 February 2027, which is a Sunday, so BGB § 193 and
DPMAV § 18a roll it to 1 March. The fee (250 EUR, plus 50 per further sign)
falls due inside the same period, and the window is not extendable.
MarkenG § 91(1) sentence 2 bars even Wiedereinsetzung.

Worth noting next to the Swiss correction in this same release: Germany and
Switzerland start their Madrid windows on the same day and close them one day
apart. MSchV art. 2 ends a Swiss period on the corresponding day, BGB § 188(2)
Alt. 2 ends a German one the day before. `window_ends_on` on
`GET /v1/opposition-rules` reports which reading each rule applies. No German
source gives a worked example of the end day, so it is derived from the two
BGB provisions and cross-checked against the DPMA's own Richtlinie W 7736,
which states the start in the office's voice; the derivation is recorded in
the evidence pack rather than asserted.

**Reindex.** Opposition windows are baked into search documents at index time,
and DE Madrid documents currently carry null windows, so the
`opposition_status` and `opposition_closes_before` / `opposition_closes_after`
filters will not see this rule until German Madrid designations are reindexed.
Request-time surfaces are correct on deploy. As for Switzerland, the reindex
only becomes meaningful once designation records carry the WIPO gazette
publication date the rule anchors on.

## September 3, 2026: Swiss Madrid opposition windows close up to 30 days earlier

**Computed dates change for `ch_opposition_madrid`, in the safe direction.**
The opposition window against an international registration designating
Switzerland does not run from the day the WIPO Gazette published the
designation. MSchV art. 50(1) starts it on the **first day of the month
following** the Gazette month, and MSchV art. 2 ends a month period on the
same-numbered day, so the window closes on the 1st of the fourth month. The
IGE publishes the worked example: a Gazette of 19 July 2022 opens the window
on 1 August 2022 and closes it on 1 November 2022.

Signa was computing "Gazette day plus one month" to "Gazette day plus four
months" instead. That is the same answer only when the Gazette date is itself
the 1st; for every other issue the close we published was up to 30 days
**later** than the law allows. A Gazette of 15 January 2026 was served a close
of 15 May 2026 where the true, non-extendable deadline is 1 May 2026. Later
than the truth is the dangerous direction on an opposition bar, so this is a
correction customers should re-docket: every Swiss Madrid close moves earlier
by nought to thirty days, and every open moves to the 1st of the month.

`GET /v1/opposition-rules` now reports `window_starts_on:
"first_of_following_month"` and `window_ends_on: "corresponding_day_of_start"`
for this rule, and `window_starts_offset_months` is `null`. Both enums are new
and additive; no existing value changed meaning. Trademark responses, the
oppositions compute endpoint, the `derived` block and watch evaluation
recompute the window per request, so they are correct as soon as this deploys.

**Reindex.** Opposition windows are baked into search documents at index time,
so `opposition_status` and the `opposition_closes_before` /
`opposition_closes_after` filters carry the old arithmetic until Swiss Madrid
documents are reindexed. In practice there is nothing to correct yet: a
designation is indexed as part of its international-registration family, and
IR-family documents carry null opposition windows today, while the Madrid
source we ingest publishes no per-designation publication date for the rule to
anchor on. The reindex therefore belongs with the work that starts storing the
WIPO Gazette publication date for designations, not with this change.

## September 3, 2026: Norway Madrid opposition, and a publication-date guard for every Madrid rule

**New rule.** `no_opposition_madrid` covers an international registration
designating Norway. Patentstyret publishes in Norsk varemerketidende that the
IR has effect in Norway (varemerkeloven § 70 annet ledd, varemerkeforskriften
§ 32), and § 71 første ledd gives anyone three months from THAT
kunngjøringsdag to oppose, applying § 26 correspondingly. So every mechanic of
the Norwegian domestic rule carries over by statute rather than by copy: the
count starts on the publication day itself, the three-month period ends on the
corresponding day-number, it rolls off 24 and 31 December, Saturdays,
helligdager and likestilte dager under betalingsforskriften § 6, and it cannot
be extended. There is no WIPO offset: § 71 runs straight from Patentstyret's
own publication, unlike EUIPO's Madrid-EU rule. The 18-month Madrid refusal
limit is an obligation the office owes WIPO and never changes the three months
the parties get. `GET /v1/opposition-rules`, `POST /v1/oppositions/compute`
(office `NO`, filing route `madrid`) and the `derived` block on a Norwegian
Madrid designation now answer instead of returning nothing.

One documented limit. The rule reports `effective_from: "2010-07-01"` and
`applicable_from_date: "2010-07-01"`, the day varemerkeloven (the Act § 71
belongs to) entered into force (res. 25. juni 2010 nr. 936). Before it the
repealed 1961 Act § 21 gave TWO months from the kunngjøringsdag, an era we do
not model on the Madrid route, and serving the modern three months to it would
report a close date a month LATER than the law gave. A Norwegian designation
published before 1 July 2010 therefore comes back `supported: false` rather
than with a window.

**No reindex required.** This adds a window where there was none; it changes
no existing window. Baked `opposition_window_opens` / `opposition_window_closes`
values in the search index are all still correct. Norwegian Madrid
designations pick their new window up at their next natural reindex.

**A guard against the wrong publication.** A Madrid designation has two
publications that can fill one date field (WIPO's Gazette of the
international registration, and the designated office's own journal
republication), and different offices run their clock from different ones.
Feeding a rule the other one silently produces a plausible-looking, wrong
statutory bar. Each Madrid rule now declares which publication it needs, and
`POST /v1/oppositions/compute` accepts an optional `publication_date_kind`
(`office_journal`, `wipo_gazette`, `office_register`, `unknown`) saying which
one your date is. When the two disagree the response is `status: "unknown"`
with null dates and the rule's identity intact, rather than a date you should
not act on. A new nullable `reason` field on each
`POST /v1/oppositions/compute` item and on the trademark
`derived.opposition_window` block says WHY it is unknown
(`"publication_date_kind_mismatch"` today, null otherwise), so you can tell a
contradicted publication kind from a mark that simply has no publication date
yet; the field is additive and null on every response that had no reason to
carry it. Omit the field and nothing changes. Today EVERY Madrid rule
declares its publication: `ch`, `de` and `fr` run from the WIPO Gazette; `no`,
`fi`, `pl`, `sg`, `us`, `ca`, `au`, `eu`, `jp`, `kr`, `in` and `tr` run from
the designated office's own journal.

Declaring it is not optional in practice. A WIPO-feed leg is resolved by
jurisdiction, and the date it carries is the Gazette's, so a rule that has
NOT adjudicated which publication it runs from is refused on that path
outright rather than dated on a publication nobody established it uses. The
four Asian Madrid rules (`jp`, `kr`, `in`, `tr`) were adjudicated for this
release; each of their statutes names the office's own gazette and says the
WIPO Gazette is not the trigger.

## September 3, 2026: EU restitutio, Canada's transitional renewal window, India's priority-dated term, and WIPO closure days

Four deadline corrections, all computed at request time. Nothing is baked into
the search index, so every one of them is live the moment this deploys and no
reindex is involved.

**The EU now has a restoration deadline.** `POST /v1/deadlines/compute`,
`GET /v1/deadline-rules` and the `derived` block on an EU trademark return a
new `restoration` row once a renewal has passed its due date, under one new
`rule_id`: `eu_restitutio`. It runs from the end of the six-month grace period
to twelve months after the registration expired, which is what Article 104(2)
EUTMR leaves after deducting the further six months of Article 53(3) from the
year following the missed time limit, and what the EUIPO Guidelines state in
one sentence: the one-year period "starts on the day on which the protection
ends and not on the expiry of the further 6-month grace period". Read the row
as an outer date, not as a promise. Restitutio is discretionary: it is granted
only where all due care required by the circumstances was taken, it carries a
fee, and it must also be requested within two months of the removal of the
obstacle, a date no register publishes. No existing EU date moves.

**Canada's first renewal after 17 June 2019 opens far earlier than we said.**
For a trademark that was on the register before that day, Trademarks
Regulations s. 159 lets the renewal fee for the FIRST renewal falling due on or
after it be paid from the start of the period being renewed, with no six-month
early limit, and CIPO says so directly: "there is no restriction on how early
the renewal fee for the first renewal after June 17, 2019 may be submitted."
The engine used to report that window opening six months before the due date.
`window_opens` on the affected cycle now moves back to the day of registration
or of last renewal, which can be fifteen years earlier. Only that one cycle is
affected; the due date, the grace expiry and every later cycle are unchanged,
and the window can only widen, so nothing that was filable becomes unfilable.

**An Indian trademark with a convention priority claim expires earlier than its
filing date implies.** Trade Marks Act 1999 s. 154(2) registers a Paris or WTO
convention application as of the date the application was made in the
convention country, and s. 23(1) is expressly subject to it, so the ten-year
term of s. 25(1) runs from the priority date, up to six months before the
Indian filing. `POST /v1/deadlines/compute` accepts a new optional
`priority_date` on each item; send it for an IN item and the renewal is
computed from it, with `trigger_field` reading `priority_date` so the answer
says which date it used. Omit it and India falls back to the filing date
exactly as before. The field is inert for every other jurisdiction: no other
pinned term provision runs from a priority date. Note the current limit: our
own stored trademarks carry priority claims in a separate record, so the
`derived` block, portfolio deadline lists and the iCal feed do not yet pass one
and still serve the filing-date answer for that cohort, which is the later
date. Where the office states an expiry, that expiry already anchors the
schedule and the question does not arise.

**WIPO renewal dates now move off days the International Bureau is closed.**
Rule 4(4) of the Regulations under the Madrid Protocol extends a period that
expires on a day the IB is not open to the public to the first subsequent day
it is, and a renewal payment is a communication the IB must receive. WIPO rows
now report `holiday_calendar` `wipo_geneva`, and a due date or grace expiry
that falls on a weekend or on one of the eight days WIPO published for 2026
(1 January, 3 and 6 April, 14 and 25 May, 31 July, 25 and 31 December) is
served on the next open day with the adjustment field reading `moved`. An
international registration dated 13 June 2016, for example, was renewable
"by 13 June 2026", a Saturday; the answer is now Monday 15 June 2026. The
International Bureau publishes its closure list one year at a time, so the
calendar covers 2026 only: dates in any other year are emitted exactly as the
Rule 4(1) and 4(2) arithmetic produced them and report `not_checked` rather
than a business-day guarantee we cannot make. Filing-window opening dates are
never rolled, on any office.

Every value above cites the instrument it comes from, with a verbatim quote and
a hash-pinned snapshot, on `GET /v1/deadline-rules` and on each compute
response.

## September 3, 2026: Mexican OPPOSITION windows follow the same month-overflow rule, and the overflow is now reported

**Opposition close dates change for MX (IMPI). Reindex MX.**

Earlier today Signa applied Reglamento de la Ley de la Propiedad Industrial
art. 4o, párrafo segundo to the Mexican maintenance ladders: a period counted
in months that ends on a day the target month does not have runs into the
following calendar month and ends on its first business day, rather than
clamping to the short month's last day. The opposition engine kept the clamp,
which was stated as a limit in that release. It no longer does.

Art. 4o computes «los plazos establecidos en meses o años a que se refiere el
artículo 184 de la Ley», and art. 184 LPI (re-enacted word for word as LFPPI
art. 21) is the Law's general period-computation article, not a
maintenance-only provision. The one-month opposition period of LFPPI art. 221
is a period fixed in months by the Law, so it is inside art. 4o. An application
published on 31 March 2026 now closes for opposition on **Monday 4 May 2026**,
not 30 April: April has no 31st, so the period runs into May, and May 1 is Día
del Trabajo with the weekend behind it. A publication on 31 August 2026 closes
1 October instead of 30 September. Publications whose day number exists in the
next month are untouched. Every corrected date is LATER than the one Signa
served before, so nobody who relied on the old date filed late.

`GET /v1/opposition-rules` gains `month_end_overflow` on every rule, so a
client that recomputes a window from the rule can reproduce the close date.
It reads `last_day` everywhere except `mx_opposition`.

**The overflow move is now reported.** `due_date_adjustment` and
`grace_expiry_adjustment` on `POST /v1/deadlines/compute` gain a fourth value,
`month_end_overflow`. Before this, an overflow that landed on an open day was
indistinguishable from a date nothing had happened to: the fields described the
weekend and holiday roll only, so they read `unchanged`. The new value
subsumes the roll on such a date, because art. 4o para. 2 names the first
BUSINESS day of the following month, so the move into the next month and the
roll that completes it are one statutory step. The addition is additive: no
existing value was renamed or removed.

Opposition windows are baked into the search index at index time, so the
materialised `opposition_status` and
`opposition_closes_before`/`opposition_closes_after` filters on MX documents
are corrected only after **MX is reindexed**. `POST /v1/oppositions/compute`
and the trademark response recompute per request and are correct as soon as
this deploys.

## September 3, 2026: Mexican deadlines now roll, month-overflow ends in the next month, and Madrid designations renew on the international registration

**Computed dates change for MX (IMPI), on both engines. Reindex MX.**

Mexican maintenance dates were published raw: a renewal that fell on a Sunday
or on a day IMPI declares inhábil was served on that day. Reglamento de la Ley
de la Propiedad Industrial art. 4o, the 1994 regulation the LFPPI keeps in
force, says otherwise in its third paragraph, and Signa now applies it. A
renewal due date, grace end or 3-year declaration-of-use window end that lands
on a weekend, on a fixed Mexican holiday or on a día inhábil IMPI has published
moves to the next working day, `holiday_calendar` reads `mx_mexico`, and the
two adjustment fields read `moved` or `unchanged`. Filing windows never move:
a window that opens on a closed day cannot be filed that day, and moving it
would shorten the statutory six months.

The same article's second paragraph fixes a subtler error. When a period
counted in months or years ends on a day the target month does not have: a
six-month grace counted from a 31st, landing on "31 February". Mexico does not
clamp to the 28th the way most offices do. The period ends on the **first
business day of the following calendar month**. A grace period that used to
read 28 February 2033 now reads 1 March; in a leap year, 29 February 2036
becomes Monday 2 March 2036. This is the first office in the corpus to depart
from the month-end clamp, and it is behind a per-office flag, so no other
jurisdiction's dates move.

**The IMPI holiday calendar was rebuilt from the Diario Oficial.** It carried a
flat "IMPI closes 16 to 31 December, every year" block that no published
acuerdo supports. IMPI sets its year-end closure annually: 18 December to
2 January for 2024, 19 December to 3 January for 2025, 22 December to 6 January
for 2026. So the old calendar closed four days IMPI works and opened five days
it does not, in both directions. It also omitted 5 May and 1 September, which
four published acuerdos name. The calendar is now eleven recurring days plus
exactly what each pinned acuerdo states, including the one-off closures of
1 October 2024, 12 December 2025, 23 February 2026 and 30 June 2026.

**Madrid designations of Mexico now renew on the right date.** A designation
has no independent Mexican term: it expires with the international
registration and is renewed centrally at WIPO. The rule that served it was
Mexico's national "renewal plus declaration of use", which the engine anchored
on IMPI's grant date, and for a designation granted years after the international
registration, that is a renewal date up to a decade too late. There is now a
separate rule, `mx_renewal_madrid`, anchored on the international registration
date, and a designation whose international registration date is unknown and
whose office expiry is not on the record DECLINES with
`requires_office_date` instead of publishing a guess. The declaration of use
that follows a Madrid renewal runs three months from the International
Bureau's notice, a date no record carries, so Signa deliberately publishes
nothing for it rather than a wrong date.

Every Mexican value is now cited to the Diario Oficial de la Federación itself
(the primary publication) with a verbatim quote and a pinned snapshot, with
the consolidated ordenjuridico.gob.mx copies kept alongside. That includes the
eight IMPI acuerdos behind the calendar.

Three limits are stated rather than hidden. IMPI publishes each December's
closure in the preceding autumn, so a year with no published acuerdo has no
closure in Signa: a date in late December 2026 or later is served un-rolled,
which is earlier than the true date, never later. The opposition engine still
applies the ordinary month-end clamp rather than art. 4o's overflow rule, again
in the earlier direction. And the overflow move itself is not reported in the
adjustment fields, which describe the weekend and holiday roll only.

Trademark responses and `POST /v1/deadlines/compute` recompute per request, so
they are correct as soon as this deploys. Opposition windows are baked into the
search index at index time, so the materialised `opposition_status` and
`opposition_closes_before`/`opposition_closes_after` filters on MX documents
are corrected only after **MX is reindexed**.

## September 3, 2026: Australian renewal grace periods roll onto the next open day

An Australian trade mark can still be renewed for six months after it expires
(Trade Marks Act 1995 s 79). When the last day of that six-month period falls
on a weekend, a Canberra public holiday or IP Australia's standing Christmas
close-down, `POST /v1/deadlines/compute` and the `derived` block on a trademark
now serve the next day the office is open, and say so in
`grace_expiry_adjustment` (`moved`) and `holiday_calendar` (`au_australia`).
Australian grace dates were previously served one to three days EARLY.

The renewal DUE date deliberately does NOT move, and this is the only office
where the two answers differ. s 223A(1) rolls "the last day of a period
provided by this Act (except this section) for doing an act"; the s 79 period
is one, reg 21.24A supplies the prescribed circumstance and reg 21.24C, the
exclusion list, does not reach a renewal request. The twelve-month renewal
REQUEST period is a different animal: it is prescribed by reg 7.3 rather than
by the Act, and Schedule 9 item 10 keys the \$100-per-month surcharge on the
request being made "after the expiry date to which section 79 of the Act
refers": the expiry day itself. Serving a later due date would tell you that
you may still renew fee-free on the Monday when the surcharge may already have
attached on the Saturday. So an Australian `due_date` stays on the statutory
expiry and reports `due_date_adjustment: "not_checked"` on every row, whatever
day of the week it lands on: no calendar was consulted for it, and `unchanged`
would claim a Saturday is a business day.

Nothing else about the Australian rule changed: same `rule_id` (`au_renewal`),
same ten-year term, same twelve-month window, same six-month grace, same
consequence. Three provisions are newly pinned and quoted verbatim next to the
field they move: s 223A(1), reg 21.24A, and reg 21.24C in full, so that the
absence of a renewal request from the exclusion list is verifiable rather than
asserted. Maintenance deadlines are computed per request, so this is live as
soon as it deploys and no reindex is required. It supersedes the "Australia is
deliberately NOT rolled" note in the entry below.

## September 3, 2026: renewal dates roll on closed office days in Norway, France and Iceland, and on BOIP's own closing days in the Benelux

Four offices join the maintenance rollover: a renewal due date or grace expiry
that lands on a day the office is closed is now served on the next day it is
open, instead of on the exact calendar date. `POST /v1/deadlines/compute` and
the `derived` block on a trademark answer with the moved date and say so in
`due_date_adjustment` / `grace_expiry_adjustment` (`moved`, `unchanged` or
`not_checked`) and `holiday_calendar`. Nothing about a rule id, a window
length or a consequence changed; only the served dates.

Each office rolls on the provision that governs its own renewal deadline, not
on a generic weekend rule. Norway: betalingsforskriften § 6 første ledd (24 and
31 December, Saturdays, helligdager and days the legislation treats as
equivalent), extended to varemerkeloven deadlines by § 6 tredje ledd. France:
CPI R. 718-2 al. 5, applied to the renewal declaration by INPI's own renewal
directives, which work the example: a mark expiring Saturday 14 May 2022 could
be renewed on Monday 16 May with no late-renewal fee. Iceland: Stjórnsýslulög
nr. 37/1993 8. gr., which extends a period ending on a general public holiday
to the next day the authority is OPEN. Benelux: Uitvoeringsreglement regel 3.9,
lid 3. Every one is pinned and quoted verbatim next to the field it moves.

Two office calendars were corrected from primary sources in the same change,
and both corrections can move a date on their own.

The Benelux calendar is now BOIP's own closing-day list, published as a Regel
van de Directeur-Generaal of 3 April 2019, rather than an assumed Dutch
national-holiday set. Ten days join it: 2 January, 1 May, Europe Day, the
Luxembourg and Belgian national days, Assumption, All Saints, All Souls,
Armistice Day and the Belgian King's Day, and Good Friday LEAVES it, because
the rule names it on neither limb and BOIP is open that day. A Benelux deadline
falling on Good Friday is therefore due that day, not the following Tuesday.

Iceland's 24 and 31 December STAY CLOSED, and no Icelandic date moves on that
account. An earlier draft of this release opened both days: they are public
holidays only FROM 13.00 under lög nr. 88/1971 6. gr. (24 December only from
18.00 as a church holiday, lög nr. 32/1997 2. gr.), so the statute alone gives
a half day, and no ISIPO closure notice had been found. One was then found:
Hugverkastofan's own December 2020 notice, which says the office is closed on
Þorláksmessa, aðfangadagur and gamlársdagur and that every deadline falling in
that period moves to the next opening day. The office's own statement settles
the office's own opening days, so both remain closed, exactly as before this
release. See "Philippines, India and Iceland business-day calendars
re-sourced" below.

Benelux opposition windows DO move, because Good Friday left `bx_benelux`.
Opposition windows are materialised into search documents at index time, so
`opposition_status` and the `opposition_closes_*` filters carry the corrected
dates for BX records only after that office is reindexed. Every trademark
response recomputes the window per request and is correct as soon as this
deploys, and maintenance deadlines are computed per request throughout, so no
reindex is needed for them.

Australia was NOT rolled in this change, and the reason was documented rather
than hidden: Trade Marks Act 1995 s 223A(1) settles the six-month post-expiry
limb but not the due date, and the rollover was one switch that moved both
dates. SUPERSEDED the same day by the entry above, which gives the engine a
grace-only roll and turns it on for Australia.

Two accuracy limits are recorded with the same candour. INPI closure decisions
before 2026 were never fetched, so a French date that fell on a pre-2026 closure
day is served un-rolled. BOIP publishes any FURTHER closing day for a given year
on a page that is not reachable programmatically, so those ad-hoc days are not
modelled either. Both under-roll rather than over-roll.

## September 3, 2026: EUIPO Article 2 days now extend time limits; `consequence_if_missed` vocabulary fixed

**EU dates move later on four days a year.** The `euipo_alicante` calendar
now carries BOTH lists of the Executive Director's annual decision on
non-working days: the Article 1 days on which the Office is closed AND the
Article 2 days on which the Office is open but ordinary post is not received
(19 March, 23 June, 9 October, 8 December in EX-24-9 for 2025 and EX-25-16
for 2026). Article 69(1) EUTMDR extends a time limit expiring "on a day on
which the Office is not open for receipt of documents or on which ... ordinary
post is not delivered in the locality in which the Office is located ... until
the first day thereafter on which the Office is open for receipt of documents
and on which ordinary mail is delivered", and the Office's own decision reads
it the same way (EX-24-9 recital (1)(b)). Until now only Article 1 days
rolled, which served the earlier date; the served date is now the statutory
one. Affected: `POST /v1/oppositions/compute` and `opposition_window` for
`EM` marks whose window closes on one of those days (e.g. a Part A
publication on 9 July 2026 now closes 13 October 2026, not 9 October), the
EU office-action response deadlines, and the watch-evaluator's opposition
windows. Opposition windows are baked into the search index at index time, so
indexed `EM` documents carry the corrected close only after the EU reindex
(scheduled with this release). The 2027-2030 Article 2
days are carried on the same estimate basis as the Article 1 days and are
replaced each December from the published decision.

**EU RENEWAL dates move on the same days.** An earlier draft of this entry said
they did not, on the ground that the EU maintenance config does not
business-day adjust. It does now: the same release turns on the roll for EU
renewals, so a `eu_renewal` due date or grace expiry landing on any day the
Alicante calendar carries, Article 1 closures and Article 2 ordinary-post days
alike, is served on the next open day instead. A mark filed 19 March 2016 was
served a renewal due 19 March 2026 and a grace expiry 19 September 2026; it is
now due 20 March 2026 with grace to 21 September 2026, because 19 March is San
José, an Article 2 day. The direction is later. For an **Article 1** day that is
unambiguously the safe direction: the Office is shut on the original date, so
the served day is the first one on which you can actually file. For an
**Article 2** day it is a judgement call, and this entry originally overstated
it, on 19 March the Office is **open**, and only ordinary post is not delivered
in Alicante. Signa rolls it on the reading of Article 69(1) EUTMDR that the
Office's own decision states (EX-24-9 recital (1)(b)), which covers a day on
which "ordinary post is not delivered" as well as a closure. An e-filer can
nonetheless file on the original day, so if the Office ever reads Article 69(1)
as extending only for post-borne filings, the served date is a day LATER than
the true one. That is the unsafe direction, it is bounded at four days a year,
and it is now listed in
[Accuracy limits](https://docs.signa.so/guides/deadline-rules.md#accuracy-limits). `due_date_adjustment` and
`grace_expiry_adjustment` read `moved` on those rows and `unchanged` on the
rest, so a row always says whether a calendar was consulted. Renewal dates are
not baked into the search index, so no reindex is needed for this half.

**`consequence_if_missed` now means one thing per value.** The value names
the mechanism the office's pinned provision prescribes: `expiration` (the
right ends by operation of law), `removal_from_register` (the office strikes
the entry as the statutory consequence), `cancellation` (the office cancels a
still-running registration for a missed maintenance filing, US §8/§71),
and restorability is said only by the presence of a `restoration` rule. Nine
rules were relabelled to match their pinned text; no date moved:
`pl_renewal` `cancellation` → `expiration` (PWP art. 168 ust. 1 pkt 1
"wygasa"), `fr_renewal` `cancellation` → `expiration` (CPI L. 712-1),
`de_renewal` `cancellation` → `removal_from_register` (§ 47 Abs. 6 MarkenG
"gelöscht"), `dk_renewal` `cancellation` → `removal_from_register`
(varemærkeloven § 37 "udslettelse"), `eu_renewal` `cancellation` →
`removal_from_register` (Art 53(8) EUTMR "cancel the mark from the
register"), `bx_renewal` `irrevocable_loss` → `expiration` (BVIE art.
2.26(a)), `ch_renewal` `irrevocable_loss` → `removal_from_register` (MSchG
art. 35(b)), `mx_declaration_of_use_3yr` `irrevocable_loss` → `expiration`
(LFPPI art. 233 "caducará de pleno derecho"), `th_renewal`
`removal_from_register` → `expiration` (Trademark Act s. 56 deems the
registration revoked). `irrevocable_loss` is retired: no rule serves it. The
full vocabulary is in the [deadline rules
guide](https://docs.signa.so/guides/deadline-rules.md#consequence-vocabulary). No wire-format change:
the field stays a string and no new value was added.

## September 3, 2026: Philippines, India and Iceland business-day calendars re-sourced

Three office calendars used for business-day adjustment now rest on the
primary instruments that actually set the closed days, and two of them change
published dates.

**Philippines (`ph_philippines`, used by `ph_opposition`).** The closed days
are now transcribed year by year from the presidential proclamations that
declare them, Proclamations 368 s. 2023, 453, 514, 579 and 665 s. 2024 for
2024; 727 and 729 s. 2024, 839, 878 and 911 s. 2025 for 2025; 1006 s. 2025,
1189 and 1264 s. 2026 for 2026, together with the statutory fixed days (RA 9492 s. 1,
RA 10966) computed for every year. Three curated days were wrong and move: Eid
al-Fitr 2025 is 1 April (not 31 March), Eid al-Adha 2025 is 6 June (not 7
June) and Eid al-Fitr 2026 is 20 March (not 21 March). Days the proclamations
make working days are now OPEN (25 February 2025 and 2026; 21 August 2024,
observed on 23 August), and days they add are closed (9 February 2024, 12 May
2025, 31 October 2025, 2 November 2026, every Christmas Eve). For 2027 onward,
until each year's proclamation is pinned, only the statutory fixed days are
closed and the "Monday nearest" holidays are open: an opposition close on one
of those days is published un-rolled, which is never later than the true date.
The opposition extension note now cites IPOPHL Memorandum Circular 2022-013
itself: one 45-day extension, granted only once, on meritorious grounds,
reckoned from the original due date, in force 30 June 2022.

**India (`cgpdtm_india`, new).** Indian deadlines are now business-day
adjusted. The Trade Marks Registry observes the annual DoPT Office Memorandum
for central government offices; the 2024, 2025 and 2026 memoranda are pinned
and their 14 compulsory holidays per year make up the calendar. `in_renewal`,
`in_restoration`, `in_opposition` and `in_opposition_madrid` now report
`holiday_calendar: "cgpdtm_india"` and `moved` / `unchanged` for 2024–2026
dates: a renewal due on Sunday 15 June 2025 is served as Monday 16 June; an
opposition closing on Republic Day 2026 is served as 27 January. Dates after
2026 report `not_checked` until the 2027 memorandum is pinned. Three limits
are documented, all in the safe direction: the three branch-local optional
holidays (Mumbai, Delhi, Kolkata, Chennai and Ahmedabad differ), Ambedkar
Jayanti, and any moon-sighting shift of an Islamic day are not modelled, so a
deadline on one of them is published un-rolled.

**Iceland (`is_iceland`).** No date changes. 24 and 31 December stay closed,
now on the office's own statement (Hugverkastofan's December 2020 notice:
closed on both days, deadlines falling on them move to the next opening day)
and the statutes that make them half-day frídagar; 23 December stays open
because the office declares it year by year.

Trademark responses recompute opposition windows per request, so they are
correct as soon as this deploys. The materialised `opposition_status` and
`opposition_closes_before`/`opposition_closes_after` search filters are baked
at index time: IN and PH search documents carry the rolled and corrected
closes only after those offices are reindexed. Maintenance rows are computed
per request and need no reindex.

## September 3, 2026: Korea (KIPO) deadline and opposition rules

New jurisdiction. `KR` now has a maintenance config and two opposition rules,
so `GET /v1/deadline-rules`, `GET /v1/opposition-rules`,
`POST /v1/deadlines/compute`, `POST /v1/oppositions/compute` (office `KR`) and
the `derived` block on a trademark answer for Korean marks instead of
returning nothing. Three new `rule_id` values: `kr_renewal`, `kr_opposition`,
`kr_opposition_madrid`.

Renewal runs 10 years from the REGISTRATION date (상표법 제83조①), with a
12-month filing window before the term ends that closes on the expiry day and
a 6-month window after it at 330,000 KRW per class instead of 300,000
(제84조②, 제78조③). Korean law has no restoration of a missed renewal
application and no declaration of use, so neither is emitted: that absence is
the finding, not a gap. Opposition is 30 days from KIPO's own 출원공고 for
applications published on or after 22 July 2025 (법률 제20697호), and 2 MONTHS
for publications before that day; an international registration designating
Korea is opposed on the identical window, running from KIPO's publication
rather than the WIPO Gazette (제180조①). Every value cites the Act, the fee
rules or the KIPO publication it comes from, with a verbatim quote and a
pinned snapshot.

Two things about dates deserve a direct statement. First, the 22 July 2025
change is the first opposition pivot that switches UNITS (calendar months
before, a day count after), and the engine now resolves the era's length with
its unit; nothing moves for any other office. Second, Korean deadlines are
business-day adjusted against the `kipo_korea` calendar, which this release
corrects from primary sources: 1 May (노동절) is now a closed day in every
year (상표법 제16조제4호 names it, and the shipped calendar had omitted it),
with a Saturday/Sunday substitute from 2026; 17 July (제헌절) is closed from
11 May 2026, when the amended 관공서의 공휴일에 관한 규정 made every 국경일 a
public holiday again; and 28 September 2026 is OPEN, because the regulation
substitutes a 설날/추석 day only when it falls on a Sunday and 26 September
2026 is a Saturday. A KIPO deadline that lands on one of those days moves
accordingly.

Four limits are documented rather than hidden. The renewal `window_opens` is
one day earlier than KIPO's own count (KIPO opens the window the day AFTER
expiry minus one year); it is never rolled and never a deadline. Election days
and 임시공휴일 designated by government notice are not in the calendar until
the notice is pinned, so a deadline landing on one is published un-rolled:
earlier than the true date, never later. A renewal filed ON the expiry day is
modelled as surcharge-free, which is how KIPO's example reads. And a Madrid
designation is computed only when its international registration date is
known: the KIPO connector must carry the IRN, and its publication date for a
designation must be KIPO's own 출원공고일.

Trademark responses recompute opposition windows per request, so they are
correct as soon as this deploys. Only the materialised `opposition_status` and
`opposition_closes_before`/`opposition_closes_after` search filters are baked
at index time, so KR search documents carry these values only after KR is
reindexed; no KR records are indexed today, so that reindex belongs with the
KIPO connector rather than with this change.

## September 3, 2026: Türkiye dates now roll, pre-2018 renewal windows and the 2017 opposition straddle corrected

**Computed dates change for TR (TÜRKPATENT), on both engines.** Three of the
limits documented when Türkiye landed on 2 September are closed.

**Business-day adjustment is now applied.** A Turkish renewal due date, grace
end or opposition close that falls on a Saturday, Sunday, one of the fixed
holidays of Law 2429 (1 January, 23 April, 1 May, 19 May, 15 July, 30 August,
29 October) or a Ramazan or Kurban Bayramı day is served on the next working
day, so `holiday_calendar` reads `turkpatent_turkiye` and the two adjustment
fields read `moved` or `unchanged` for dates in 2024 to 2030 (`not_checked`
outside that range, where the calendar is not curated). The bayram days come
from Diyanet İşleri Başkanlığı's official tables for each year; the arefe half
days, the afternoon of 28 October and the Presidency's ad-hoc administrative
leave days are working days and never move a date. This rests on a documented
convention rather than an office statement: no TÜRKPATENT source states how a
last day on a non-working day is treated before the office, and the roll
applies the rule the general procedural statutes give the courts (HMK 6100
m. 93/1, İYUK 2577 m. 8/2). Direction of the change: every affected date moves
LATER, by one to five days (for example, a renewal due on 1 May 2026, a Friday
holiday, is now served 4 May; an opposition closing on 27 May 2026, the first
day of Kurban Bayramı, is now served 1 June).

**Renewal cycles whose anniversary fell before 10 January 2018** now show the
request window the repealed KHK 556 m. 41/3 gave them: `window_opens` and
`due_date` move to the last day of the anniversary's month (up to 30 days
later than before), while `grace_expiry` stays on the exact anniversary plus
six months, because m. 41/5 anchors the lapse on the end of the term. Every
such cycle is historical (visible with `include_missed`); no current deadline
moves.

**Opposition windows for applications filed before 10 January 2017 but
published on or after it** now get the three months the KHK gave them (SMK
geçici m. 1/1 concludes an application under the law in force on its filing
date), one month later than before; the served citation follows. Windows for
applications filed on or after that date are unchanged. `POST
/v1/oppositions/compute` accepts an optional `filing_date` per item for this;
omit it and the item falls back to the publication date, which yields the
shorter modern window. This filing-date key applies to national applications
(`tr_opposition`) only. Madrid designations (`tr_opposition_madrid`) stay keyed
on the publication date: a designation's `filing_date` is the international
registration date, and a subsequent designation of Türkiye carries no date of
its own, so a filing-keyed pivot would have served a subsequent designation
made after 10 January 2017 on an older international registration three
months instead of the two SMK m. 18/1 gives it, one month too late. An
original designation on a pre-2017 international registration published on
or after 10 January 2017 is therefore served two months where the KHK
arguably gave it three; every such window closed in 2017.

Trademark responses and `POST /v1/deadlines/compute` recompute per request,
so they are correct as soon as this deploys. **TR reindex required:** the
materialised `opposition_status` and
`opposition_closes_before`/`opposition_closes_after` search filters read
window dates baked at index time, so Turkish search documents carry the
rolled and filing-keyed close dates only after TR is reindexed.

## September 3, 2026: Danish renewal dates on office closing days now roll

**Computed dates change for DK (DKPTO).** A Danish renewal due date or grace
end that falls on a Saturday, Sunday, a Danish public holiday or one of
DKPTO's enumerated closing days is now served on the next working day, as the
office's own Varemærkehåndbog states ("Er sidste rettidige indbetalingsdato
en dag, hvor Patent- og Varemærkestyrelsen ikke har åbnet … så er betaling
førstkommende hverdag rettidig") and as dkpto.dk/lukkedage confirms for the
closing days ("På officielle lukkedage, vil alle frister automatisk blive
forlænget til førstkommende hverdag"). Rows now read `holiday_calendar:
"dk_denmark"` with `moved` or `unchanged` adjustments instead of
`not_checked`.

Direction of the change: dates move LATER only, by one to seven days (a mark
registered 10 January 2017 was due 10 January 2027, a Sunday, and is now due
11 January; a renewal due 28 December 2026 rolls through the 28 to 30
December closure, New Year's Eve, New Year's Day and the weekend to 4 January
2027\). Marks whose dates already fall on a working day do not move. Computed
at request time; no reindex (maintenance deadlines are not indexed).

## September 2, 2026: Philippines Renewal DAU window opens six months earlier

**Computed dates change for PH (IPOPHL).** The `ph_declaration_of_use_renewal`
rule (the Declaration of Actual Use due within one year of each renewal) used
to open its filing window at the renewal itself. IPOPHL's Trademark
Regulations of 2023 (Memorandum Circular 2023-001, in force 14 February 2023)
say in Rule 206 that "The Renewal DAU may be filed within the six-month period
before the expiration of the registration", so the window now opens six months
before expiry - together with the renewal window - for every renewal cycle
expiring on or after 14 February 2023. Cycles that expired before that day
keep the old window: the change is modelled as an era pivot inside the rule,
keyed on the cycle, not on today's date, and a window for a cycle expiring
inside the first six months of the new regime opens on 14 February 2023
itself, because no filing could precede the Regulations that permit it.

Only `window_opens` moves. `due_date` and `grace_expiry` are unchanged (one
year after the renewal, no grace), and so is every other Philippine rule. The
customer-visible effect is an earlier `window_open` status: a mark registered
5 January 2020 now shows its first Renewal DAU window opening 5 July 2029
instead of 5 January 2030. Under the 2017 Regulations (MC 17-010) Rule 206 was
only a transitional trigger, which is why the earlier window is right for the
earlier cycles; both texts are pinned with verbatim quotes, and the in-force
date is pinned from WIPO Lex because the circular's own Rule 1406 carries it
handwritten. Maintenance deadlines are computed per request, so no reindex is
involved.

## September 2, 2026: Philippines Madrid designations get their own DAU anchors

**Computed dates change for PH (IPOPHL) international registrations.** Two new
`rule_id` values, `ph_declaration_of_use_3yr_madrid` and
`ph_declaration_of_use_5yr_madrid`, serve marks on the `madrid` route. Under
IPOPHL's Madrid Regulations (MC 17-011, Rule 20) the 3rd-year DAU of an
international registration designating the Philippines runs "within three
years from the date of the international registration" and the 5th-year DAU
"within one (1) year from the fifth anniversary of the date of statement of
grant of protection" - not from the national filing and registration dates the
`ph_declaration_of_use_3yr` and `ph_declaration_of_use_5yr` rules use. Those
two rules now apply to the national route only (`applies_to: domestic`); the
Madrid rules read `applies_to: madrid_only`, trigger on
`intl_registration_date` and `protection_grant_date` respectively, and are
absent when the record does not carry that date rather than guessed from
another one. A designation with no international registration date still
returns no schedule at all (`requires_office_date`), as before.

The Renewal DAU and Mid-Renewal DAU are unchanged for designations: Rule 20
runs them from the renewal of the registration, which for an international
registration is the WIPO renewal on the international registration date the
renewal row already uses. One limit is documented rather than hidden: a
SUBSEQUENT designation's 3rd-year DAU legally runs from the recording of that
designation, and the engine anchors it on the international registration date
instead, which is never later. No reindex is involved.

## September 2, 2026: Swedish publication and expiry dates corrected (PRV connector)

Two corrections to how we read PRV's bulk feed. Neither changes a stored
record until the PRV backfill and reindex have run, so existing responses are
unchanged at deploy time; the dates below move once that maintenance completes.

**`publication_date` for national registrations since December 2022.** PRV's
feed dates the registration notice by gazette issue, one day (occasionally two
or three) after the registration decision. PRV counts the opposition period
from the kungörelsedag, which is the registration day itself, and its own
register shows the opposition period starting on that day. We now take the
registration day as `publication_date` for the registration notice; the
gazette issue date stays on `publications[].publication_date`. After the
backfill and reindex, `publication_date` on affected Swedish marks moves one
day earlier (to `registration_date`), and every Swedish opposition window
computed from it (`derived.opposition_window`, the `opposition_status` and
`opposition_closes_*` filters) moves one day earlier at both ends. Together
with the opening-day correction below, a mark registered on 1 April now
shows a window of 1 April to 1 July, matching PRV, instead of 3 April to 2
July. Marks registered before December 2022 already carried the registration
day and do not move. Madrid designations of Sweden are not touched by this
change.

**`expiry_date` for registrations from 2019 on.** PRV reports a ten-year
expiry for every registration, but our far-future date guard (built for a
handful of corrupt year-2077 and year-5014 owner dates) rejected any date more
than two years out, so Swedish registrations from 2019 on, and older marks
whose latest renewal runs past 2028, were served with no `expiry_date` and no
`renewal_due_date`. Both are now ingested as reported. Renewal deadlines for
these marks anchor on PRV's own date once it is stored; where that differs
from the statutory computation we used before, PRV's date wins.

## September 2, 2026: Canada (CIPO) opposition extension benchmark before December 2023

**No computed date moves.** `window_opens`, `window_closes` and `status` for
Canadian marks are unchanged; this corrects the advisory `common_extension`
field only, and only for the direct (non-Madrid) route.

The Trademarks Opposition Board rewrote its "Practice in trademark opposition
proceedings" on 1 December 2023 and cut the benchmark first extension to file
a statement of opposition from 4 months (with a cooling-off extension of up to
9 months on consent) to 2 months (up to 7). The `ca_opposition` rule served the
post-2023 figure for every window. The office's own transition rule keys on
when the extension request was received ("requests for extensions of time
received on or after December 1 will be subject to the revised extensions of
time"), and a non-retroactive s. 47(1) request is received no later than the
deadline it extends, so the rule now keys on the served close date:

* `derived.opposition_window.common_extension` for a CA direct-route window
  that **closes before 1 December 2023** now reads `months: 4` (was 2) with
  notes naming the 2019 notice and the 9-month cooling-off figure.
* A window closing **on or after 1 December 2023** keeps `months: 2`; its
  notes are rewritten (they no longer claim the window is "otherwise
  non-extendable" - the 7-month cooling-off extension exists) and name the
  straddle: a window that opened before 1 December 2023 and closes on or
  after it may have been extended under either figure depending on when the
  request was received.
* `GET /v1/opposition-rules` continues to show the current (2-month) tier for
  `ca_opposition`; the rule's citations gain the superseded 2019 notice and
  CIPO's own side-by-side changes page, both pinned from the Wayback Machine
  because the office has removed them.
* `ca_opposition_madrid` is untouched: Trademarks Regulations s. 125 caps the
  Protocol-application extension at 4 months in every era.

One documented limit: the 4-month figure is verified for the notice in force
from 17 June 2019. The benchmark under the 2009 notice it replaced is not
pinned, so a window closing before 17 June 2019 is served the 4-month figure
with notes saying so. No search filter reads `common_extension`, so no
reindex is needed.

## September 2, 2026: MX oppositions before 30 August 2016 are no longer served

**Computed dates change for MX (IMPI).** Mexico had no pre-grant opposition
procedure until the decree published in the Diario Oficial de la Federación on
1 June 2016, whose sole transitorio puts it in force "a los 90 días naturales
siguientes a su publicación" - 1 June 2016 plus 90 calendar days, i.e.
30 August 2016. The same decree introduced the pre-grant publication of the
application, so an MX record carrying a publication date before that day is
showing a registration publication, which never opened an opposition period.
The `mx_opposition` rule now declares `applicable_from_date` and
`effective_from` of `2016-08-30`, and MX publications earlier than that return
no opposition window at all.

Direction of the change: windows are REMOVED, never added. Nothing about a
publication on or after 30 August 2016 moves - the one-month non-extendable
period, its day-after start and its rollover calendar are unchanged. If you
read an opposition window for an MX mark published before 30 August 2016, that
window was wrong and is now absent.

Trademark responses recompute the opposition window per request, so they are
correct as soon as this deploys. The `opposition_status` and
`opposition_closes_before`/`opposition_closes_after` search filters read
values materialised at index time, so until the MX index is rebuilt those
filters may still match Mexican marks published before 30 August 2016 even
though the returned record now carries no opposition window.

## September 2, 2026: India (CGPDTM) deadline and opposition rules

New jurisdiction. `IN` now has a maintenance config and two opposition rules,
so `GET /v1/deadline-rules`, `GET /v1/opposition-rules`,
`POST /v1/deadlines/compute` and the `derived` block on a trademark answer for
Indian marks instead of returning nothing. Four new `rule_id` values:
`in_renewal`, `in_restoration`, `in_opposition`, `in_opposition_madrid`.

Renewal runs 10 years from the APPLICATION date, not the grant: s. 23(1) of the
Trade Marks Act 1999 registers a mark "as of the date of the making of the said
application". The filing window opens a year before expiry (Rules 2017
r. 57(1)), a 6-month surcharge window follows expiry, and restoration is
available after 6 and within 12 months from the expiry itself. Marks registered
before the Act commenced on 15 September 2003 run 7-year periods until the
first period that starts on or after that date. Opposition is 4 months from
publication of the Trade Marks Journal issue that advertised the application,
counted from the day after (General Clauses Act 1897 s. 9(1)) and
non-extendable; it was 3 months for applications published before 8 July 2013.
An international registration designating India is opposed on the same 4-month
window, running from the INDIAN Journal republication rather than the WIPO
Gazette. Every value cites the statute, the Rules or the CGPDTM practice
document it comes from, with a verbatim quote and a pinned snapshot.

Six limits are documented rather than hidden. No closed-day roll is applied to
any Indian date: the law requires one but the Registry publishes no
consolidated holiday list, so `holiday_calendar` is `null` and every adjustment
reads `not_checked` rather than a guess. A mark claiming convention priority
expires on its PRIORITY date plus ten years, which the engine cannot compute
without an office-supplied expiry. A mark registered late may renew within six
months of its actual registration, so its real due date can be later than the
one published. A mark filed before 15 September 2003 but registered after it
takes a ten-year first term, where the schedule gives seven. And nothing in the
Act or Rules settles whether a renewal filed ON the expiry date avoids the
surcharge; the conservative reading is modelled. Lastly, the anchor of the
pre-2003 seven-year terms is not pinned to a quotable source: s. 159(6)
evidences the seven-year length but not the date it runs from, and India
Code's scan of the 1958 Act carries no text layer to quote, so if the
filing-date anchor is wrong the direction of the error is unknown.

Trademark responses recompute opposition windows per request, so they are
correct as soon as this deploys. Only the materialised `opposition_status` and
`opposition_closes_before`/`opposition_closes_after` search filters are baked
at index time, so IN search documents carry these values only after IN is
reindexed; no IN records are indexed today.

## September 2, 2026: Türkiye (TÜRKPATENT) deadline and opposition rules

New jurisdiction. `TR` now has a maintenance config and two opposition rules, so
`GET /v1/deadline-rules`, `GET /v1/opposition-rules`,
`POST /v1/deadlines/compute` and the `derived` block on a trademark answer for
Turkish marks instead of returning nothing. Three new `rule_id` values:
`tr_renewal`, `tr_opposition`, `tr_opposition_madrid`.

Renewal runs 10 years from the FILING date, with a 6-month filing window before
the term ends and a 6-month window after it against an additional fee; missing
both ends the right. Turkish law has no restoration and no declaration of use,
so neither is emitted: that absence is the finding, not a gap. Opposition is 2
months from the publication of the APPLICATION in the Bülten, non-extendable,
and identical for Madrid designations (SMK m. 14/1 makes an international
application produce the same results as a direct one); publications before
2017-01-10 get the 3 months the repealed KHK 556 gave. Every value cites the
statute or the TÜRKPATENT publication it comes from, with a verbatim quote and a
pinned snapshot.

Five limits are documented rather than hidden. **No business-day adjustment is
applied to any Turkish date**: no Turkish source states that a deadline falling
on a non-working day extends before the office, and TÜRKPATENT's own 2025-03-29
notice told users to file during an administrative holiday, so
`holiday_calendar` is `null` and both adjustments read `not_checked` on every
row. Renewal cycles whose anniversary falls before 2018-01-10 were governed by a
month-end-anchored *request* window that the engine does not yet express, so
their `window_opens` and `due_date` can be up to 30 days early; every such cycle
is in the past. (`grace_expiry` is not affected: the repealed KHK 556 anchored
the six-month lapse on the exact end of the term, which is what we publish.)
An application filed before 2017-01-10 but published after it legally had a
3-month opposition window while we publish 2: earlier than the true date,
never later. Applications published in Marka Bülteni 410 to 417, roughly
12 December 2022 to 27 March 2023, carry opposition end dates that
TÜRKPATENT's 2023-04-14 notice fixed per bulletin once the periods suspended
after the February 2023 earthquakes restarted; a one-off table is not a rule,
so those office-fixed close dates are not modelled and the notice is pinned to
keep the limit auditable. And whether a renewal request filed ON the day the
term ends is still surcharge-free is stated by neither SMK m. 23/2 nor the
office, so the exact anniversary is kept as the due date.

Trademark responses recompute opposition windows per request, so they are
correct as soon as this deploys. Only the materialised `opposition_status` and
`opposition_closes_before`/`opposition_closes_after` search filters are baked
at index time, so TR search documents carry these values only after TR is
reindexed; no TR records are indexed today, so that reindex belongs with the
TÜRKPATENT connector rather than with this change.

## September 2, 2026: Japan (JPO) deadline and opposition rules

New jurisdiction. `JP` now has a maintenance config and two opposition rules,
so `GET /v1/deadline-rules`, `GET /v1/opposition-rules`,
`POST /v1/deadlines/compute` and the `derived` block on a trademark answer for
Japanese marks instead of returning nothing. Four new `rule_id` values:
`jp_renewal`, `jp_restoration`, `jp_opposition`, `jp_opposition_madrid`.

Renewal runs 10 years from the registration date, with a 6-month filing window
before expiry and a 6-month late window after it; missing both extinguishes the
registration retroactively, and a further 6-month restoration window follows.
Opposition is POST-registration: 2 months from the day after the 商標掲載公報
issue, non-extendable, with the same window for Madrid designations. Deadlines
falling on a JPO closed day roll to the next open day. Every value cites the
statute or the JPO practice document it comes from, with a verbatim quote and a
pinned snapshot.

Three limits are documented rather than hidden: the published `window_opens` can
be 1 to 3 days earlier than 商標法第20条第2項 allows, and `grace_expiry` can be
up to 7 days early when the expiry lands on a closed day. Madrid designations
are also served a `jp_restoration` row that Japanese law does not give them.
All three are fixed by engine changes that ship separately.

Trademark responses recompute opposition windows per request, so they are
correct as soon as this deploys. Only the materialised `opposition_status` and
`opposition_closes_before`/`opposition_closes_after` search filters are baked
at index time, so JP search documents carry these values only after JP is
reindexed; no JP records are indexed today.

## September 2, 2026: opposition window opening day corrected (Benelux, Finland, Norway, Sweden)

Opposition windows for BOIP, PRH Finland, Patentstyret, and PRV Sweden now open
on the publication day, one day earlier than before. Their closing dates are
unchanged. The served opposition `rule_version` moves to `2026-09-02`.
`derived.ruleset_version` on trademark responses (backed by the internal
`DEADLINE_RULESET_VERSION` constant) also moves to `2026-09-02`. It is one
global stamp (the maximum `lastVerified` across all maintenance configs), so
it moves for every record in every jurisdiction, including ones this release
did not touch; this release bumped that maximum.
The version move on its own does not imply that any computed date changed.

**REINDEX required for these four offices.** Trademark responses recompute
opposition windows per request, so they are correct as soon as this deploys.
Only the materialised `opposition_status` filter stays stale until those
offices are reindexed, because `opposition_window_opens` is baked into existing
search documents; the `opposition_closes_before`/`opposition_closes_after`
filters are unaffected, because no closing date moves.
Benelux, Finland and Norway can be reindexed now.

**Sweden caveat.** For marks registered since December 2022, the publication
date we stored for PRV came from the gazette issue, which runs one day after
the registration decision (40 of 40 records sampled per month). PRV counts the
opposition window from the registration day itself. The connector correction
is in the "Swedish publication and expiry dates corrected" entry above; Swedish
opposition windows stay one day late until the PRV backfill and reindex
described there have run. Sweden is reindexed after that backfill, not with
the other three offices.

## September 2, 2026: German renewal dates corrected for applications pending on 14 January 2019

DE national marks filed before 14 January 2019 but registered on or after that
date now expire on the exact filing anniversary. They were previously shown
with a month-end deadline, which is later than the law allows (for example,
2028-10-31 instead of 2028-10-05). Marks already registered before the cutoff
retain month-end expiry, and office-reported expiry dates still take priority.
The correction is computed at request time; no reindex is required.

Scope of the change: only DE national marks filed before 2019-01-14, registered
on or after that date, and without an office-reported expiry date. Our deadline
evaluation harness holds no DPMA samples, so 0 records in it move. The first
renewals for this group fall in 2028 and 2029, so no affected deadline is
currently past due.

## September 2, 2026: French renewal grace end corrected (moves later)

French renewal grace periods previously ended early, so customers had more
time than Signa showed. They now run for six months from the day after
the registration expires, matching CPI R. 712-24 and R. 718-2 exactly.

Scope: no French renewal grace end moves earlier. Most move one day later. A
grace period whose due date falls on a month end can move up to four days
later, because the old and the new end clamp to different month ends: a due
date of 2026-02-28 moves its grace end from 2026-08-28 to 2026-09-01, a due
date of 2026-04-30 moves two days, and 2028-02-29 moves three. A few whose old
and new ends both clamp to the same month end do not move at all. The
correction is computed at request time, so no reindex is required (maintenance
deadlines are not indexed).

## September 2, 2026: office calendar corrections

Deadline rollover calendars now match the 2026 audit of official office
closures. No rule window or anchor changed.

Trademark responses recompute opposition windows per request, so every office
below is correct as soon as this deploys. Only the materialised
`opposition_status` and `opposition_closes_before`/`opposition_closes_after`
search filters stay stale until the offices named below are reindexed.

* **EUIPO:** 8 December 2025 and 2026 no longer roll; those are Article 2
  ordinary-post-only days, not Article 1 office closures. Reindex EU
  opposition documents.
* **CIPO:** weekend substitutes for 24 June and 30 September now roll on the
  following Monday. Those two need no reindex before the first affected 2028
  opposition cohort. Separately, the 17–19 September 2025 office closure now
  rolls through the weekend to 22 September, so CA documents published 17 to
  19 July 2025 carry an opposition close date up to five days early until CA
  is reindexed. Opposition status is unaffected: those windows are already
  closed.
* **IPOS:** 11 August 2025 and 20 March 2026 no longer roll; the excluded days
  1–16 May 2024 roll to 17 May, and 14 October 2025 rolls to 15 October.
  Reindex affected Singapore opposition documents.
* **IP Australia:** Canberra Day, Saturday and Sunday ANZAC Day substitutes,
  and the standing 25 December–1 January close-down now roll. For example,
  9 March 2026 rolls to 10 March, 27 April to 28 April, and 29 December to
  4 January 2027. Reindex Australian opposition documents.
* **IPI:** the conservative nationwide calendar now excludes canton-specific
  days, including 2 January, Good Friday, Easter Monday, Whit Monday, 1 May,
  26 December. The governing party or representative canton is not available
  to the engine, so deadlines are never moved later based on the wrong canton.
  Reindex Swiss opposition documents.
* **DKPTO:** 15 May and 28–30 December 2026 now roll as enumerated office
  closures. Reindex is not currently needed because Signa has no DK office
  documents; revisit when DK ingestion lands.
* **INPI France:** 15 May 2026 now rolls to 18 May under the Director General's
  closure decision. Reindex French opposition documents.
* **DPMA:** the one-off Berlin holiday on 8 May 2025 now rolls to 9 May. No
  reindex is required for the weekly weekday publication cohort.

## September 2, 2026: Era-aware opposition citations

The `source` on a computed opposition window now cites the instrument that
states the window actually served. For rules whose opposition period changed
on a fixed date, a publication before the change cites the earlier statute and
a publication on or after it cites the amending law. This covers all seven
opposition rules that change their period on a date, so a pre-change
publication now cites the instrument that states the period it was given:

* **Australia**, direct and Madrid, published before 2013-04-15 (3 months):
  Trade Marks Regulations 1995 reg 22.9 item 2 for a direct application and the
  2012 compilation's reg 17A.29(2)(b)(i) for an international registration,
  in place of the current reg 5.6(1) / reg 17A.33(1) "2 months".
* **India**, published before 2013-07-08 (3 months): Trade Marks Rules 2002
  r. 47(1), in place of s. 21(1) "four months".
* **Thailand**, published before 2016-07-28 (90 days): s. 35 paragraph 1 of the
  Trademark Act B.E. 2534 as in force before Act (No. 3) B.E. 2559, in place of
  the current s. 35 paragraph 1 "sixty days".
* **Türkiye**, direct and Madrid, published before 2017-01-10 (3 months):
  the repealed Decree-Law 556 art. 35/1, and WIPO Information Notice No.
  19/2014 for a designation, in place of SMK 6769 art. 18/1 "iki ay" for a
  direct application and the WIPO Madrid Member Profile ("two months from the
  date of publication of the international registration") for a designation.
* **Vietnam**, published before 2026-04-01 (5 months): Law 07/2022/QH15, in
  place of Law 131/2025/QH15, which states only the three-month period.

No window dates move, the `source` shape (`citation`, `url`) is unchanged, and
no reindex is required: the citation is computed at request time.

## September 2, 2026: Vietnamese opposition windows corrected (3 months since 1 April 2026)

Vietnamese trademark applications published on or after 2026-04-01 now have
a 3-month opposition window. These windows were previously shown 2 months too
late. Trademark responses recompute opposition windows per request, so they are
correct as soon as this deploys. Only the materialised `opposition_status` and
`opposition_closes_before`/`opposition_closes_after` search filters stay stale
until VN documents published on or after 2026-04-01 are reindexed.

## September 2, 2026: Brazilian and Mexican renewal deadlines

Renewal deadlines are now computed for Brazilian and Mexican registrations where
none were computed before; the grant-date trigger was never fed from stored
data. The affected cohort is BR and MX national-route registrations whose stored
registration date feeds that trigger; records carrying an international
registration number are excluded. The deadlines are computed at request time, so
no reindex is required (maintenance deadlines are not indexed).

## September 2, 2026: Icelandic renewal dates corrected

IS renewal deadlines: applications filed before 2020-09-01 are now anchored
on the registration date for every renewal, even when registration occurred
after the reform. They were previously shown early from the filing-date
anniversary. For every affected record that carries a registration date the
renewal moves later; none moves earlier. Records with an office-reported
expiry remain anchored on that expiry.

There is a third outcome. A pre-2020 IS record that carries neither a
registration date nor an office-reported expiry now returns no computed
renewal at all, where it previously returned one anchored on the filing date.
The filing date is the wrong anchor for that cohort, so the deadline is
withheld rather than guessed; send the registration date, or the office's
expiry date, to get a schedule back. This matches how the other
registration-anchored pivots (SE, FI and NO) already behave. Denmark is not in
that set: its pivot is keyed on the registration date rather than the filing
date, so a DK record with no registration date falls through to the modern
filing anchor and is served a schedule.

Renewal deadlines are computed at request time, so no reindex is required.
IS opposition dates did not change either: `opposition_window_opens`,
`opposition_window_closes` and `opposition_rule_version` in the search index
are unaffected. The index stores no rule citations at all. `GET
/v1/deadline-rules` and `GET /v1/opposition-rules` serve `sources[]` straight
from the deployed configuration, so the new IS primary-source citations take
effect with the deploy (within the 24-hour reference-cache window), and no
reindex is involved.

## September 2, 2026: Office rule reference data provenance

Reference data: deadline and opposition rules for AU, BR, BX, CA, CH, DE, DK,
EU, FI, FR, GB, MX, NO, PH, PL, SG, TH, US and WIPO now cite
primary sources with verbatim quotes and pinned snapshots. `sources[]` on
`GET /v1/deadline-rules` and `GET /v1/opposition-rules` changed for those
offices: law-firm guides, publisher summaries and mirrors of primary text were
replaced by the statute, regulation or office publication they paraphrased.
Nothing else in those responses moved, and no computed date changed from the
re-citation alone. IN, JP and TR are new rule sets rather than re-citations,
and are announced separately above.

For DE (DPMA) the opposition rule's five bare links to individual
gesetze-im-internet section pages are replaced by eleven pinned citations:
MarkenG §§ 41, 42 and 91, MarkenV § 27, BGB §§ 187, 188 and 193, DPMAV § 18a,
PatKostG § 6(1) with the opposition fee numbers, and the DPMA's own opposition
guideline W 7736 (Markenblatt publication, the Ereignisfrist arithmetic, and
the rule that statutory periods cannot be extended). The German renewal config
additionally cites PatKostG §§ 3, 5 and 7, BGB §§ 187 and 188, and three DPMA
practice pages carrying the worked renewal example. No computed dates changed.

For FI (PRH) both configs move to primary Finnish law. The renewal config's
two remaining links (an English PRH page and a law-firm alert) are replaced
by twelve citations: Tavaramerkkilaki 544/2019 §§ 16, 30, 106 and 107 (the ten
years from the application date, the one-year/six-month window, removal from
the register, and the 1 May 2019 commencement that pivots the anchor), the
repealed 7/1964 §§ 22 and 24 for marks applied for before it, the 1930 Act on
calculating time limits for the same-numbered-day arithmetic, and three Finnish
PRH pages. The opposition rule's own statutory links are re-pinned (544/2019
§ 32 and the 1930 Act) and joined by 544/2019 §§ 24 and 33, 7/1964 § 20 and
four PRH pages. A citation to a Government Decree on Trademarks (1268/2018) was
removed: that statute number belongs to an unrelated act and no decree governs
the Finnish opposition period. No computed dates changed.

For FR (INPI) the opposition rule loses all four of its previous links and
gains thirteen pinned citations. Two of the four were wrong as well as
unverifiable: the Légifrance article id cited for CPI art. L. 712-4 does not
exist, and the one cited for Code de procédure civile arts. 641/642 identifies
neither article (they are LEGIARTI000006411002 and LEGIARTI000006411003). The
rule now cites CPI arts. L. 712-4 (both the in-force text and the pre-2019
text, with L. 712-3, showing the two months have not changed since 1992),
R. 712-8 for the BOPI publication that starts the period, R. 718-2 for
the same-numbered-day arithmetic and the weekend/holiday roll, R. 712-14 for
the one further month allowed for the exposé des moyens, R. 712-15 for the
inadmissibility of a late opposition and R. 712-12 for the absence of any
restoration of it, plus INPI's opposition directives (July 2026) and its
current opposition page. The renewal config additionally cites CPI arts.
L. 712-1 and L. 714-5 and INPI's nullity/revocation page. Statutory text is
quoted from the DILA open-data LEGI publication of each article version,
because legifrance.gouv.fr blocks automated retrieval. No computed dates
changed.

For MX (IMPI) both configs move to primary Mexican law. The renewal config's
five previous citations - a WIPO Lex catalogue page and three law-firm guides,
plus WIPO Information Notice 13/2018 - become thirteen: LFPPI arts. 178, 21,
233, 237 and 238 and transitorios PRIMERO, DECIMO and DECIMO PRIMERO (the ten
years from grant, the six-month renewal window and six-month grace, the
declaration of use in the three months after the third year, and the
2018-08-10 and 2020-11-05 commencements), the abrogated Ley de la Propiedad
Industrial art. 95 for marks applied for before 5 November 2020, IMPI's own
LFPPI seminar deck, and Information Notice 13/2018 promoted in place with its
quote. The opposition rule's three links are replaced by seven citations:
LFPPI arts. 221, 21 and 18, Reglamento de la LPI arts. 4o and 59 TER, the
abrogated LPI art. 120 showing the one-month non-extendable period has not
changed since 2016, and the transitorio Único of the decree published in the
DOF on 1 June 2016, which is the text that dates the 2016-08-30 gate. All
three were unusable: every one of them pointed at diputados.gob.mx, which no
longer answers, and the Federal Code of Civil Procedure art. 286 is not the
instrument that governs IMPI time limits either.
Statutory text is quoted from the ordenjuridico.gob.mx consolidated PDFs. No
computed dates changed.

## September 2, 2026: The events feed is documented (beta)

[`GET /v1/events`](https://docs.signa.so/api-reference/monitoring/events/list) and
[`GET /v1/events/{id}`](https://docs.signa.so/api-reference/monitoring/events/retrieve) are now part
of the published API reference and the OpenAPI document, under Monitoring. They
were already live and callable with the `events:read` scope; what changed is
that they are documented and supported rather than undocumented.

The feed is your organization's append-only event ledger, retained 30 days.
Every webhook you receive is also a row in it, addressable by the same id (the
`webhook-id` header IS the `evt_*` id), which makes it the recovery path for an
outage your webhook retries could not cover: persist the last id you processed,
then ask for `?sort=id&after=evt_…`. A checkpoint older than the window returns
`410 replay_window_expired`; see
[Replay from a checkpoint](https://docs.signa.so/guides/monitoring/webhooks.md#replay-from-a-checkpoint).

**Beta means one specific thing here.** The ledger, the `evt_*` ids, the filters
and the replay rail are stable enough to build on. The per-event `data` payloads
are versioned by `payload_version` and their shape is **not** yet promised. Pin
`payload_version` in your consumer and read this changelog before upgrading.

No behaviour changed: same rows, same filters, same scopes. The MCP surface adds
a matching read tool, `signa_events_list`.

## September 2, 2026: Alert diffs use public field names (breaking, beta)

**`data.alert.event.diff[].path` on `alert.created` webhooks and
`GET /v1/alerts*` now uses public trademark field names**, the same ones
`trademark.status_changed` adopted. Effective on deploy; alerts are in beta and
this is the last chance to fix the vocabulary before it is frozen.

| Was (internal column) | Now (public field) |
| - | - |
| `status_primary` | `status` |
| `mark_text_primary` | `mark_text` |
| `publication_date_first` | `publication_date` |
| `international_registration_number` | `ir_number` |
| `challenge_states` | `status_challenges` |
| `trademark_owners` | `owners` |
| `trademark_attorneys` | `attorneys` |
| `trademark_classification` | `classifications` |
| `trademark_media` | `media` |
| every other `trademark_*` child table | its collection name |

Fields Signa tracks but does not publish (`is_primary_source`,
`mark_feature_type_raw`, `mark_legal_category_raw`, `application_language`) are
**dropped** rather than renamed, so a diff can be shorter than before, and
every `path` you now see is a field you can read back from
`GET /v1/trademarks/{id}`. `event.summary` is built from the same names, so the
headline reads `"Status changed: pending → registered"` where it used to read
`"Status primary changed: …"`.

Redeliveries (`POST /v1/webhooks/{id}/deliveries/{delivery_id}/redeliver`) and
automatic retry attempts of alerts created BEFORE this change are re-emitted
with the new names too (`event.summary` included, since the headline is
re-derived from the translated diff), so your handler only ever has to
understand one vocabulary. `GET /v1/events/{id}` applies the same projection to
the stored `alert.created` body, so the pull rail and a redelivered push of the
same event agree. Nothing about which alerts fire, or when, changed.

## September 2, 2026: Office dates and your docket reference on events

Three additive fields on `trademark.status_changed` and `office_action.issued`,
on both the webhook body and `GET /v1/events/{id}`. Nothing was renamed, no
stored value changed, and `payload_version` stays `1`.

**`source_date` is the office's date; `occurred_at` was always ours.**
`occurred_at` is when Signa produced the event: ingestion time for
`trademark.*` and `office_action.*`, alert-creation time for `alert.created`.
That was already true, and it is now documented per family instead of implied.
The new `source_date` is the office-reported data date of the change as we
stored it (`YYYY-MM-DD`, or `null` when the feed reports none), equal to
`event_date` on office actions. The two differ by the office publication lag,
typically 1 to 14 days, so **sort a docket by `source_date`** (meaningful within
one office) and use `occurred_at` to answer "when did I learn this". Its meaning
is honest rather than uniform: day precision, USPTO transaction date, WIPO
gazette date, snapshot offices the crawl date.

It is **not** a legal effective date; `changes.status_effective_date` is. We
deliberately do not fold the effective date into `source_date`, because an
office re-crawl can backdate it by years and your feed would travel backwards on
a mark that was merely re-read.

**`portfolios` carries your `external_ref`, frozen at record time.** Both
families now include the portfolios the mark was in when the event was recorded,
each with the reference you set on the membership:

```json theme={null}
"portfolios": [{ "id": "ptf_9aB2xY", "external_ref": "MATTER-4471" }]
```

It is a snapshot, not a live lookup. Re-keying a matter reference tomorrow never
rewrites an event you already received, which is what keeps a redelivery
byte-identical to the original and keeps `GET /v1/events/{id}` in agreement with
the body you verified the signature over. Empty array when the mark was in no
portfolio; `null` `external_ref` means unknown or unset, never "cleared".
`alert.created` does not carry either field.

The SDK adds `EventPortfolioRef` and the new fields on
`TrademarkStatusChangedData`, `OfficeActionIssuedData` and the org-event detail
types.

## September 2, 2026: Webhook subscriptions, portfolio scope, and diff field names

Three contract clarifications on the event surface, all landing together.

**Subscribing no longer depends on our rollout state.** `enabled_events` accepts
`alert.created`, `trademark.status_changed` and `office_action.issued` at all
times. The last two are accepted for subscription but **not yet emitted**:
they begin delivering when the API events projector is enabled. Previously the
accepted set widened with our rollout, which meant the published OpenAPI
document described a different API depending on the environment you generated
your client against. Subscribe now and you receive them from the first delivery.

**`portfolio_id` means one thing.** An endpoint's `portfolio_id`, and
`GET /v1/events?portfolio_id=`, now filter **every** event type against the
portfolio membership recorded when the event was recorded, `alert.created`
included. Previously alerts bypassed the filter entirely, so a portfolio-scoped
endpoint or query returned every alert in the organization regardless of
portfolio. If you scoped an endpoint to a portfolio and relied on it still
receiving all alerts, clear `portfolio_id` on that endpoint (or add a second,
unscoped endpoint): similarity, class and owner watches match marks you do not
own, and those alerts have no portfolio membership.

**`changed_fields` and `changes` use public field names.** The
`trademark.status_changed` payload and `GET /v1/events/{id}` now key the diff by
the field names on the trademark resource instead of our internal column names:
`status_primary` becomes `status`, `mark_text_primary` becomes `mark_text`,
`publication_date_first` becomes `publication_date`,
`international_registration_number` becomes `ir_number`, `challenge_states`
becomes `status_challenges`, and changed child collections read as `owners`,
`classifications`, `media` and so on. Internal-only columns are omitted rather
than renamed, so both lists can be shorter than before. Every name you now see
is a field you can read back from `GET /v1/trademarks/{id}`.

## September 2, 2026: Richer goods/services validation evidence

`POST /v1/goods-services/validate` now adds descriptive wording coverage,
candidate spans and USPTO template drafting aids, suggested class inference,
cited indefinite-wording warnings, catalog `as_of` freshness, and explicit
candidate-search completeness. Summary fields roll up inferred items, warnings,
and complete, truncated, or degraded searches. `listed` and `not_listed` keep
their existing catalog-membership meaning. The TypeScript SDK exposes the new
types in version 0.12.0; version 0.11.0 was never published, so no published SDK
contract is broken.

## September 2, 2026: Goods/services catalog validation

`POST /v1/goods-services/validate` checks submitted wording against the
pre-approved catalogs available for US, EM, JP, KR, and CN. `listed` means the
wording appears in that office's catalog. `not_listed` is an absence of
evidence, not a refusal. Other recognized offices return `unsupported_office`
with reason `no_catalog_data`.

The endpoint returns one result per input item, bills per item, and can provide
same-class candidate wording when `class_number` is supplied. An optional
`Idempotency-Key` enables safe replay. The TypeScript SDK exposes it as
`goodsServices.validate()` in version 0.11.0.

## September 2, 2026: Entity lists and suggest answer 503 when OpenSearch is unavailable

`GET /v1/owners`, `/v1/attorneys`, `/v1/firms`, `/v1/entities`, `/v1/suggest`
and `/v1/trademarks/suggest` no longer fall back to a degraded Postgres page or
an empty result when OpenSearch is unavailable. They answer `503
service_unavailable` with a `Retry-After` header instead. The SDK retries 503
automatically. Cursors and result ordering are unchanged on the happy path.

## September 2, 2026: Saved searches removed

The `/v1/saved-searches` endpoints are gone and now answer `404`. The
`signa.savedSearches` SDK resource, the `SavedSearch` types, the
`from_saved_search` option on `signa.watches.create` and the `ssr_` id prefix
were removed with them. The feature had no production usage. Store the search
body on your side and re-send it to `POST /v1/trademarks`, or create a watch
with the same query.

## September 2, 2026: Swedish renewal dates corrected

SE renewal deadlines: registrations applied for before 2019-01-01 are now
anchored on the registration date per the transitional rule (SFS 2018:1652
p. 4 a), whenever they were registered. About 1,300 active marks change
date, in both directions: roughly 1,290 move later (they were shown up to
6 years early), and 8 move earlier or gain a deadline that was previously
outside the horizon (their filing-anchored cycle had already elapsed; the
registration-anchored one lands sooner, by up to 2,445 days). Computed at
request time, no reindex. Records with an office-reported expiry are
unaffected.

## September 1, 2026: Office-action webhook events

`office_action.issued` is now a subscribable, versioned semantic event. It
fires once for each newly stored office action on a portfolio mark and carries
the stored prosecution-history fields using public office-code vocabulary.
Emission begins when the API events projector is enabled.

A first detail fetch that newly inserts multiple office actions stores them
without webhook emission. A single-action history may emit one
`office_action.issued`: one action is indistinguishable from a genuinely new one,
and a stale event is safer than silently dropping a real refusal notice. Once
history is known, each later new action emits once.

## September 1, 2026: Trademark status webhooks and portfolio-scoped endpoints

`trademark.status_changed` is now available as a subscribable webhook event
type. It carries the spine-native event envelope; emission begins when the API
events projector is enabled. Endpoints without a `portfolio_id` receive every
`trademark.status_changed` event for the organization; setting a `portfolio_id`
narrows those deliveries to marks in that portfolio.

`POST /v1/webhooks`, `PATCH /v1/webhooks/{id}`, and `GET /v1/webhooks` now
expose the additive `portfolio_id` field.

## September 1, 2026: Webhook event envelope (BREAKING)

**This is a breaking webhook contract change.** For `alert.created`, the
envelope `id` and signed `webhook-id` header now use the spine event identifier
(`evt_*`) instead of the alert identifier (`alt_*`). The payload is no longer a
flat merged field bag: `data` is now `{ event_id, alert_id, alert }`, with the
rich alert resource nested under `data.alert`.

`webhook-id` is the documented idempotency key. Migrate or reset stored
idempotency keys during this upgrade so the `alt_*` to `evt_*` identity change
does not cause incorrect duplicate handling. Update consumers to read paths
such as `data.alert.id` and `data.alert.event.type`.

## September 1, 2026: Forward replay on `GET /v1/events`

The event feed gains a second rail. The default feed keeps its shape and its
rows: newest first, opaque cursor, same order (list items do gain two
timestamp fields, detailed below). What is new is a forward rail a sync client
can resume from, plus two behaviour changes on the existing surface that you
need to know about before you upgrade.

**Discard any `GET /v1/events` cursor you have stored.** Event cursors are now
bound to the query that minted them. The payload carries the sort direction and
a fingerprint of the filters, the limit and the sort, so a cursor can only be
replayed against the identical query. Every cursor minted before this change
lacks both, and now returns `400 cursor_invalid`. Cursors were always documented
as opaque and short-lived, so this only affects you if you persisted one.
Restart from page one without a cursor.

`cursor_invalid` is a different failure from `cursor_expired`, and the split is
deliberate. `cursor_expired` means the value is not a cursor: malformed,
unsigned, or minted for another endpoint. Restart from page one.
`cursor_invalid` means the cursor is genuinely ours but is bound to a different
query than the one you just sent, usually because a filter or the limit changed
mid-pagination. Restart from page one with the parameters you actually want.

**`since` now filters on when Signa recorded the event, not when the office
reported it.** It used to bind the row's creation timestamp; it now binds
`recorded_at`, Signa's ledger clock. This is what a sync client wants. Source
timestamps can be backdated by an office arbitrarily far, so filtering on them
silently skips late-arriving rows that were recorded after your last poll.
Every event now carries both clocks explicitly, `occurred_at` for the office
and `recorded_at` for us, so nothing is lost. `created_at` stays as an alias of
`recorded_at`.

**`sort=id` turns the feed into a replay rail.** Pair it with
`after=<evt_ id>` to resume from a checkpoint you persisted, exclusive of the
checkpoint itself. Ledger sequence is the only sortable field, and `after` is
valid only with `sort=id`. `sort=-id` remains the default; its ordering and
cursor behaviour are unchanged. List items gain `occurred_at` and `recorded_at`,
and `trademark_id` is now nullable.

```bash theme={null}
curl "https://api.signa.so/v1/events?sort=id&after=evt_3D7rZ9&limit=100" \
  -H "Authorization: Bearer $SIGNA_API_KEY"
```

**A checkpoint older than the retained window is a `410`, not a silent gap.**
Replay is bounded by retention. If the events after your checkpoint have been
pruned, the response is `410 replay_window_expired` rather than a `200` that
quietly omits them. The floor is per organization, so another tenant's
retention never affects your answer. Recovery is to start a new initial sync
with `sort=id` and no `after`, then persist the newest event ID you see. The
TypeScript SDK maps `410` to a new `GoneError`.

**`portfolio_id` is family-aware.** It filters every event type through the
portfolio membership snapshot captured when the event is recorded, so later
membership changes never rewrite history. That includes `alert.created`: an
alert is in a portfolio's feed only when the alerting mark was in the portfolio
at the moment the alert was recorded, and similarity, class and owner watches
routinely match marks you do not own, which have no membership. This is
identical to portfolio-scoped webhook delivery. The parameter requires
`portfolios:manage` in addition to `events:read`.

## September 1, 2026: Examiner citations

Three new endpoints, a new count on the trademark detail, and a per-office
capability flag. Everything here is additive. The only thing that changes for
an existing integration is that detail `ETag`s rotate once, at deploy.

**Citations are now a first-class resource.** A citation is a prior mark an
examiner cited against a pending application. `GET /v1/citations` queries them
across marks, filterable by `offices`, `disposition`, `action_stage`,
`refusal_type`, `trademark_id`, `cited_trademark_id`, `cited_ref` and an
`action_date` range. Each row is one citation occurrence: one prior mark, one
application, one office action.

**And per mark, in both directions.**
`GET /v1/trademarks/{id}/citations` returns what was cited against that
application. `GET /v1/trademarks/{id}/cited-by` returns the applications that
mark has been cited against, which is the question a portfolio owner actually
asks. `cited-by` also picks up citations matched by office-printed reference
where Signa has not linked the row to a record yet, and the
`?cited_trademark_id=` filter on `GET /v1/citations` matches the same rows, so
the two views agree.

**`citations_count` on the trademark detail.** `GET /v1/trademarks/{id}` and
`POST /v1/trademarks/batch` carry a new `citations_count`. It is present on
every full-detail response, including the batch lookup surface, and it is
nullable. Like `events_count` and `proceedings_count`, it is dropped by a
sparse `?fields=` projection that does not ask for it. An integer, including `0`, means the office is
covered and that is the real count. `null` means citations are not counted for
that office at all, which is not the same as "there are none".

**`capabilities.citations` tells you which is which.** `GET /v1/offices` and
`GET /v1/offices/{code}` now carry a `capabilities` block whose `citations`
field reads `available`, `in_progress`, `not_available` or `not_applicable`.
`available` offices return a number in `citations_count`; every other state
returns `null`. `not_applicable` is the useful one: an office that does no
ex-officio relative-grounds examination never issues a citation-bearing
refusal, so its empty list is a fact about the regime rather than a gap in our
coverage. Branch on this field instead of inferring coverage from an empty
response. Coverage today is USPTO §2(d) refusals.

**SDK.** `signa.citations.list()`, `signa.trademarks.citations(id)` and
`signa.trademarks.citedBy(id)`, all auto-paginating like every other list.

**Detail `ETag`s rotate once.** The representation salt goes from `v3` to
`v4`, so the validator format is now `"v4.{version}-{epoch}"` plus the
`derived` content hash, plus a `citations_count` hash on covered offices.
Every cached trademark detail body misses once and refetches. Nothing to
change on your side. The extra hash exists because citations are written by
the extraction pipeline, which does not touch the record's version: without
it, a client holding an `ETag` would be revalidated onto a stale count
forever.

## September 1, 2026: Every rule has a stable id

Deadline and opposition rules now carry a permanent `rule_id`, so a computed
date can be traced to the rule that produced it and joined to the catalogue
that documents it. `rule_id` is an opaque slug (do not parse it), and it will
not be renamed once published.

Alongside it, two dates that were previously conflated are now separate.
`effective_from` is the earliest date from which a rule's modelled content is
authoritative, and is `null` where the corpus does not establish one.
`last_verified` is when we last checked the rule against its sources; on
opposition rules that is the value `rule_version` has always carried.

`POST /v1/deadlines/compute` rows gain `rule_id`, `effective_from`,
`last_verified` and the business-day provenance `due_date_adjustment`,
`grace_expiry_adjustment` and `holiday_calendar`. The two adjustment fields
are `moved`, `unchanged` or `not_checked`. `not_checked` covers a
jurisdiction that does not observe the roll, an office with no wired calendar,
and a date beyond a calendar's coverage, so a date we never checked is never
reported as one we checked and left alone. Each item also carries the
jurisdiction's `sources`, plus `as_of` and `computed_at`, which together make
a response replayable as a record of what was computed and when.

`POST /v1/oppositions/compute` gains `rule_id` and `effective_from`;
`rule_version` is unchanged. Both catalogues expose the same three fields, and
`GET /v1/deadline-rules` now also serves the restoration rules for Great
Britain, Singapore and Iceland, which the engine could always compute but the
catalogue never listed. Trademark detail, portfolio deadlines and watch
diagnostics carry `rule_id` on each row.

All fields are additive. One caching note: the trademark detail response
folds a content hash of its `derived` block into the ETag, and that block now
carries `rule_id`, so detail validators rotate once on this release.

## September 1, 2026: Prosecution deadlines on `/v1/deadlines/compute`

`POST /v1/deadlines/compute` now computes **prosecution** deadlines as well as
maintenance ones: office action response periods and statement of use clocks,
resolved per fact instance. Send an optional `facts` array on any item and the
result gains `prosecution_deadlines[]` and `support[]`.

**This is additive.** `prosecution_deadlines[]` and `support[]` appear only on
items that sent `facts`. (The same release adds rule identity to every item
(see the entry above), so an item you send without `facts` is unchanged apart
from those provenance fields.)

**Every computed date cites its rule.** Each prosecution deadline carries the
rule id, the era it belongs to (`effective_from` / `effective_until`), which
case date selected that era, when the rule was last verified against source,
and the sources themselves with pinpoint section, verbatim quoted text and a
sha256 of the snapshotted document. A computed date that cannot cite its rule
does not ship.

**Support is now per deadline type, and it is honest about coverage.** A
single `supported: boolean` could not say that a US mark has a renewal
schedule but no office action deadline, so `support[]` reports one entry per
type with a four-valued `status`:

* `computed`, a modelled rule produced or retired a date
* `stated`, no modelled rule exists but the office stated the deadline itself
* `facts_required`, a rule exists and is verified, but you must supply the
  trigger
* `unsupported`, no rule is modelled

Today that reads: USPTO computes from stored or supplied events. EUIPO rules
are verified but Signa stores no EUIPO examination events, so EUIPO is
`facts_required` until you send the trigger. There is no Madrid level rule and
there will not be one, because the Madrid System does not set the holder
response period; it is designated office law. For an unmodelled contracting
party, send a `stated_deadline` fact carrying what the office declared and the
date comes back with the notification as its authority.

The item level `unsupported_reason` enum widens from 2 values to 20 so both
engines share one reason vocabulary. The field itself still only emits the two
maintenance values; the new codes appear on `support[].reason` and
`prosecution_deadlines[].reason`. Treat it as an open enum.

Billing is unchanged: 1 unit per item, whether or not the item carries facts.

## August 31, 2026: Computed data moves under `derived`

A response-shape change on the trademark detail and batch endpoints, plus a
new provenance field. This is a breaking change: `deadlines[]` and
`opposition_window` move, and three fields on each deadline row go away. We
are shipping it during beta, before anyone has built on the old shape.

**Everything Signa computes now lives under `derived`.** `deadlines[]` becomes
`derived.deadlines[]` and `opposition_window` becomes
`derived.opposition_window`. The split makes the boundary visible. Fields at
the top level are what the office told us. `derived` is what we computed.
Nothing about what we compute has changed, only where it sits.

**Deadline rows now carry dates, not countdowns.** `status`, `urgency` and
`days_until_due` are gone from the trademark response. Each row instead
carries `window_opens`, `due_date` and `grace_expiry`, plus new
`trigger_date`, `trigger_field`, `recurring` and `optional` fields.

The reason is caching. Those three fields were functions of the day you read
the response, but the response is cached and revalidated against a
record-versioned `ETag`. A client that held an `ETag` and revalidated a week
later got a `304` and kept serving last week's countdown, indefinitely. A
value that changes without the record changing cannot live in a cacheable
body.

Deriving them is a couple of lines against your own clock:

```typescript theme={null}
const today = new Date().toISOString().slice(0, 10);
const overdue = d.due_date < today;
const inGrace = overdue && d.grace_expiry !== null && d.grace_expiry >= today;
```

If you would rather the server do it, `POST /v1/deadlines/compute` takes an
explicit `as_of_date` and still returns `status`, `urgency` and
`days_until_due`. Its clock is an input, so it caches correctly.
`GET /v1/portfolios/{id}/deadlines` is unchanged.

**Opposition windows lost their clock fields too.** `derived.opposition_window`
drops `status`, `days_until_open` and `days_remaining`, and adds
`rule_version`. On search and list rows, `opposition_window` keeps
`window_opens` and `window_closes` and drops `status`. The `opposition_status`
filter is unaffected. The server still evaluates it, so filtering by `open` or
`not_started` works exactly as before.

**An empty schedule now says why it is empty.** `[]` used to be ambiguous
between "this mark has no deadlines" and "we declined to compute one".
`derived.deadlines_supported` is now `false` with a
`derived.deadlines_unsupported_reason` of either `unsupported_jurisdiction` or
`requires_office_date`, the same vocabulary `POST /v1/deadlines/compute`
already returns. `derived.ruleset_version` stamps the rule state that produced
the block.

**`renewal_due_date` now declares where it came from.** It sits at the top
level next to office-reported dates, but it is office-stated for some records
and computed by the connector for others, and nothing said which. The new
`renewal_due_date_basis` reads `reported`, `derived` or `unknown`, exactly
like `expiry_date_basis`. Connectors adopt it one at a time, so expect
`unknown` on records whose connector has not declared one yet.

**Detail `ETag`s all rotate.** As of this release the validator format is
`"v3.{version}-{epoch}"` with a content hash of the `derived` block appended,
so cached copies of the old shape miss and refetch. Nothing to do on your side
beyond expecting one full response per cached mark. (The `v3` salt moved to
`v4` in the September 1 release below.)

## August 30, 2026: Deadline engine statutory corrections

A correctness pass over the maintenance deadline engine. This is a behavior
change: the same trademark can now return a different `status`, a different
number of `deadlines[]` rows, and different dates on four jurisdictions. Read
this before upgrading if you drive filing workflows off computed deadlines.

**The due day and the last grace day are now actionable, not missed.** Status
boundaries are inclusive on the actionable side. On the due date a deadline
reads `due_soon`, not `missed`. On the last day of the grace period it reads
`in_grace`, not `missed`. `missed` now begins the day *after* grace expiry (or
the day after the due date where a rule has no grace period, such as the
Mexican third-year declaration of use). If you filtered for `missed` to build a
"too late to file" list, that list previously included two days you could still
file on.

**Refused applications no longer return a maintenance schedule.** A refused
application has nothing to maintain, so `deadlines[]` is now `[]` for it.
The one exception is Madrid-route records: a provisional refusal from a
designated office is routinely overcome, so Madrid-route marks keep computing
their schedule.

**Expired and cancelled marks now return their still-actionable rows instead of
an empty array.** These statuses are recoverable for a bounded window, and
returning `[]` hid exactly the deadlines a customer needs at that moment. An
`expired` mark now returns its open late-renewal window (the current cycle
while its filing window or grace period is open) plus its restoration row
where the jurisdiction provides one (GB, SG, IS). A `cancelled` mark returns
its restoration row only, where modeled. Statuses that are terminally dead
(`abandoned`, `withdrawn`, `surrendered`, `invalidated`, and `refused`, except
the WIPO international registration record itself under a provisional
refusal) still return `[]`.

* **This can add rows to marks you already fetch.** A cancelled GB mark that
  returned no deadlines yesterday can return a restoration row today. Client
  code that assumed a dead mark implies an empty array should be checked.
* **Rows can also disappear for expired marks without a reported expiry.** The
  engine will not invent a lapse it cannot corroborate: an expired mark with
  no office-stated expiry date is served only the cycle whose window is
  currently open, never a restoration row derived from an assumed earlier
  miss. Clients alerting on `deadlines[]` shrinking should expect this.

**Restoration rows are no longer flagged optional.** Restoration is the only
way back onto the register once removal is final, so `optional` is now `false`
on those rows, and `include_optional=false` no longer suppresses them. If you
pass `include_optional=false` on `POST /v1/deadlines/compute` to trim the
response, expect restoration rows to appear where they did not before.

**Four statutory anchors were corrected.** These change computed dates, not
just labels.

* **Denmark:** the 2019 regime split is keyed on the **registration** date, not
  the filing date (Lov 1533/2018 §8 stk. 3). Marks filed before 2019-01-01 but
  registered after it were anchored up to a year off.
* **Mexico:** the 2020 regime split is keyed on the **filing** date, not the
  grant date (LFPPI Transitorio DÉCIMO). Applications in prosecution when the
  LFPPI entered into force on 2020-11-05 stay on the old filing-anchored term.
* **Philippines:** the third-year declaration of use window opens at the
  **filing** date and runs to the third anniversary of filing (RA 8293 §124.2).
  It was previously computed as a single day.
* **Thailand:** publications before 2016-07-28 get the **90-day** opposition
  window that was in force at the time. The 60-day window applies only from the
  2016 amendment forward.

## July 25, 2026: Jurisdiction protection scope

A precision fix to what `jurisdictions` means, plus honest screening verdicts
when register coverage is incomplete. This is a behavior change: the same
request can now return more results and, for screening, a new verdict value.
Because it changes result sets during beta, read this before upgrading.

**`jurisdictions` now selects rights that protect, or seek protection, in a
territory, not just literal territory legs.** Previously `jurisdictions=FR`
matched only French national filings and Madrid designations of France. It now
also matches regional rights whose membership covers France, so a EU trade mark
(which protects France) is included. See
[Search Trademarks](https://docs.signa.so/api-reference/trademarks/search-trademarks.md) and the
[glossary](https://docs.signa.so/guides/glossary.md).

* **Result counts grow.** A country-scoped filter picks up the regional rights
  that protect it. On production data, `jurisdictions=FR` roughly doubles once EU
  trade marks are counted. Countries with no live national office but active EU
  coverage (for example `DE`) go from a handful of Madrid hits to full EUTM
  coverage.
* **`territory_match=direct` restores the old behavior.** Pass it to match only
  literal territory legs, byte-for-byte the pre-July-2026 result set. The default
  is `territory_match=protection`. Requesting a regional code (`EU`) stays strict
  on search: it matches EUTMs and IRs designating the EUIPO, not member-state
  national marks.
* **Why results grew is now explained.** Under the default `protection` mode,
  each hit returned with a `jurisdictions` filter carries `territory_matches[]`,
  mapping the requested code to the territory it matched on and the basis
  (`direct` leg or `regional_membership`). The field is absent under
  `territory_match=direct`. `search_meta.territory_match` echoes the mode that ran.
* **Suggest and image search grow too.** `GET /v1/trademarks/suggest` and
  `POST /v1/trademarks/search/image` apply the same protection-scope expansion and
  accept `territory_match`. Suggest now also matches Madrid designations of a
  requested territory (an international registration designating France is
  suggested under `jurisdictions=FR`), and image search echoes the applied mode as
  `search_meta.territory_match`.
* **Member-country codes are newly accepted.** Benelux member states (`BE`, `LU`,
  `NL`) and OAPI member states (for example `SN`) had no jurisdiction row of their
  own and previously returned `400`. They are now valid `jurisdictions` values, so
  `jurisdictions=BE` selects Benelux and EU rights that protect Belgium.
* *Saved searches were removed on September 2, 2026.* **Saved searches replay under the new semantics automatically.** A stored
  search with `jurisdictions=FR` is re-executed against the current matcher, so
  it now returns EU trade marks it did not before, with no change on your side.
  If you need a saved search to keep the old literal-leg behavior, re-save it with
  `territory_match=direct`.
* **Breaking for pagination.** Because the query shape of a jurisdiction-filtered
  search changed, pagination cursors minted before this release on such a query
  now return `400 cursor_invalid`. Clients holding a stale cursor must restart
  pagination from the first page.
* **During the rolling deploy, expect a brief mixed window.** While the release
  rolls out, requests may land on either old or new instances, so a
  jurisdiction-filtered query can alternate between the old and new result sets,
  and `territory_match` may return `400` from a not-yet-updated instance. Stale
  jurisdiction-filtered cursors invalidate as above. The window closes once every
  instance is on the new release; retry the request if you hit it.

**Screening can now return `needs_review` when register coverage is incomplete.**
A clearance verdict is only trustworthy if Signa can actually see the marks that
protect a requested territory. When the register that would carry a conflict is
not connected live, a zero-conflict screen must not read as `clear`.

* **New verdict value.** `GET /v1/screening` verdicts are now
  `high_risk` / `caution` / `needs_review` / `clear`. `needs_review` is a
  would-be `clear` that could not be confirmed because a requested territory's
  register is dark or stale. Handle it as "we could not rule this out", not as a
  weaker clear. Found conflicts still band high or medium exactly as before,
  observed risk is never suppressed.
* **Why this is deliberate.** Before this change, screening a common brand in a
  class scoped to a territory whose national register was not yet connected could
  return `clear` with zero conflicts, even though real national marks existed and
  were simply invisible to us. Returning `clear` there is a false negative on a
  clearance product. We would rather tell you the corpus was incomplete than
  imply a name is safe.
* **A new `coverage` block** on the screening response reports which registers
  gated the verdict (offices axis in ST.3 codes, territories axis in jurisdiction
  codes) and a `complete_for_clear` boolean. See
  [Screen a Name](https://docs.signa.so/api-reference/screening/screen.md#coverage).
* **Check a Listing** gets the same protection-scope expansion, and an incomplete
  requested register forces a would-be `clear` to `caution` (the listing verdict
  enum is unchanged).
* **Screening responses are no longer client-cacheable.** They are served
  `Cache-Control: private, no-store`. Re-run a screen rather than replaying a
  stored response.
* **`screening_id` values changed at this epoch.** The identifier now folds the
  coverage inputs that can flip a verdict, so ids minted before this release do
  not correspond to ids minted after. Do not treat a pre-release `screening_id`
  as stable across this boundary.
* **Watches are unchanged.** Monitoring keeps office-based scoping and does not
  apply the protection-scope expansion, so a watch scoped to a country still
  watches that country's office only (a `DE` watch does not surface EU trade
  marks). See [Create Watch](https://docs.signa.so/api-reference/monitoring/watches/create.md).

## July 14, 2026

* **Compare (beta):** `POST /v1/compare`, its TypeScript SDK resource, and its
  developer guide complete the public-facing Intelligence surface alongside
  Screening. Compare scores one candidate against up to 10 selected conflicts.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.