Relatórios e oposições
Este guia cobre as leituras focadas em registrabilidade e oposições do contrato da Tarno: o
relatório de registrabilidade de uma marca, a linha do tempo de oposições de uma marca, e a
listagem de oposições abertas em uma classe de Nice ranqueadas por similaridade. Os três endpoints
usam o scope brands:read e têm equivalente MCP.
O esquema completo e sempre atualizado por campo vive na referência OpenAPI interativa em
/docs(Scalar). Este guia explica o contrato de relatórios e oposições e aponta para essa referência — não reformula o esquema. Quando um campo não está documentado aqui,/docsé autoritativo.
Ao longo do guia a URL base é escrita como $TARNO_BASE_URL e a chave como $TARNO_API_KEY, e cada
chamada leva o cabeçalho X-API-Key — igual ao
guia de primeiros passos e ao guia de autenticação.
O que este guia cobre
Três leituras, todas sob /v1 e todas com scope brands:read:
- Relatório de registrabilidade (
GET /v1/brands/:nroSolicitud/report) — o detalhe de uma marca como um superset, com campos extras úteis para avaliar registrabilidade. - Linha do tempo de oposições (
GET /v1/brands/:nroSolicitud/opposition) — a sequência cronológica dos eventos de oposição de uma marca, mais a janela de oposição derivada (quando aplicável). - Oposições abertas (
GET /v1/oppositions/open) — marcas com janela de oposição aberta em uma classe de Nice, ranqueadas por similaridade a um portfólio.
Estas rotas são de somente leitura e honestas por design: nunca fabricam datas ou prazos que o corpus não pode sustentar. Quando um dado não existe, o campo é omitido (ver os avisos de honestidade em cada seção).
Relatório de registrabilidade
GET /v1/brands/:nroSolicitud/report — scope brands:read. MCP: get_brand_report.
Retorna um BrandReport, que é um superset do detalhe da marca: inclui tudo que
GET /v1/brands/:nroSolicitud retorna (classes, pessoas, anotações do Estado-Diário) mais os
campos extras úteis para avaliar registrabilidade. Um número de solicitação desconhecido retorna
404 not_found.
Campos extras sobre o detalhe base:
| Campo | Tipo | Notas |
|---|---|---|
nroRegistro |
string | omitido | Número de registro. É omitido quando a marca não tem registro — nunca chega como null. |
estado |
string | Estado atual da marca. |
tipoSigno |
string | Tipo de sinal (nominativa, mista, figurativa, …). |
fechaPresentacion |
YYYY-MM-DD |
Data de apresentação. |
fechaVencimiento |
YYYY-MM-DD | null |
Data de vencimento do registro. |
fechaPublicacion |
YYYY-MM-DD | null |
Data de publicação no Estado-Diário. |
fechaRegistro |
YYYY-MM-DD | null |
Data de registro. |
tipoNombre |
string | Nome do tipo de marca. |
subtipoNombre |
string | null | Nome do subtipo, se aplicável. |
traduccion |
string | null | Tradução declarada do sinal, se aplicável. |
descripcionEtiqueta |
string | null | Descrição da etiqueta. |
protectionDescription |
string | null | Descrição do escopo de proteção. |
imagenUrl |
string | null | URL da imagem do sinal (marcas mistas/figurativas). |
renovadaDe |
string | null | Número de solicitação da marca da qual esta é renovação. |
renovadaPor |
string | null | Número de solicitação da marca que renova esta. |
titulares[] |
array\<Persona> | Titulares, separados por papel. Cada pessoa traz a sua classificação, comuna e region (ver abaixo). |
representantes[] |
array\<Persona> | Representantes, separados por papel. |
completeness |
objeto | Bloco de honestidade. SEMPRE presente (ver aviso). |
coverageThrough |
objeto | omitido | Fronteira de cobertura. Omitido se não há instância enriquecida (ver aviso). |
estadoDerivado |
string | omitido | Estado que o corpus realmente pode justificar, do mesmo catálogo controlado que estado. Omitido quando não há nada a derivar. |
estadoDesactualizado |
boolean | SEMPRE presente. Verdadeiro quando o corpus já tem a prova da concessão que estado ainda não reflete. |
estadonão muda: continua sendo o valor EM BRUTO da INAPI.estadoDerivadoeestadoDesactualizadosão campos adicionados, nunca uma reescrita deestado. Existem porque a INAPI atribui o número e a data de registro antes de virar o estado: medido, ela vira no dia ~4-6 após a data de registro e só chega a 100 % no dia 9, enquanto a anotação de concessão do Estado-Diário entra no mesmo dia em que é declarada. Nessa janela a ficha dizia «em trâmite, sem registro» sobre um processo já titulado.estadoDerivadopublica o que a evidência do corpus prova eestadoDesactualizadoavisa que os dois não coincidem.
nroRegistrotambém é omitido quando a INAPI envia o seu sentinela de zero. A INAPI publica «sem número de registro» como um zero (0numérico no detalhe do Buscador, o texto"0"no CSV de open-data). Um zero não é um número de registro, portanto o relatório trata-o como ausência: omite o campo. Antes emitianroRegistro: "0", o que quebrava a própria promessa de omissão do contrato e podia aparecer junto deestadoDerivado: "registrada"na mesma resposta. Um consumidor que já verificava a presença do campo não precisa mudar nada: apenas deixa de receber o zero. O identificador do processo énroSolicitud, um campo distinto e sempre presente, que isto não altera.
titulares[] e representantes[] estão separados por papel. Uma pessoa cujo papel é 'ambas'
(titular e representante) aparece em ambos os arrays.
Dois blocos de honestidade.
completenessestá sempre presente e declara quão completo está o registro retornado.coverageThroughé a fronteira de cobertura dos dados e é omitido quando a marca não tem uma instância enriquecida — sua ausência significa "sem fronteira conhecida", nunca um valor inventado.
Titularidade atual, e histórico
personas[], titulares[] e representantes[] trazem apenas quem o é hoje. Uma marca que mudou
de mãos já não nomeia o dono anterior entre os atuais — antes nomeava, e eram dois titulares
igualmente vigentes com um deles falso.
Os anteriores vivem em personasHistoricas[], com mais dois campos:
| campo | o que é |
|---|---|
retiradoEn |
a data em que o INAPI diz que a titularidade se moveu, não quando a processámos |
retiradoPor |
a anotação que o prova: A07T transferência total, A07P parcial, MSO08 cessão, A01 mudança de nome |
Está sempre presente e quase sempre vazio. Não estar é o sinal de que a marca mudou de mãos.
Copropriedade não é histórico. Dois titulares simultâneos são legítimos e ambos ficam em
personas[]: só se retira alguém quando existe uma anotação que nomeia o adquirente.
Classes que cobrem a marca, e as que já não cobrem
Uma marca regista-se em classes, e elas não vivem nem morrem juntas. clases[] traz apenas as
que a cobrem hoje; as restantes passam para clasesHistoricas[], com o motivo que o INAPI dá
em estado.
Medido: 10.232 marcas têm classes em estados misturados, e 8.220 delas constam registrada —
a marca está viva enquanto parte das suas classes foi recusada ou não foi renovada. FARMACIAS
AHUMADA (pedido 1113943) consta registada com 34 classes: 7 cobrem-na e 27 não foram
renovadas. Antes listavam-se as 34 por igual.
| cobre hoje | já não cobre |
|---|---|
(C) Concedida · (X) En Trámite · (1) Para conceder · (2) Para rechazar |
(N) Rechazada · (A) Abandonada · (D) Desistida · (V) Vencida · (3) No renovada · (O) Cancelada voluntariamente · (U) Anulada · (I) Dividida · (T) Transferida · (P) Transferida Parcialmente |
(2) Para rechazar continua a cobrir. É uma decisão proposta, não firme: dá-la por caída seria
antecipar um desfecho que não ocorreu.
(T), (P) e (I) não morreram: MUDARAM. A classe foi transferida ou dividida e continua a
proteger alguém, mas já não sob esta marca — que é o que aqui se responde.
Sem estado ⇒ fica em clases[]. Não saber não prova que tenha caído. O estado viaja tal como
o INAPI o publica, para poderdes julgar por vós.
Ónus sobre a marca
gravamenes[] responde que ónus continuam ali, não se alguma vez houve algum. Isso já estava em
anotaciones[], mas para o ler era preciso saber que A05I inscreve um penhor e A05A o levanta.
Aqui cada inscrição já vem emparelhada com o levantamento que a encerrou.
| campo | o que é |
|---|---|
tipo |
prenda, precautoria, embargo, prohibicion ou otros |
estado |
activo (inscrito e por levantar) ou alzado |
inscritoEn / alzadoEn |
as duas datas do INAPI |
aFavorDe |
o credor, retirado apenas da inscrição |
seccionInscripcion / seccionAlzamiento |
os códigos que o provam |
alzamientoObservable |
se o INAPI publica algum código capaz de levantar este tipo de ónus |
Os activos vêm primeiro, e dentro deles os mais recentes.
aFavorDe é o credor, nunca o titular. A expressão "A Favor de:" muda de sentido entre os dois
lados: na inscrição nomeia quem recebe a garantia e no levantamento quem recupera a marca livre. Por
isso só se lê do lado da inscrição. Medido: nos penhores, o "A Favor de:" do levantamento é um titular
da própria marca em 81,6 % dos casos, e o da inscrição apenas em 0,7 %.
⚠ alzamientoObservable: false significa que esse activo não é afirmável. Para prohibición e
otros, o INAPI não publica qualquer código que os levante: constam por levantar porque não temos
como saber, não porque continuem vivos. Não os useis para afirmar que uma marca está onerada hoje.
Um levantamento pode vir sem a sua inscrição (inscritoEn: null): o ónus foi levantado e a sua
inscrição é anterior ao que o histórico alcança. Conta como alzado, nunca como activo.
Está sempre presente e quase sempre vazio: em todo o corpus, 2.790 marcas têm algum ónus registado e 2.048 algum activo.
Uma licença não é um ónus. A03I/A03A formam um par idêntico na forma e ficam de fora de
propósito: é um direito concedido sobre a marca, não um gravame contra ela.
Âmbito territorial das pessoas
Cada pessoa de personas[], titulares[] e representantes[] traz mais dois campos:
| Campo | Tipo | Notas |
|---|---|---|
comuna |
string | null | Código CUT da comuna, sem zero à esquerda: '13114' (Las Condes), '5109' (Viña del Mar) |
region |
string | null | Código CUT da região, sem zero à esquerda: '13', '5' |
comunaNombre |
string | null | O nome dessa comuna ('Las Condes'), resolvido do catálogo |
regionNombre |
string | null | O nome dessa região ('Región Metropolitana') |
Desde 2026-08-25 os códigos viajam com o seu nome. Continuam não sendo o endereço — esse
não é exposto. Os nomes são os que a INAPI publica, com as suas próprias grafias (Valparaiso sem
acento); null quando o código não está no catálogo, nunca inventados.
Cobertura: 100 % das regiões e 99,82 % das comunas.
- A província sai do próprio código. Complete a comuna com zeros até 5 dígitos e fique com os
3 primeiros:
05109→051(província de Valparaíso),03301→033(Huasco). Nenhuma tabela é necessária para filtrar por província. 99/99999/999é a sentinela de desconhecido ou estrangeiro, não uma região real.- Viajam sempre juntas: ou as duas nulas, ou as duas com valor.
- Cobertura medida (2026-08-24): 70,0 % dos titulares e 96,4 % dos representantes.
Que tipo de parte é cada pessoa
Desde 2026-09-08 cada pessoa de personas[], titulares[] e representantes[] traz mais cinco
campos, idênticos aos que devolvem o detalhe da marca e a busca de pessoas. São
aditivos: nenhum campo anterior foi removido, renomeado ou re-tipado, e nenhum scope mudou —são
lidos sob o mesmo brands:read—, portanto um consumidor existente continua a parsear o relatório sem
tocar em nada.
| Campo | Tipo | Notas |
|---|---|---|
tipo |
natural | juridica | desconocido |
Que tipo de parte é. desconocido é um valor, não um buraco: um terço do corpus não tem RUT nem sufixo societário, e o contrato di-lo em vez de o deduzir. |
tipoOrigen |
rut | sufijo | manual | omitido |
Que sinal produziu tipo. Sem sinal a chave desaparece — nunca chega como null. |
observacion |
string | null | A cláusula de jurisdição que a INAPI escreve colada ao nome («…, SOCIEDAD ORGANIZADA BAJO LAS LEYES DE…»), retirada de nombre. É prosa da INAPI, com 681 grafias distintas da mesma cláusula: não é vocabulário controlado. |
nombrePublicado |
string | null | A grafia tal como a INAPI a publicou. |
revision |
boolean | O corpus marcou a linha para que uma pessoa a veja. O motivo não é publicado. |
- Um filtro
tipo = 'natural'não serve para «tudo o que não é uma empresa». Traria umas 130.000 partes que ninguém classificou. Se precisa de certeza, exijatipoOrigen: 'rut'. nombreé o campo com que se BUSCA;nombrePublicadoé o que se MOSTRA no relatório, ou o que se CONFRONTA com o documento da INAPI.nombreé a forma canónica (MAIÚSCULAS com acentos); comparar contranombrePublicadoreintroduz o ruído de maiúsculas e acentos que a canónica remove.- O corpus de pessoas foi reescrito a 2026-09-08: 396.444 nomes passaram à forma canónica (~240.000 mudaram de grafia) e foram fundidos 179 grupos de duplicados sem que nenhuma marca mudasse de dono. Comparar um relatório em cache de antes com um de hoje mostrará diferenças de maiúsculas e acentos que não são mudanças de dado.
Identidade de cada anotação
Cada elemento de anotaciones[] traz um id (string opaca). É estável: a escrita do corpus é
somente-anexação, então uma anotação conserva seu id enquanto existir.
!!! warning "O que o id NÃO resolve"
A chave que decide se uma anotação chega a ser escrita é
(nro_solicitud, tipo, fecha, md5(observacion)) e não inclui a seção. Para um código fora
de M1..M14 sem observação, isso permite no máximo uma anotação por marca e por dia: uma
segunda seção do mesmo dia nunca é escrita e portanto não tem id.
Medido: 5,37 % do que a fonte oferece. Ver o esquema.
!!! danger "historyIncomplete: false não garante que não falte nenhuma anotação"
É um negativo estreito: significa "não há diff de Sheets pendente de confirmação pelo
Buscador". Não é levantado por um movimento que não muda estado, nem pelo atraso do
Buscador em relação ao Estado Diario, nem pela colisão de chave acima. coverageTier
(denso/residual) é a era do corpus, não uma afirmação sobre a marca. Para delimitar o
que se pode afirmar, use dataAsOf, coverageThrough e enrichmentPending.
curl
curl -sS "$TARNO_BASE_URL/v1/brands/123456/report" \
-H "X-API-Key: $TARNO_API_KEY"
TypeScript (fetch)
const res = await fetch(`${process.env.TARNO_BASE_URL}/v1/brands/123456/report`, {
headers: { "X-API-Key": process.env.TARNO_API_KEY! },
});
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${res.status} ${error.code}: ${error.message} (requestId=${error.requestId})`);
}
const report = await res.json(); // BrandReport — superset do detalhe da marca
console.log(report.estado, report.completeness);
// coverageThrough pode vir omitido se não há instância enriquecida.
Linha do tempo de oposições de uma marca
GET /v1/brands/:nroSolicitud/opposition — scope brands:read. MCP: get_brand_opposition.
Retorna uma OppositionTimeline: uma lista plana e cronológica (fecha ascendente) das
anotações do Estado-Diário cujo tipo de seção é 'oposicion', mais a janela de oposição derivada
quando a marca tem data de publicação. Um número de solicitação desconhecido retorna 404 not_found.
| Campo | Tipo | Notas |
|---|---|---|
nroSolicitud |
string | Número de solicitação da marca. |
estado |
string | omitido | Estado bruto (raw). Omitido se for NULL. |
eventos[] |
array\<Evento> | Anotações de seção 'oposicion', em ordem cronológica (fecha ASC). |
ventanaOposicion |
objeto | omitido | A única data derivada. Presente só se há data de publicação (ver aviso). |
freshnessNote |
string | SEMPRE presente. Declara honestamente o gap de frescor do Estado-Diário. |
dataAsOf |
string | omitido | Marca temporal dos dados, se aplicável. |
coverageThrough |
objeto | omitido | Fronteira de cobertura. |
Cada elemento de eventos[]:
| Subcampo | Tipo | Notas |
|---|---|---|
fecha |
YYYY-MM-DD |
Data da anotação. |
seccion |
string | Código da seção do Estado-Diário. |
seccionNombre |
string | Nome legível da seção. |
seccionTipo |
'oposicion' |
Tipo de seção — sempre 'oposicion' nesta linha do tempo. |
observacion |
string | Texto da anotação. |
E ventanaOposicion (quando presente):
| Subcampo | Tipo | Notas |
|---|---|---|
fechaPublicacion |
YYYY-MM-DD |
Data de publicação a partir da qual a janela é contada. |
fechaLimite |
YYYY-MM-DD |
Data limite = publicação + 30 dias úteis. |
diasHabilesRestantes |
integer | Dias úteis restantes até a data limite. |
vencida |
bool | true se a janela já venceu. |
Honestidade — prazos. Os
eventosnão levam um prazo por-evento: correm a partir de datas de notificação que o corpus não captura com precisão, então nenhuma data limite por evento é calculada. A única data derivada segura éventanaOposicion= data de publicação + 30 dias úteis, e está presente só se a marca tem data de publicação — nunca é fabricada. Além disso,freshnessNoteestá sempre presente e declara o gap de frescor do Estado-Diário, para que você saiba o quanto a linha do tempo pode estar atrasada em relação à realidade.
curl
curl -sS "$TARNO_BASE_URL/v1/brands/123456/opposition" \
-H "X-API-Key: $TARNO_API_KEY"
TypeScript (fetch)
const res = await fetch(`${process.env.TARNO_BASE_URL}/v1/brands/123456/opposition`, {
headers: { "X-API-Key": process.env.TARNO_API_KEY! },
});
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${res.status} ${error.code}: ${error.message} (requestId=${error.requestId})`);
}
const timeline = await res.json(); // OppositionTimeline
console.log(timeline.freshnessNote); // sempre presente
for (const ev of timeline.eventos) {
console.log(ev.fecha, ev.seccionNombre, ev.observacion);
}
// ventanaOposicion só existe se a marca tem fechaPublicacion.
if (timeline.ventanaOposicion) {
console.log("Vence:", timeline.ventanaOposicion.fechaLimite, "vencida:", timeline.ventanaOposicion.vencida);
}
Oposições abertas em uma classe
GET /v1/oppositions/open?clase=X&(q=…|nroSolicitud=…) — scope brands:read. MCP: get_open_oppositions.
Lista marcas com janela de oposição aberta em uma classe de Nice, ranqueadas por similaridade a
um portfólio. O caso de uso: vigiar ameaças de registro na sua classe — marcas recém-publicadas
cuja janela de oposição segue aberta e que se parecem com algo que você quer proteger. O ranking usa o
mesmo modelo de similaridade que search / find-similar (fonético + léxico + semântico).
Query:
| Param | Tipo | Notas |
|---|---|---|
clase |
integer | OBRIGATÓRIO. Classe de Nice, inteiro 1–45. |
q |
string | Texto livre. Exatamente um de q ou nroSolicitud (ver aviso). |
nroSolicitud |
string | Uma marca existente como referência. Exatamente um de q ou nroSolicitud (ver aviso). |
O xor
q/nroSolicitud. Você deve enviarclasee exatamente um deq(texto livre) ounroSolicitud(uma marca existente que atua como referência de similaridade). Enviar ambos ou nenhum retorna400 validation_error.
Resposta: BrandSummaryPage = { items: BrandSummary[], nextCursor: string | null, dataAsOf? }.
A paginação é por keyset: para a próxima página, reenvie nextCursor sem alterações como cursor;
nextCursor: null = fim — mesma semântica de keyset que primeiros passos.
Cada elemento de items é a mesma forma BrandSummary documentada em
primeiros passos e em /docs.
curl
curl -sS "$TARNO_BASE_URL/v1/oppositions/open?clase=43&q=cafe%20example" \
-H "X-API-Key: $TARNO_API_KEY"
TypeScript (fetch) — paginando
const baseUrl = process.env.TARNO_BASE_URL!;
const apiKey = process.env.TARNO_API_KEY!;
let cursor: string | null = null;
do {
const url = new URL(`${baseUrl}/v1/oppositions/open`);
url.searchParams.set("clase", "43");
url.searchParams.set("q", "cafe example"); // exatamente um de q | nroSolicitud
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers: { "X-API-Key": apiKey } });
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${res.status} ${error.code}: ${error.message} (requestId=${error.requestId})`);
}
const page = await res.json(); // BrandSummaryPage
for (const brand of page.items) console.log(brand.nroSolicitud, brand.denominacion, brand.score);
cursor = page.nextCursor; // null = fim
} while (cursor);
Scopes
Os três endpoints requerem o scope brands:read.
Um array de scopes vazio não tem restrição (mesma regra que no resto do contrato): uma chave sem
scopes recebe todos os scopes de leitura, incluindo brands:read. Uma chave com scopes não vazios
recebe só os listados; chamar uma destas rotas sem brands:read é rejeitado com 403 forbidden
nomeando o scope faltante.
Consulte o guia de autenticação para o ciclo de vida da chave e a semântica de scopes.
MCP
REST é equivalente a MCP: mesma chave, mesmos scopes, mesmos esquemas Zod. O servidor MCP expõe estas ferramentas:
| Ferramenta MCP | Equivalente REST |
|---|---|
get_brand_report |
GET /v1/brands/:nroSolicitud/report |
get_brand_opposition |
GET /v1/brands/:nroSolicitud/opposition |
get_open_oppositions |
GET /v1/oppositions/open |
Consulte o guia de MCP para o endpoint, o transporte e a autenticação.
Erros
Todos os erros usam o envelope uniforme { error: { code, message, requestId } } — descrito em
primeiros passos.
| HTTP | code |
Quando |
|---|---|---|
| 401 | unauthorized |
Chave ausente, malformada, desconhecida ou revogada. |
| 403 | forbidden |
Falta o scope brands:read requerido. |
| 404 | not_found |
Marca inexistente (em report e opposition). |
| 400 | validation_error |
Em oppositions/open: falta clase, ou o xor q / nroSolicitud não é cumprido (ambos ou nenhum). |
Ver também
- Autenticação e scopes —
X-API-Key,brands:read, ciclo de vida da chave. - Conecte seu agente de IA (MCP) — as mesmas ferramentas de relatórios e oposições via MCP.
- Primeiros passos — URL base, paginação por keyset, envelope de erro,
BrandSummary. - A referência OpenAPI ao vivo em
/docs— o esquema autoritativo por campo.