Skip to main content
GET
/
v1
/
trademarks
/
{id}
/
documents
Trademark Documents
curl --request GET \
  --url https://api.signa.so/v1/trademarks/{id}/documents \
  --header 'Authorization: Bearer <token>'
import requests

url = "https://api.signa.so/v1/trademarks/{id}/documents"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.text)
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

fetch('https://api.signa.so/v1/trademarks/{id}/documents', 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/trademarks/{id}/documents",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <token>"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.signa.so/v1/trademarks/{id}/documents"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.get("https://api.signa.so/v1/trademarks/{id}/documents")
  .header("Authorization", "Bearer <token>")
  .asString();
require 'uri'
require 'net/http'

url = URI("https://api.signa.so/v1/trademarks/{id}/documents")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
{
  "object": "<string>",
  "source_sync": {
    "status": "<string>",
    "last_synced_at": "<string>"
  },
  "data": [
    {
      "id": "<string>",
      "object": "<string>",
      "document_kind": "<string>",
      "official_date": "<string>",
      "description": "<string>",
      "mime_type": "<string>",
      "page_count": 123,
      "url": "<string>"
    }
  ],
  "has_more": true,
  "pagination": {}
}

When to use this

If you run docketing or an IPMS product, you need office documents the day they issue: the non-final office action a client must answer, the registration certificate, the filed forms in the prosecution history. This endpoint returns that document list for a mark, and each row carries a url that streams the actual file. USPTO documents are fetched from TSDR on demand the first time you ask, so you get fresh metadata without running your own TSDR poller or fighting its throttles. The list is document-type media only. Logos, drawings, and specimens live on the media[] array of the trademark record, not here.

Path Parameters

id
string
required
Trademark ID (tm_...).

Query Parameters

document_kind
string
Filter by one or more document kinds, comma-separated. Values: office_action, certificate, correspondence, filed_form, other. Example: document_kind=office_action,certificate.
official_date_gte
string
Return documents whose official date is on or after this date (YYYY-MM-DD).
official_date_lt
string
Return documents whose official date is strictly before this date (YYYY-MM-DD). Must be after official_date_gte when both are supplied.
limit
integer
default:"20"
Page size, 1 to 100.
cursor
string
Pagination cursor from a previous response’s pagination.cursor.

Response

A standard list envelope with one extra field, source_sync, describing the freshness of this mark’s document metadata.
object
string
Always list.
source_sync
object
Freshness of the document metadata for this mark.
data
array
Array of trademark document objects, newest official date first.
has_more
boolean
Whether more pages exist after this one.
pagination
object
Contains cursor (the token for the next page, or null).

First call: metadata fetch in flight

The first time you request documents for a USPTO mark that has never been synced, Signa fetches the metadata inline. If another request already holds the fetch lock, or the upstream fetch is still settling, you get status: pending with an empty data array. Request again in a moment.
First call (pending)
{
  "object": "list",
  "source_sync": { "status": "pending", "last_synced_at": null },
  "data": [],
  "has_more": false,
  "pagination": { "cursor": null },
  "request_id": "req_01kx22n3rpj1e6cmnpfbf482k6"
}

Second call: synced with a document row

Once the fetch completes, the list is synced and each row carries its media-proxy url.
Second call (synced)
{
  "object": "list",
  "source_sync": { "status": "synced", "last_synced_at": "2026-07-09T04:00:00Z" },
  "data": [
    {
      "id": "med_019d2141-6ce9-771b-872e-bc8b20e49fcf",
      "object": "trademark_document",
      "document_kind": "office_action",
      "official_date": "2025-11-04",
      "description": "Nonfinal Office Action",
      "mime_type": "application/pdf",
      "page_count": 12,
      "url": "https://api.signa.so/v1/trademarks/tm_8kLm2nPq/media/med_019d2141-6ce9-771b-872e-bc8b20e49fcf"
    }
  ],
  "has_more": false,
  "pagination": { "cursor": null },
  "request_id": "req_01kx22p7c8a0d3fjm2q9x7t4bz"
}
For a non-USPTO mark you always get 200 with an empty list and source_sync.status of unsupported. This endpoint never returns an error status for an office that lacks document support.

Code Examples

# First call kicks off the lazy fetch
curl "https://api.signa.so/v1/trademarks/tm_8kLm2nPq/documents" \
  -H "Authorization: Bearer $SIGNA_API_KEY"

# Filter to office actions issued in 2025
curl "https://api.signa.so/v1/trademarks/tm_8kLm2nPq/documents?document_kind=office_action&official_date_gte=2025-01-01&official_date_lt=2026-01-01" \
  -H "Authorization: Bearer $SIGNA_API_KEY"
import { Signa } from "@signa-so/sdk";

const signa = new Signa({ api_key: process.env.SIGNA_API_KEY });

const docs = await signa.trademarks.documents("tm_8kLm2nPq", {
  document_kind: "office_action",
  official_date_gte: "2025-01-01",
  official_date_lt: "2026-01-01",
});

if (docs.source_sync?.status === "pending") {
  // metadata still settling; request again shortly
}

for (const doc of docs.data) {
  console.log(doc.document_kind, doc.official_date, doc.url);
}

Downloading document files

Each document row’s url points at the media proxy, GET /v1/trademarks/{id}/media/{mediaId}. Following it streams the file bytes:
  • The response is the raw file with its real Content-Type (application/pdf for office actions and certificates).
  • X-Content-Type-Options: nosniff is set. The proxy never relabels a file: an image row stays an image content type.
  • The endpoint is unauthenticated so it works directly in <a href> and <embed>, but it is IP rate-limited, so treat it as a per-download link, not a bulk hose.
For a USPTO document, the first download is a cold fetch: the proxy pulls the file from TSDR under a shared download budget and persists it. When that budget is momentarily exhausted, the proxy returns 502 upstream_error with a Retry-After: 60 header. Honor it and retry after the stated delay. Once persisted, subsequent downloads serve the stored bytes and do not touch TSDR.
cURL
# Stream an office action PDF to disk
curl -L "https://api.signa.so/v1/trademarks/tm_8kLm2nPq/media/med_019d2141-6ce9-771b-872e-bc8b20e49fcf" \
  -o office-action.pdf

Errors

StatusTypeDescription
400validation_errorInvalid trademark ID, bad document_kind, or official_date_gte not before official_date_lt
401unauthorizedMissing or invalid API key
403forbiddenAPI key lacks the trademarks:read scope
404not_foundTrademark does not exist
The media proxy returns 502 upstream_error (with Retry-After) when the TSDR download budget is exhausted, and 404 not_found when the media record or upstream file does not exist.