Compute Oppositions
curl --request POST \
--url https://api.signa.so/v1/oppositions/compute \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"items": [
{
"office": "<string>",
"filing_route": "<string>",
"publication_date": "<string>",
"filing_date": "<string>",
"publication_date_kind": "<string>"
}
],
"as_of_date": "<string>"
}
'import requests
url = "https://api.signa.so/v1/oppositions/compute"
payload = {
"items": [
{
"office": "<string>",
"filing_route": "<string>",
"publication_date": "<string>",
"filing_date": "<string>",
"publication_date_kind": "<string>"
}
],
"as_of_date": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
items: [
{
office: '<string>',
filing_route: '<string>',
publication_date: '<string>',
filing_date: '<string>',
publication_date_kind: '<string>'
}
],
as_of_date: '<string>'
})
};
fetch('https://api.signa.so/v1/oppositions/compute', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.signa.so/v1/oppositions/compute",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'items' => [
[
'office' => '<string>',
'filing_route' => '<string>',
'publication_date' => '<string>',
'filing_date' => '<string>',
'publication_date_kind' => '<string>'
]
],
'as_of_date' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.signa.so/v1/oppositions/compute"
payload := strings.NewReader("{\n \"items\": [\n {\n \"office\": \"<string>\",\n \"filing_route\": \"<string>\",\n \"publication_date\": \"<string>\",\n \"filing_date\": \"<string>\",\n \"publication_date_kind\": \"<string>\"\n }\n ],\n \"as_of_date\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.signa.so/v1/oppositions/compute")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"items\": [\n {\n \"office\": \"<string>\",\n \"filing_route\": \"<string>\",\n \"publication_date\": \"<string>\",\n \"filing_date\": \"<string>\",\n \"publication_date_kind\": \"<string>\"\n }\n ],\n \"as_of_date\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signa.so/v1/oppositions/compute")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"items\": [\n {\n \"office\": \"<string>\",\n \"filing_route\": \"<string>\",\n \"publication_date\": \"<string>\",\n \"filing_date\": \"<string>\",\n \"publication_date_kind\": \"<string>\"\n }\n ],\n \"as_of_date\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"object": "list",
"data": [
{
"object": "opposition_window",
"supported": true,
"rule_id": "eu_opposition",
"effective_from": null,
"jurisdiction_code": "EU",
"office_code": "EM",
"filing_route": "domestic",
"publication_date": "2026-05-01",
"window_opens": "2026-05-02",
"window_closes": "2026-08-03",
"status": "open",
"days_until_open": null,
"days_remaining": 29,
"office_time_zone": "Europe/Madrid",
"trigger_event": "application_publication",
"rule_version": "2026-05-06",
"common_extension": null,
"source": {
"citation": "Article 46(1) EUTMR (Regulation (EU) 2017/1001)",
"url": "https://eur-lex.europa.eu/eli/reg/2017/1001/oj/eng"
},
"reason": null
}
],
"has_more": false,
"pagination": { "cursor": null },
"request_id": "req_bT9kM3nP"
}
Deadlines
Compute Oppositions
Compute trademark opposition windows from publication dates
POST
/
v1
/
oppositions
/
compute
Compute Oppositions
curl --request POST \
--url https://api.signa.so/v1/oppositions/compute \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"items": [
{
"office": "<string>",
"filing_route": "<string>",
"publication_date": "<string>",
"filing_date": "<string>",
"publication_date_kind": "<string>"
}
],
"as_of_date": "<string>"
}
'import requests
url = "https://api.signa.so/v1/oppositions/compute"
payload = {
"items": [
{
"office": "<string>",
"filing_route": "<string>",
"publication_date": "<string>",
"filing_date": "<string>",
"publication_date_kind": "<string>"
}
],
"as_of_date": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
items: [
{
office: '<string>',
filing_route: '<string>',
publication_date: '<string>',
filing_date: '<string>',
publication_date_kind: '<string>'
}
],
as_of_date: '<string>'
})
};
fetch('https://api.signa.so/v1/oppositions/compute', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.signa.so/v1/oppositions/compute",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'items' => [
[
'office' => '<string>',
'filing_route' => '<string>',
'publication_date' => '<string>',
'filing_date' => '<string>',
'publication_date_kind' => '<string>'
]
],
'as_of_date' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.signa.so/v1/oppositions/compute"
payload := strings.NewReader("{\n \"items\": [\n {\n \"office\": \"<string>\",\n \"filing_route\": \"<string>\",\n \"publication_date\": \"<string>\",\n \"filing_date\": \"<string>\",\n \"publication_date_kind\": \"<string>\"\n }\n ],\n \"as_of_date\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.signa.so/v1/oppositions/compute")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"items\": [\n {\n \"office\": \"<string>\",\n \"filing_route\": \"<string>\",\n \"publication_date\": \"<string>\",\n \"filing_date\": \"<string>\",\n \"publication_date_kind\": \"<string>\"\n }\n ],\n \"as_of_date\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.signa.so/v1/oppositions/compute")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"items\": [\n {\n \"office\": \"<string>\",\n \"filing_route\": \"<string>\",\n \"publication_date\": \"<string>\",\n \"filing_date\": \"<string>\",\n \"publication_date_kind\": \"<string>\"\n }\n ],\n \"as_of_date\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"object": "list",
"data": [
{
"object": "opposition_window",
"supported": true,
"rule_id": "eu_opposition",
"effective_from": null,
"jurisdiction_code": "EU",
"office_code": "EM",
"filing_route": "domestic",
"publication_date": "2026-05-01",
"window_opens": "2026-05-02",
"window_closes": "2026-08-03",
"status": "open",
"days_until_open": null,
"days_remaining": 29,
"office_time_zone": "Europe/Madrid",
"trigger_event": "application_publication",
"rule_version": "2026-05-06",
"common_extension": null,
"source": {
"citation": "Article 46(1) EUTMR (Regulation (EU) 2017/1001)",
"url": "https://eur-lex.europa.eu/eli/reg/2017/1001/oj/eng"
},
"reason": null
}
],
"has_more": false,
"pagination": { "cursor": null },
"request_id": "req_bT9kM3nP"
}
Credits: 10 per item.
Every item carries the same key set, supported or not: unsupported items null the window fields rather than omitting them.
Overview
Compute the opposition window for a published mark (when it opens, when it closes, and how much time is left) from a publication date, without storing anything. Opposition windows are short, unforgiving, and specific to each office. When a conflicting mark publishes, the oppose-by date is what separates a cheap, procedural opposition now from a costly cancellation later. Send a publication date and Signa returns the window, its current status, any routine extensions, and the office rule behind it. Pair it with List Opposition Rules to inspect the office rules, trigger events, time zones, extensions, and citations behind each computation.This endpoint computes the forward-looking opposition window (dates and timing) from a publication date. It does not return opposition cases that have actually been filed. For filed opposition, cancellation, and appeal cases, use Search Proceedings.
Request Body
object[]
required
Publications to compute, max 1,000 items. Results are returned in the same order, with one
opposition_window per input item.Show Item object
Show Item object
string
required
Uppercase ST.3 trademark office code, e.g.
US, EM, or CA. Legacy lowercase codes (e.g. uspto, and eu for EUIPO) are accepted as permanent aliases.string
Filing route. One of
national, regional, or madrid. Defaults to national. The response reports the office rule discriminator as domestic or madrid_designation.string
Publication calendar date as
YYYY-MM-DD. Omit when the publication date is unknown; the response status will be unknown.string
Application filing calendar date as
YYYY-MM-DD. Read only by rules whose era pivot is keyed on the filing date rather than the publication date (Türkiye: an application filed before 2017-01-10 keeps the former 3-month window even when published later). Omit it and such a rule falls back to the publication date, which yields the shorter modern window. It is also read by a rule that excludes marks already filed when its opposition procedure commenced: Switzerland (national filings made before 1993-04-01) and Poland (national filings made before 2016-04-15) have no window for such a mark, whatever its publication date. Omit it and only the publication date is tested against the commencement. Every other rule ignores it.string
Which publication
publication_date is. One of office_journal, wipo_gazette, office_register, or unknown.A Madrid designation has two publications that can fill one date field: WIPO’s Gazette of International Marks, and the designated office’s own journal republication. They are routinely months apart, and offices run the opposition clock from different ones. FR, CH and DE run from the WIPO Gazette; AU, CA, EU, FI, NO, PL, SG and US run from their own journal. Feed a rule the other office’s date and you get a plausible date that is not the statutory bar.Supply this field and a rule that needs the other publication returns supported: true with status: "unknown", null window fields and reason: "publication_date_kind_mismatch", keeping rule_id, office_code, trigger_event, rule_version and source so you can see exactly which rule declined and why.unknown is not the same as omitting the field. It is a positive statement that you looked and the source did not establish which publication the date is, and on an adjudicated Madrid rule that unestablished provenance is refused for the same reason a mismatch is: same status: "unknown", same reason. There is no third outcome where an unknown provenance yields dates.Omitting the field is the only value-free option: the window is computed exactly as before, because no claim about provenance was made. Ignored on the national and regional filing routes, where there is no second candidate publication. See Opposition windows for the per-office table.string
Office-local calendar date to compute relative to, as
YYYY-MM-DD. Defaults to today.Response
A standard list response withdata: OppositionWindowComputation[]. Pagination is not used; data[i] corresponds to items[i].
string
Always
list.object[]
Show OppositionWindowComputation
Show OppositionWindowComputation
string
Always
opposition_window.boolean
true when an opposition rule was found for the office and filing route and applied. That is a statement about rule resolution, not about dates: omit publication_date and the item is still supported: true, with status: "unknown" and null window fields, because there is no trigger to compute from. false when Signa has no opposition rule for the office and route, when a rule exists but does not reach the publication date, or when the window could not be computed.string | null
Stable, permanent slug of the opposition rule used for this item, e.g.
eu_opposition. Use it as the join key to List Opposition Rules. Treat it as opaque: do not parse it or infer an office from it. It is deliberately not a prefixed entity ID (tm_, own_) because it names a statutory rule, not a stored entity. Populated whenever a rule was resolved for the office and route, including on supported: false items whose window could not be computed. null only when no rule exists for that office and route.string | null
Earliest date from which the rule’s modelled content is authoritative, as
YYYY-MM-DD. null when the source corpus does not establish one, which is the common case. Reported alongside rule_id, including on unsupported items: a publication date earlier than effective_from is outside the rule’s reach and returns reason: "before_commencement" with effective_from naming the boundary. Era pivots inside a rule (content that varies by publication date) are modelled internally and are not reported here.string | null
Jurisdiction for the office rule, e.g.
EU.string | null
Uppercase ST.3 office code used for the computation, e.g.
EM.string | null
Rule discriminator:
domestic or madrid_designation.string | null
Publication date used as the trigger.
string | null
First day the opposition window is open.
string | null
Last day to oppose, after office-calendar rollover. Read it together with
close_adjustment, which says whether a calendar rollover was actually applied.string | null
Whether
window_closes was checked against the office’s holiday calendar, and whether that check moved it. moved: the close fell on a day the office is shut and was rolled forward to the next business day. unchanged: the calendar was consulted and the arithmetic close was already a business day. not_checked: no calendar check stands behind this date. The close falls outside the calendar’s pinned coverage years (a per-notice calendar such as China’s or India’s advertises only the years whose annual notice is published), the rule ships without a calendar, or there is no publication date. A rollover can only move a close later, never earlier, so a not_checked date is correct-or-early: safe to act on, but not a decided answer. If you derive open/closed from the dates yourself, treat not_checked as indeterminate for the 20 days after window_closes. null only when no window was computed.string | null
One of
not_started, open, closed, or unknown. Computed against as_of_date; where close_adjustment is not_checked it is derived from the served, unverified close, so a window Signa reports closed within 20 days of that date may in fact still be open.integer | null
Days until
window_opens when the status is not_started.integer | null
Days remaining in the window when known.
string | null
IANA office time zone for display.
string | null
application_publication, registration_publication, or international_designation_publication.string | null
Date Signa last verified this rule against its sources, as
YYYY-MM-DD. Same value as last_verified on List Opposition Rules; it moves on a verification pass even when nothing about the rule changed. null on unsupported items.string | null
Why the window is unsupported or degraded, as a snake_case code;
null on every normal window. With supported: false: unsupported_office (no rule exists for this office and route; rule_id is null), before_commencement (a rule exists, but the publication date, or the filing_date you sent, falls before the date it applies from; rule_id names the rule and effective_from the boundary) or window_not_computable (a rule resolved but no window could be produced). With supported: true, a degraded window: publication_date_kind_mismatch, the publication_date_kind you sent is not the publication the matched Madrid rule runs from, including the explicit unknown. (Before the provenance release the unsupported codes were in a separate unsupported_reason field.) A missing publication_date stays reason-less: status: "unknown" with a null publication_date already says that. Treat the vocabulary as open: branch on the codes you know and fall through on the rest.object | null
boolean
Always
false.object
Always
{ "cursor": null }.string
Unique request identifier for support and debugging.
rule_id and effective_from are the exception. They report the rule Signa resolved for the office and filing route, and that resolution either succeeded or it did not, independent of whether a window came out of it. So an unsupported item still carries them whenever a rule was found, which keeps it joinable to List Opposition Rules. They are null only when no rule exists for that office and route.
Three cases end in supported: false:
-
No rule for the office or route.
reason: "unsupported_office",rule_id: null,effective_from: null. -
A rule exists but does not reach the publication or filing date.
reason: "before_commencement",rule_idnames the rule andeffective_fromnames the date it starts from. A Vietnamese publication from 2022 reportsrule_id: "vn_opposition"witheffective_from: "2023-01-01", because opposition under art. 112a only applies from that date. Recompute with a publication date on or aftereffective_fromand the same rule returns a window. -
The window could not be computed. This is now a NARROW case: a close date past the office calendar’s covered years that falls in the 20 days before the date you are computing as of, where a rollover could still be holding the window open and guessing either way would be wrong.
reason: "window_not_computable", affecting that item only.rule_idandeffective_fromare populated; every other window field, includingrule_version, isnull. Every OTHER out-of-coverage close, meaning one in the future, one falling exactly on the as-of date, and one more than 20 days before it, is published withclose_adjustment: "not_checked"rather than declined, after the deterministic roll over weekends and the fixed holidays the calendar computes from the law. Silence on a rights-losing deadline is worse than an early date, and an unverified close is never later than the true one. This is what a calendar built from annual office notices (China, India) does for every window closing in a year whose notice is not yet published.
supported: true, status: "unknown", and window_opens, window_closes, days_until_open and days_remaining all null. rule_id, effective_from, rule_version, source, trigger_event and the office fields are populated as usual in each. reason is what tells them apart.
- No publication date. Omit
publication_dateand the rule still resolves and applies; there is simply no trigger to compute from.reasonstaysnull: the nullpublication_datealready says it. - The wrong publication. Send a
publication_date_kindthat is not the one the matched Madrid rule runs from (or the explicitunknown), and the rule declines rather than dating the window from a publication it does not recognise.reasonispublication_date_kind_mismatch.
supported: true as “there are dates”: check status or the window fields.
{
"object": "list",
"data": [
{
"object": "opposition_window",
"supported": true,
"rule_id": "eu_opposition",
"effective_from": null,
"jurisdiction_code": "EU",
"office_code": "EM",
"filing_route": "domestic",
"publication_date": "2026-05-01",
"window_opens": "2026-05-02",
"window_closes": "2026-08-03",
"status": "open",
"days_until_open": null,
"days_remaining": 29,
"office_time_zone": "Europe/Madrid",
"trigger_event": "application_publication",
"rule_version": "2026-05-06",
"common_extension": null,
"source": {
"citation": "Article 46(1) EUTMR (Regulation (EU) 2017/1001)",
"url": "https://eur-lex.europa.eu/eli/reg/2017/1001/oj/eng"
},
"reason": null
}
],
"has_more": false,
"pagination": { "cursor": null },
"request_id": "req_bT9kM3nP"
}
Code Examples
curl "https://api.signa.so/v1/oppositions/compute" \
-H "Authorization: Bearer sig_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"office": "EM",
"publication_date": "2026-05-01"
}
],
"as_of_date": "2026-07-05"
}'
import { Signa } from "@signa-so/sdk";
const signa = new Signa({ api_key: process.env.SIGNA_API_KEY });
const windows = await signa.oppositions.compute({
items: [{ office: "EM", publication_date: '2026-05-01' }]
});
for (const window of windows.data) {
console.log(window.office_code, window.status, window.window_closes);
}
Related Endpoints
- List Opposition Rules, inspect the office rule corpus and statutory citations
- Search Proceedings, filed opposition, cancellation, and appeal cases
- Trademark Proceedings, proceedings scoped to one mark