# SPECTRA developer API — 1.1.0-rc.3

Production contract 1.1.0-rc.3. This API returns stored data only; access does not activate collection, publishing or paid analysis.

Production base: `https://api.spectra-ai.app/api/external/v1`. The 13 GET routes return tenant-scoped stored data; examples are synthetic.

Documentation: https://developers.spectra-ai.app/ · [Download OpenAPI](openapi.json).

## Authentication and quick start

1. Approve the integration tenant, required read scopes, verified recipient and secure delivery channel.
2. After approval, store the issued credential in the portal server's secret store. Do not embed it in browser bundles, URLs, telemetry or logs.
3. Send `Authorization: Bearer <machine credential>` from the server. Start with `GET /status`.
4. Follow the route examples below and complete approved-data, missing-scope and cross-tenant rejection acceptance before portal release.

The examples assume `SPECTRA_API_TOKEN` is populated by a server secret manager. Never paste secrets into this documentation. Request examples refuse redirects to avoid forwarding credentials to a different destination. Do not enable shell tracing or verbose HTTP logging around credentials.

## Scopes and tenant enforcement

| Scope | Approved read family |
| --- | --- |
| `status.read` | Operational status |
| `monitors.read` | Monitors and topics |
| `content.read` | Approved stored content |
| `analytics.read` | Countries and analytics |
| `reports.read` | Explicitly shared reports and downloads |

This integration is bound to **SPECTRA DDM Integration (SPECTRA-DDM)**. Grant only the five approved read scopes above. The server derives the tenant from the credential on every list, detail and download route. Tenant override parameters and headers are rejected; callers cannot select another organisation. Foreign or unavailable identifiers return 404. Internal administration, credential management, monitor editing, writes, collection, publishing and paid analysis remain inaccessible through this API.

## Credential lifetime and renewal

The agreed lifetime is **five calendar months from issuance**. Preserve the UTC time and clamp the day if the target month is shorter. Record exact issuance and expiry in UTC and WAT (UTC+01:00) at issuance.

A secure retrieval link expires separately and much sooner than the credential. Record both expiries; retrieving a link does not renew the credential. Each authorised recipient needs an independent retrieval path.

Begin renewal 30 days before expiry, escalate at 14 days and target cutover seven days before expiry. Renewal requires approval and secure delivery. Reminders are not automatically scheduled. The credential remains revocable before expiry. Issuing or renewing access does not activate collection or paid services.

## Data availability, freshness and visibility

These routes return stored evidence and aggregates of stored evidence. Reading does not start continuous collection. Display sample data, stored evidence and actively collected data as distinct states.

| Output | Interpretation and restrictions |
| --- | --- |
| Status and monitors | Inspect collection state, last successful collection, last run, stale flags and source coverage. Provider configuration is not proof of current ingestion. |
| Content | Active, non-archived, policy-approved normalized records only. Provider redistribution policy can restrict titles, identifiers, references and derived fields. Private provider payloads and full text are unavailable. X references are not a full-text licence; YouTube visibility/freshness restrictions apply. |
| Analytics | `generatedAtUtc` is aggregation time, not source collection freshness. Display sample size, evidence count and truncation. Unknown, withheld or unavailable sentiment is not neutral. |
| Reports | Only explicitly externally shared and unexpired reports. Results/downloads require completed, current, policy-valid evidence. Held, stale or failed metadata may have null results; download returns 409. Internal private reports are unavailable. |

A language, country filter or location mention does not establish author residence, nationality or ethnicity. Unknown geography remains unknown. A country's inclusion does not extend provider coverage, permissions or redistribution rights.

## Filters and pagination

Use only parameters documented for each route; unknown or repeated parameters are rejected. Defaults for paginated lists are page 1 and 25 items; maximum page size is 100 and page number 100000. Carry returned `asOfUtc` unchanged between pages. Privacy removals and provider-policy changes may still alter a later page.

Content and analytics date filters require ISO timestamps with explicit offsets. `from` is inclusive; `to` is exclusive and cannot exceed `asOfUtc`. The default interval is seven days, maximum 31 days. Search text is limited to 100 characters. The historic date windows in examples are synthetic and should be replaced with an approved current interval.

Content and analytics accept `country=FR,JP` (OR, deduplicated), `unknown`, or `global` alone (also the default when country is omitted). Unknown is role-specific and excludes policy-withheld evidence. `geographyRole=subject|origin|source`, default subject. Country totals overlap; do not sum them as unique evidence. The independent `language=fr` filter matches exact stored tags case-insensitively, not fr-CA. It performs no translation or identity inference.

Monitor list/detail reads expose the same saved `geographyTargets` (country/region/city IDs and labels, parentId, adminLevel, available), `languages`, `geographyBasis` and `timeZone`. Missing targets retain ID with available=false and null labels. Existing fields remain unchanged. Empty language preferences mean unrestricted; legacy configurations default to geographyBasis=legacy. Monitor creation/editing remains unavailable to machine credentials.

All assigned ISO countries are supported; city coverage is partial, and 13 territories lack polygons. Provider-side country-wide search is not assumed. Fixture acceptance does not establish live ingestion. English lexical analysis is unavailable for other languages, not a neutral result.

Analytics processing is bounded (default 5000 records, deployment-configurable); inspect truncation. Default rate limit 60/minute, credential/configuration dependent. Honor Retry-After with bounded retries and jitter.

## Errors and limits

| Status | Meaning and action |
| --- | --- |
| 400 | Invalid, duplicate or unknown parameter, or tenant override attempt. Correct the request. |
| 401 | Missing, invalid, expired or revoked machine credential. Use approved renewal/delivery procedures. |
| 403 | Missing scope or access-policy denial. Request a scope review. |
| 404 | Missing, foreign, unavailable or expired record. Do not infer cross-tenant existence. |
| 405 | Write operation unavailable. Use documented GET routes. |
| 409 | Report not currently downloadable. Inspect its state; do not treat it as a completed export. |
| 429 | Rate limited. Honor `Retry-After` and bound retry work. |
| 503 | Temporarily unavailable. Retry safely with a bound. |

Handled API errors follow the `Problem` schema with safe `code` and `requestId` fields. Retain request IDs for support without logging credentials or private payloads. Tolerate future safe codes within the same status class. Each route below lists its documented responses.


Unexpected server/proxy failures do not guarantee a Problem body. Check status and content type before parsing.

## Runtime prerequisites and portable preview

Use Bash with curl 7.76+; Node.js 20+ with fetch and AbortSignal.timeout; or Python 3.10+ (standard library only). Requests have a 15-second timeout; cURL additionally has a five-second connection timeout. Never follow redirects with credentials. For the portable preview, extract all files and open index.html, or run `python3 -m http.server 8080 --bind 127.0.0.1` in the extracted directory.

## Complete JSON and CSV downloads

Replace REPORT_ID with a report UUID returned by GET /reports. Supply the token from a server secret manager. JSON contains the report envelope and analytics; CSV is a documented aggregate projection with section,key,count rows, not a content export. CSV includes summary totals, unknown/withheld, sentiment, countries, sources, and evidence/positive/negative/neutral trends. Other trend states and references remain in JSON.

```sh
# Bash. Disable shell tracing. A failed request does not replace a prior download.
set -eu
REPORT_ID=00000000-0000-4000-8000-000000000001
for format in json csv; do
  tmp=$(mktemp)
  if curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - > "$tmp" <<EOF
url = "https://api.spectra-ai.app/api/external/v1/reports/$REPORT_ID/download?format=$format"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: */*"
EOF
  then mv "$tmp" "report.$format"; else rm -f "$tmp"; exit 1; fi
done
```


## Detailed endpoint reference

All examples are synthetic and credential-free. Response shapes are illustrative, not authenticated production receipts. Replace synthetic identifiers using tenant-scoped list responses. The only supported operation below is GET.

## GET /api/external/v1/status

Required scope: `status.read`.

Required machine scope: status.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

No query parameters accepted.

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/status"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/status", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/status", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalStatus"
}
```

Synthetic response shape (not a live result):

```json
{
  "schemaVersion": "spectra.external/v1",
  "observedAtUtc": "2026-11-05T08:00:00Z",
  "activeMonitors": 15,
  "maximumActiveMonitors": 15,
  "storedEvidenceCount": 8,
  "collectionState": "sample_partially_stale",
  "sources": [
    {
      "provider": "Rss",
      "collectionVerified": true,
      "storedEvidenceCount": 8,
      "successfulRuns": 3,
      "failedRuns": 1,
      "lastSuccessfulCollectionAtUtc": "2026-11-05T07:45:00Z",
      "state": "sample_partially_stale",
      "storedContentCoverage": true
    }
  ],
  "alerts": [
    "Synthetic example: some active monitors have no successful collection yet."
  ],
  "paidAnalysisTriggered": false
}
```

## GET /api/external/v1/monitors

Required scope: `monitors.read`.

Required machine scope: monitors.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `page` (query, optional): One-based page number; default 1, maximum 100000. `{"type": "integer", "minimum": 1, "maximum": 100000, "default": 1}`

- `pageSize` (query, optional): Items per page; default 25, maximum 100. `{"type": "integer", "minimum": 1, "maximum": 100, "default": 25}`

- `asOfUtc` (query, optional): Explicit ISO8601 timestamp with offset, default now. Carry unchanged between pages; stabilizes additions but not deletions/policy changes. `{"type": "string", "format": "date-time", "description": "Default now. Carry unchanged between list pages; stabilises additions, not privacy deletions or policy changes."}`

- `monitorId` (query, optional): Owned monitor UUID; restrict evidence to unsuppressed matches. Foreign identifiers return an empty list. `{"type": "string", "format": "uuid"}`

- `topicId` (query, optional): Alias for monitorId. If both are supplied they must match. `{"type": "string", "format": "uuid"}`

- `provider` (query, optional): One provider name, case-insensitive; selection does not enable collection. `{"type": "string", "enum": ["X", "Facebook", "Instagram", "YouTube", "Rss", "Manual"]}`

- `q` (query, optional): Literal substring, maximum 100 characters. Monitors/topics search name; content/analytics search safe external IDs and RSS titles only. `{"type": "string", "maxLength": 100}`

- `status` (query, optional): Stored state. Monitor values are case-insensitive; reports match stored state exactly. `{"type": "string", "description": "Monitor state, case-insensitive.", "enum": ["Draft", "Scheduled", "Active", "Paused", "Completed", "Failed", "Stopped", "Archived"]}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/monitors?page=1&pageSize=25"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/monitors?page=1&pageSize=25", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/monitors?page=1&pageSize=25", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalMonitorPage"
}
```

Synthetic response shape (not a live result):

```json
{
  "items": [
    {
      "id": "10000000-0000-4000-8000-000000000001",
      "name": "[SAMPLE] Public library services",
      "objective": "Synthetic engineering fixture only; not an operational owner topic.",
      "state": "Active",
      "sources": [
        "Rss"
      ],
      "geographicScope": "TargetLocationsOnly",
      "intendedCountries": [
        "GB"
      ],
      "collectionIntervalSeconds": 1800,
      "startsAtUtc": "2026-11-04T08:00:00Z",
      "endsAtUtc": "2026-11-06T08:00:00Z",
      "continuous": false,
      "lastSuccessfulCollectionAtUtc": "2026-11-05T07:45:00Z",
      "lastRun": {
        "id": "40000000-0000-4000-8000-000000000001",
        "provider": "Rss",
        "state": "Completed",
        "startedAtUtc": "2026-11-05T07:44:58Z",
        "completedAtUtc": "2026-11-05T07:45:00Z",
        "returnedOccurrences": 4,
        "newRecords": 3,
        "duplicates": 1,
        "errors": 0,
        "errorCode": null
      },
      "storedEvidenceCount": 8,
      "stale": false,
      "geographyTargets": [
        {
          "id": "00000000-0000-4000-8000-000000000002",
          "countryIsoCode": "GB",
          "name": "United Kingdom",
          "type": "Country",
          "adminLevel": 0,
          "parentId": null,
          "available": true
        }
      ],
      "languages": [
        "en"
      ],
      "geographyBasis": "subject",
      "timeZone": "Europe/London"
    }
  ],
  "page": 1,
  "pageSize": 25,
  "totalCount": 15,
  "hasMore": false,
  "asOfUtc": "2026-11-05T08:00:00Z",
  "requestId": "synthetic-request-id"
}
```

## GET /api/external/v1/monitors/{id}

Required scope: `monitors.read`.

Required machine scope: monitors.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `id` (path, required): Owned record UUID returned by the corresponding list; inaccessible, absent or expired records return 404. `{"type": "string", "format": "uuid"}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/monitors/00000000-0000-4000-8000-000000000001"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/monitors/00000000-0000-4000-8000-000000000001", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/monitors/00000000-0000-4000-8000-000000000001", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **404**: Absent, expired or inaccessible resource. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalMonitor"
}
```

Synthetic response shape (not a live result):

```json
{
  "id": "10000000-0000-4000-8000-000000000001",
  "name": "[SAMPLE] Public library services",
  "objective": "Synthetic engineering fixture only; not an operational owner topic.",
  "state": "Active",
  "sources": [
    "Rss"
  ],
  "geographicScope": "TargetLocationsOnly",
  "intendedCountries": [
    "GB"
  ],
  "collectionIntervalSeconds": 1800,
  "startsAtUtc": "2026-11-04T08:00:00Z",
  "endsAtUtc": "2026-11-06T08:00:00Z",
  "continuous": false,
  "lastSuccessfulCollectionAtUtc": "2026-11-05T07:45:00Z",
  "lastRun": {
    "id": "40000000-0000-4000-8000-000000000001",
    "provider": "Rss",
    "state": "Completed",
    "startedAtUtc": "2026-11-05T07:44:58Z",
    "completedAtUtc": "2026-11-05T07:45:00Z",
    "returnedOccurrences": 4,
    "newRecords": 3,
    "duplicates": 1,
    "errors": 0,
    "errorCode": null
  },
  "storedEvidenceCount": 8,
  "stale": false,
  "geographyTargets": [
    {
      "id": "00000000-0000-4000-8000-000000000002",
      "countryIsoCode": "GB",
      "name": "United Kingdom",
      "type": "Country",
      "adminLevel": 0,
      "parentId": null,
      "available": true
    }
  ],
  "languages": [
    "en"
  ],
  "geographyBasis": "subject",
  "timeZone": "Europe/London"
}
```

## GET /api/external/v1/topics

Required scope: `monitors.read`.

Required machine scope: monitors.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `page` (query, optional): One-based page number; default 1, maximum 100000. `{"type": "integer", "minimum": 1, "maximum": 100000, "default": 1}`

- `pageSize` (query, optional): Items per page; default 25, maximum 100. `{"type": "integer", "minimum": 1, "maximum": 100, "default": 25}`

- `asOfUtc` (query, optional): Explicit ISO8601 timestamp with offset, default now. Carry unchanged between pages; stabilizes additions but not deletions/policy changes. `{"type": "string", "format": "date-time", "description": "Default now. Carry unchanged between list pages; stabilises additions, not privacy deletions or policy changes."}`

- `q` (query, optional): Literal substring, maximum 100 characters. Monitors/topics search name; content/analytics search safe external IDs and RSS titles only. `{"type": "string", "maxLength": 100}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/topics?page=1&pageSize=25"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/topics?page=1&pageSize=25", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/topics?page=1&pageSize=25", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalTopicPage"
}
```

Synthetic response shape (not a live result):

```json
{
  "items": [
    {
      "id": "10000000-0000-4000-8000-000000000001",
      "name": "[SAMPLE] Public library services",
      "objective": "Observe public publisher coverage of public library services for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "library",
        "opening hours"
      ],
      "phrases": [
        "library services"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000002",
      "name": "[SAMPLE] Municipal recycling updates",
      "objective": "Observe public publisher coverage of municipal recycling updates for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "recycling",
        "collection day"
      ],
      "phrases": [
        "recycling updates"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000003",
      "name": "[SAMPLE] Road maintenance advisories",
      "objective": "Observe public publisher coverage of road maintenance advisories for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "road maintenance",
        "road closure"
      ],
      "phrases": [
        "maintenance advisory"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000004",
      "name": "[SAMPLE] Public transport timetables",
      "objective": "Observe public publisher coverage of public transport timetables for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "bus timetable",
        "train timetable"
      ],
      "phrases": [
        "public transport"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000005",
      "name": "[SAMPLE] Water service notices",
      "objective": "Observe public publisher coverage of water service notices for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "water service",
        "supply notice"
      ],
      "phrases": [
        "water service notice"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000006",
      "name": "[SAMPLE] Electricity service updates",
      "objective": "Observe public publisher coverage of electricity service updates for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "electricity",
        "outage notice"
      ],
      "phrases": [
        "service restoration"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000007",
      "name": "[SAMPLE] Community park facilities",
      "objective": "Observe public publisher coverage of community park facilities for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "community park",
        "public facilities"
      ],
      "phrases": [
        "park maintenance"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000008",
      "name": "[SAMPLE] Public museum exhibitions",
      "objective": "Observe public publisher coverage of public museum exhibitions for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "museum",
        "exhibition"
      ],
      "phrases": [
        "public exhibition"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000009",
      "name": "[SAMPLE] Community sports facilities",
      "objective": "Observe public publisher coverage of community sports facilities for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "sports centre",
        "facility opening"
      ],
      "phrases": [
        "sports facilities"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000010",
      "name": "[SAMPLE] Public weather advisories",
      "objective": "Observe public publisher coverage of public weather advisories for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "weather advisory",
        "rain warning"
      ],
      "phrases": [
        "public weather"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000011",
      "name": "[SAMPLE] Local business opening notices",
      "objective": "Observe public publisher coverage of local business opening notices for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "business opening",
        "shop hours"
      ],
      "phrases": [
        "opening notice"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000012",
      "name": "[SAMPLE] Community event announcements",
      "objective": "Observe public publisher coverage of community event announcements for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "community event",
        "event date"
      ],
      "phrases": [
        "community announcement"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000013",
      "name": "[SAMPLE] Public digital services",
      "objective": "Observe public publisher coverage of public digital services for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "online service",
        "service availability"
      ],
      "phrases": [
        "digital service update"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000014",
      "name": "[SAMPLE] Waste collection schedules",
      "objective": "Observe public publisher coverage of waste collection schedules for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "waste collection",
        "schedule update"
      ],
      "phrases": [
        "collection schedule"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    },
    {
      "id": "10000000-0000-4000-8000-000000000015",
      "name": "[SAMPLE] Public accessibility information",
      "objective": "Observe public publisher coverage of public accessibility information for neutral engineering acceptance only. Replace with the final owner-curated subject before operational activation.",
      "keywords": [
        "accessibility",
        "step-free access"
      ],
      "phrases": [
        "accessible facilities"
      ],
      "exclusions": [
        "advertisement",
        "sponsored giveaway"
      ],
      "sources": [
        "Rss"
      ]
    }
  ],
  "page": 1,
  "pageSize": 25,
  "totalCount": 15,
  "hasMore": false,
  "asOfUtc": "2026-11-05T08:00:00Z",
  "requestId": "synthetic-request-id"
}
```

## GET /api/external/v1/countries

Required scope: `analytics.read`.

Required machine scope: analytics.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

No query parameters accepted.

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/countries"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/countries", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/countries", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "type": "array",
  "items": {
    "$ref": "#/components/schemas/ExternalCountry"
  }
}
```

Synthetic response shape (not a live result):

```json
[
  {
    "countryIsoCode": "GB",
    "name": "United Kingdom",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "US",
    "name": "United States",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "CA",
    "name": "Canada",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "IE",
    "name": "Ireland",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "DE",
    "name": "Germany",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "AT",
    "name": "Austria",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "CH",
    "name": "Switzerland",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "FR",
    "name": "France",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "AU",
    "name": "Australia",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "ZA",
    "name": "South Africa",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "NG",
    "name": "Nigeria",
    "coverageState": "Configured sample geography; no live collection claim.",
    "providerCountryFilter": "Not assumed; depends on provider capability and explicit provenance."
  },
  {
    "countryIsoCode": "unknown",
    "name": "Unknown",
    "coverageState": "Location not established.",
    "providerCountryFilter": "Never inferred from language or identity."
  }
]
```

## GET /api/external/v1/content

Required scope: `content.read`.

Required machine scope: content.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `page` (query, optional): One-based page number; default 1, maximum 100000. `{"type": "integer", "minimum": 1, "maximum": 100000, "default": 1}`

- `pageSize` (query, optional): Items per page; default 25, maximum 100. `{"type": "integer", "minimum": 1, "maximum": 100, "default": 25}`

- `asOfUtc` (query, optional): Explicit ISO8601 timestamp with offset, default now. Carry unchanged between pages; stabilizes additions but not deletions/policy changes. `{"type": "string", "format": "date-time", "description": "Default now. Carry unchanged between list pages; stabilises additions, not privacy deletions or policy changes."}`

- `monitorId` (query, optional): Owned monitor UUID; restrict evidence to unsuppressed matches. Foreign identifiers return an empty list. `{"type": "string", "format": "uuid"}`

- `topicId` (query, optional): Alias for monitorId. If both are supplied they must match. `{"type": "string", "format": "uuid"}`

- `provider` (query, optional): One provider name, case-insensitive; selection does not enable collection. `{"type": "string", "enum": ["X", "Facebook", "Instagram", "YouTube", "Rss", "Manual"]}`

- `country` (query, optional): Assigned ISO2 codes separated by commas with OR semantics and deduplication; case-insensitive. unknown matches no recorded geography for the selected role (excludes policy-withheld evidence). Omit or use global alone for no geography restriction. Global cannot be combined with country codes. `{"type": "string", "maxLength": 1100}`

- `geographyRole` (query, optional): Evidence role: subject (default), origin or source. No claim of residence, nationality or identity. `{"type": "string", "enum": ["subject", "origin", "source"], "default": "subject"}`

- `sentiment` (query, optional): One available sentiment state/label; excludes policy-withheld metrics. `{"type": "string", "enum": ["positive", "negative", "neutral", "mixed", "unclear", "not_analyzed", "failed", "uncertain"]}`

- `from` (query, optional): Inclusive publication time with collection-time fallback. Explicit ISO8601 offset required. Defaults to seven days before to; maximum interval 31 days. `{"type": "string", "format": "date-time", "description": "Inclusive publication time (collection fallback). Default to minus 7 days; explicit offset required. Maximum interval 31 days."}`

- `to` (query, optional): Exclusive end, default asOfUtc. Must be greater than from and no later than asOfUtc. `{"type": "string", "format": "date-time", "description": "Exclusive end, default asOfUtc; must not exceed asOfUtc."}`

- `q` (query, optional): Literal substring, maximum 100 characters. Monitors/topics search name; content/analytics search safe external IDs and RSS titles only. `{"type": "string", "maxLength": 100}`

- `language` (query, optional): Exact case-insensitive language tag, for example en, fr or en-GB; independent of geography. en does not match en-GB. Missing language never matches. No translation or identity inference. `{"type": "string", "maxLength": 35}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/content?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/content?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/content?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalContentPage"
}
```

Synthetic response shape (not a live result):

```json
{
  "items": [
    {
      "id": "20000000-0000-4000-8000-000000000001",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/1",
      "title": "[SAMPLE] Public service update 1",
      "language": "en",
      "publishedAtUtc": "2026-11-05T07:00:00Z",
      "collectedAtUtc": "2026-11-05T07:45:00Z",
      "sentiment": {
        "state": "analyzed",
        "label": "positive",
        "analyzedAtUtc": "2026-11-05T08:00:00Z",
        "method": "synthetic-fixture-only",
        "analyzerVersion": "sample/v1",
        "score": null,
        "uncertainty": "Synthetic result; not live analysis or a calibrated confidence.",
        "confidenceMeaning": "No calibrated confidence score is provided."
      },
      "geography": [
        {
          "countryIsoCode": "GB",
          "countryName": "United Kingdom",
          "role": "subject",
          "provenance": "Manual",
          "resolutionAdminLevel": 0,
          "uncertainty": "Synthetic recorded subject location; not author nationality."
        }
      ],
      "monitorIds": [
        "10000000-0000-4000-8000-000000000001"
      ],
      "redistributionMode": "metadata_and_source_reference_only",
      "fullTextAvailable": false,
      "geographyState": "recorded"
    },
    {
      "id": "20000000-0000-4000-8000-000000000002",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/2",
      "title": "[SAMPLE] Public service update 2",
      "language": "en",
      "publishedAtUtc": "2026-11-05T07:00:00Z",
      "collectedAtUtc": "2026-11-05T07:45:00Z",
      "sentiment": {
        "state": "analyzed",
        "label": "positive",
        "analyzedAtUtc": "2026-11-05T08:00:00Z",
        "method": "synthetic-fixture-only",
        "analyzerVersion": "sample/v1",
        "score": null,
        "uncertainty": "Synthetic result; not live analysis or a calibrated confidence.",
        "confidenceMeaning": "No calibrated confidence score is provided."
      },
      "geography": [
        {
          "countryIsoCode": "GB",
          "countryName": "United Kingdom",
          "role": "subject",
          "provenance": "Manual",
          "resolutionAdminLevel": 0,
          "uncertainty": "Synthetic recorded subject location; not author nationality."
        }
      ],
      "monitorIds": [
        "10000000-0000-4000-8000-000000000001"
      ],
      "redistributionMode": "metadata_and_source_reference_only",
      "fullTextAvailable": false,
      "geographyState": "recorded"
    },
    {
      "id": "20000000-0000-4000-8000-000000000003",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/3",
      "title": "[SAMPLE] Public service update 3",
      "language": "en",
      "publishedAtUtc": "2026-11-05T07:00:00Z",
      "collectedAtUtc": "2026-11-05T07:45:00Z",
      "sentiment": {
        "state": "analyzed",
        "label": "negative",
        "analyzedAtUtc": "2026-11-05T08:00:00Z",
        "method": "synthetic-fixture-only",
        "analyzerVersion": "sample/v1",
        "score": null,
        "uncertainty": "Synthetic result; not live analysis or a calibrated confidence.",
        "confidenceMeaning": "No calibrated confidence score is provided."
      },
      "geography": [
        {
          "countryIsoCode": "US",
          "countryName": "United States",
          "role": "subject",
          "provenance": "Manual",
          "resolutionAdminLevel": 0,
          "uncertainty": "Synthetic recorded subject location; not author nationality."
        }
      ],
      "monitorIds": [
        "10000000-0000-4000-8000-000000000001"
      ],
      "redistributionMode": "metadata_and_source_reference_only",
      "fullTextAvailable": false,
      "geographyState": "recorded"
    },
    {
      "id": "20000000-0000-4000-8000-000000000004",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/4",
      "title": "[SAMPLE] Public service update 4",
      "language": "en",
      "publishedAtUtc": "2026-11-05T07:00:00Z",
      "collectedAtUtc": "2026-11-05T07:45:00Z",
      "sentiment": {
        "state": "analyzed",
        "label": "neutral",
        "analyzedAtUtc": "2026-11-05T08:00:00Z",
        "method": "synthetic-fixture-only",
        "analyzerVersion": "sample/v1",
        "score": null,
        "uncertainty": "Synthetic result; not live analysis or a calibrated confidence.",
        "confidenceMeaning": "No calibrated confidence score is provided."
      },
      "geography": [
        {
          "countryIsoCode": "NG",
          "countryName": "Nigeria",
          "role": "subject",
          "provenance": "Manual",
          "resolutionAdminLevel": 0,
          "uncertainty": "Synthetic recorded subject location; not author nationality."
        }
      ],
      "monitorIds": [
        "10000000-0000-4000-8000-000000000001"
      ],
      "redistributionMode": "metadata_and_source_reference_only",
      "fullTextAvailable": false,
      "geographyState": "recorded"
    },
    {
      "id": "20000000-0000-4000-8000-000000000005",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/5",
      "title": "[SAMPLE] Public service update 5",
      "language": "en",
      "publishedAtUtc": "2026-11-05T07:00:00Z",
      "collectedAtUtc": "2026-11-05T07:45:00Z",
      "sentiment": {
        "state": "not_analyzed",
        "label": null,
        "analyzedAtUtc": null,
        "method": null,
        "analyzerVersion": null,
        "score": null,
        "uncertainty": "Synthetic result; not live analysis or a calibrated confidence.",
        "confidenceMeaning": "No calibrated confidence score is provided."
      },
      "geography": [],
      "monitorIds": [
        "10000000-0000-4000-8000-000000000001"
      ],
      "redistributionMode": "metadata_and_source_reference_only",
      "fullTextAvailable": false,
      "geographyState": "unknown"
    },
    {
      "id": "20000000-0000-4000-8000-000000000006",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/6",
      "title": "[SAMPLE] Public service update 6",
      "language": "en",
      "publishedAtUtc": "2026-11-05T07:00:00Z",
      "collectedAtUtc": "2026-11-05T07:45:00Z",
      "sentiment": {
        "state": "not_analyzed",
        "label": null,
        "analyzedAtUtc": null,
        "method": null,
        "analyzerVersion": null,
        "score": null,
        "uncertainty": "Synthetic result; not live analysis or a calibrated confidence.",
        "confidenceMeaning": "No calibrated confidence score is provided."
      },
      "geography": [],
      "monitorIds": [
        "10000000-0000-4000-8000-000000000001"
      ],
      "redistributionMode": "metadata_and_source_reference_only",
      "fullTextAvailable": false,
      "geographyState": "unknown"
    },
    {
      "id": "20000000-0000-4000-8000-000000000007",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/7",
      "title": "[SAMPLE] Public service update 7",
      "language": "en",
      "publishedAtUtc": "2026-11-05T07:00:00Z",
      "collectedAtUtc": "2026-11-05T07:45:00Z",
      "sentiment": {
        "state": "uncertain",
        "label": null,
        "analyzedAtUtc": "2026-11-05T08:00:00Z",
        "method": "synthetic-fixture-only",
        "analyzerVersion": "sample/v1",
        "score": null,
        "uncertainty": "Synthetic result; not live analysis or a calibrated confidence.",
        "confidenceMeaning": "No calibrated confidence score is provided."
      },
      "geography": [],
      "monitorIds": [
        "10000000-0000-4000-8000-000000000001"
      ],
      "redistributionMode": "metadata_and_source_reference_only",
      "fullTextAvailable": false,
      "geographyState": "unknown"
    },
    {
      "id": "20000000-0000-4000-8000-000000000008",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/8",
      "title": "[SAMPLE] Public service update 8",
      "language": "en",
      "publishedAtUtc": "2026-11-05T07:00:00Z",
      "collectedAtUtc": "2026-11-05T07:45:00Z",
      "sentiment": {
        "state": "failed",
        "label": null,
        "analyzedAtUtc": null,
        "method": null,
        "analyzerVersion": null,
        "score": null,
        "uncertainty": "Synthetic result; not live analysis or a calibrated confidence.",
        "confidenceMeaning": "No calibrated confidence score is provided."
      },
      "geography": [],
      "monitorIds": [
        "10000000-0000-4000-8000-000000000001"
      ],
      "redistributionMode": "metadata_and_source_reference_only",
      "fullTextAvailable": false,
      "geographyState": "unknown"
    }
  ],
  "page": 1,
  "pageSize": 25,
  "totalCount": 8,
  "hasMore": false,
  "asOfUtc": "2026-11-05T08:00:00Z",
  "requestId": "synthetic-request-id"
}
```

## GET /api/external/v1/content/{id}

Required scope: `content.read`.

Required machine scope: content.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `id` (path, required): Owned record UUID returned by the corresponding list; inaccessible, absent or expired records return 404. `{"type": "string", "format": "uuid"}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/content/00000000-0000-4000-8000-000000000001"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/content/00000000-0000-4000-8000-000000000001", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/content/00000000-0000-4000-8000-000000000001", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **404**: Absent, expired or inaccessible resource. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalContent"
}
```

Synthetic response shape (not a live result):

```json
{
  "id": "20000000-0000-4000-8000-000000000001",
  "provider": "Rss",
  "externalId": null,
  "canonicalUrl": "https://example.com/spectra-sample/1",
  "title": "[SAMPLE] Public service update 1",
  "language": "en",
  "publishedAtUtc": "2026-11-05T07:00:00Z",
  "collectedAtUtc": "2026-11-05T07:45:00Z",
  "sentiment": {
    "state": "analyzed",
    "label": "positive",
    "analyzedAtUtc": "2026-11-05T08:00:00Z",
    "method": "synthetic-fixture-only",
    "analyzerVersion": "sample/v1",
    "score": null,
    "uncertainty": "Synthetic result; not live analysis or a calibrated confidence.",
    "confidenceMeaning": "No calibrated confidence score is provided."
  },
  "geography": [
    {
      "countryIsoCode": "GB",
      "countryName": "United Kingdom",
      "role": "subject",
      "provenance": "Manual",
      "resolutionAdminLevel": 0,
      "uncertainty": "Synthetic recorded subject location; not author nationality."
    }
  ],
  "monitorIds": [
    "10000000-0000-4000-8000-000000000001"
  ],
  "redistributionMode": "metadata_and_source_reference_only",
  "fullTextAvailable": false,
  "geographyState": "recorded"
}
```

## GET /api/external/v1/analytics/summary

Required scope: `analytics.read`.

Required machine scope: analytics.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `page` (query, optional): One-based page number; default 1, maximum 100000. Validated only; analytics aggregates are not paginated. `{"type": "integer", "minimum": 1, "maximum": 100000, "default": 1}`

- `pageSize` (query, optional): Items per page; default 25, maximum 100. Validated only; analytics aggregates are not paginated. `{"type": "integer", "minimum": 1, "maximum": 100, "default": 25}`

- `asOfUtc` (query, optional): Explicit ISO8601 timestamp with offset, default now. Carry unchanged between pages; stabilizes additions but not deletions/policy changes. `{"type": "string", "format": "date-time", "description": "Default now. Carry unchanged between list pages; stabilises additions, not privacy deletions or policy changes."}`

- `monitorId` (query, optional): Owned monitor UUID; restrict evidence to unsuppressed matches. Foreign identifiers return an empty list. `{"type": "string", "format": "uuid"}`

- `topicId` (query, optional): Alias for monitorId. If both are supplied they must match. `{"type": "string", "format": "uuid"}`

- `provider` (query, optional): One provider name, case-insensitive; selection does not enable collection. `{"type": "string", "enum": ["X", "Facebook", "Instagram", "YouTube", "Rss", "Manual"]}`

- `country` (query, optional): Assigned ISO2 codes separated by commas with OR semantics and deduplication; case-insensitive. unknown matches no recorded geography for the selected role (excludes policy-withheld evidence). Omit or use global alone for no geography restriction. Global cannot be combined with country codes. `{"type": "string", "maxLength": 1100}`

- `geographyRole` (query, optional): Evidence role: subject (default), origin or source. No claim of residence, nationality or identity. `{"type": "string", "enum": ["subject", "origin", "source"], "default": "subject"}`

- `sentiment` (query, optional): One available sentiment state/label; excludes policy-withheld metrics. `{"type": "string", "enum": ["positive", "negative", "neutral", "mixed", "unclear", "not_analyzed", "failed", "uncertain"]}`

- `from` (query, optional): Inclusive publication time with collection-time fallback. Explicit ISO8601 offset required. Defaults to seven days before to; maximum interval 31 days. `{"type": "string", "format": "date-time", "description": "Inclusive publication time (collection fallback). Default to minus 7 days; explicit offset required. Maximum interval 31 days."}`

- `to` (query, optional): Exclusive end, default asOfUtc. Must be greater than from and no later than asOfUtc. `{"type": "string", "format": "date-time", "description": "Exclusive end, default asOfUtc; must not exceed asOfUtc."}`

- `q` (query, optional): Literal substring, maximum 100 characters. Monitors/topics search name; content/analytics search safe external IDs and RSS titles only. `{"type": "string", "maxLength": 100}`

- `language` (query, optional): Exact case-insensitive language tag, for example en, fr or en-GB; independent of geography. en does not match en-GB. Missing language never matches. No translation or identity inference. `{"type": "string", "maxLength": 35}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/analytics/summary?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/analytics/summary?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/analytics/summary?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalAnalytics"
}
```

Synthetic response shape (not a live result):

```json
{
  "generatedAtUtc": "2026-11-05T08:00:00Z",
  "asOfUtc": "2026-11-05T08:00:00Z",
  "fromUtc": "2026-11-04T08:00:00Z",
  "toUtc": "2026-11-05T08:00:00Z",
  "dateBasis": "publication time, with collected time only where publication time is unknown",
  "totalEvidenceCount": 8,
  "sampleSize": 8,
  "truncated": false,
  "sentiment": [
    {
      "key": "positive",
      "count": 2
    },
    {
      "key": "negative",
      "count": 1
    },
    {
      "key": "neutral",
      "count": 1
    },
    {
      "key": "not_analyzed",
      "count": 2
    },
    {
      "key": "uncertain",
      "count": 1
    },
    {
      "key": "failed",
      "count": 1
    }
  ],
  "countries": [
    {
      "countryIsoCode": "GB",
      "name": "United Kingdom",
      "evidenceCount": 2,
      "geographyRole": "subject",
      "provenance": [
        "Manual"
      ]
    },
    {
      "countryIsoCode": "US",
      "name": "United States",
      "evidenceCount": 1,
      "geographyRole": "subject",
      "provenance": [
        "Manual"
      ]
    },
    {
      "countryIsoCode": "NG",
      "name": "Nigeria",
      "evidenceCount": 1,
      "geographyRole": "subject",
      "provenance": [
        "Manual"
      ]
    }
  ],
  "unknownLocationCount": 4,
  "sources": [
    {
      "key": "Rss",
      "count": 8
    }
  ],
  "trends": [
    {
      "dateUtc": "2026-11-05",
      "evidenceCount": 8,
      "positive": 2,
      "negative": 1,
      "neutral": 1,
      "mixed": 0,
      "unclear": 0,
      "notAnalyzed": 2,
      "failed": 1,
      "uncertain": 1,
      "withheld": 0
    }
  ],
  "sourceCoverage": [
    {
      "provider": "Rss",
      "collectionVerified": true,
      "storedEvidenceCount": 8,
      "successfulRuns": 3,
      "failedRuns": 1,
      "lastSuccessfulCollectionAtUtc": "2026-11-05T07:45:00Z",
      "state": "sample_partially_stale",
      "storedContentCoverage": true
    }
  ],
  "evidenceReferences": [
    {
      "contentId": "20000000-0000-4000-8000-000000000001",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/1"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000002",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/2"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000003",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/3"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000004",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/4"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000005",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/5"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000006",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/6"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000007",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/7"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000008",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/8"
    }
  ],
  "limitations": [
    "SYNTHETIC SAMPLE DATA: no live collection or sentiment is demonstrated.",
    "Collected evidence is not a representative public poll.",
    "Unknown country is retained; nationality or diaspora membership is never inferred."
  ],
  "evidenceFingerprint": "5c627018beb312d732ccbf735f5716270153c2567bb6746743517e14eb7fe383",
  "withheldLocationCount": 0
}
```

## GET /api/external/v1/analytics/sentiment

Required scope: `analytics.read`.

Required machine scope: analytics.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `page` (query, optional): One-based page number; default 1, maximum 100000. Validated only; analytics aggregates are not paginated. `{"type": "integer", "minimum": 1, "maximum": 100000, "default": 1}`

- `pageSize` (query, optional): Items per page; default 25, maximum 100. Validated only; analytics aggregates are not paginated. `{"type": "integer", "minimum": 1, "maximum": 100, "default": 25}`

- `asOfUtc` (query, optional): Explicit ISO8601 timestamp with offset, default now. Carry unchanged between pages; stabilizes additions but not deletions/policy changes. `{"type": "string", "format": "date-time", "description": "Default now. Carry unchanged between list pages; stabilises additions, not privacy deletions or policy changes."}`

- `monitorId` (query, optional): Owned monitor UUID; restrict evidence to unsuppressed matches. Foreign identifiers return an empty list. `{"type": "string", "format": "uuid"}`

- `topicId` (query, optional): Alias for monitorId. If both are supplied they must match. `{"type": "string", "format": "uuid"}`

- `provider` (query, optional): One provider name, case-insensitive; selection does not enable collection. `{"type": "string", "enum": ["X", "Facebook", "Instagram", "YouTube", "Rss", "Manual"]}`

- `country` (query, optional): Assigned ISO2 codes separated by commas with OR semantics and deduplication; case-insensitive. unknown matches no recorded geography for the selected role (excludes policy-withheld evidence). Omit or use global alone for no geography restriction. Global cannot be combined with country codes. `{"type": "string", "maxLength": 1100}`

- `geographyRole` (query, optional): Evidence role: subject (default), origin or source. No claim of residence, nationality or identity. `{"type": "string", "enum": ["subject", "origin", "source"], "default": "subject"}`

- `sentiment` (query, optional): One available sentiment state/label; excludes policy-withheld metrics. `{"type": "string", "enum": ["positive", "negative", "neutral", "mixed", "unclear", "not_analyzed", "failed", "uncertain"]}`

- `from` (query, optional): Inclusive publication time with collection-time fallback. Explicit ISO8601 offset required. Defaults to seven days before to; maximum interval 31 days. `{"type": "string", "format": "date-time", "description": "Inclusive publication time (collection fallback). Default to minus 7 days; explicit offset required. Maximum interval 31 days."}`

- `to` (query, optional): Exclusive end, default asOfUtc. Must be greater than from and no later than asOfUtc. `{"type": "string", "format": "date-time", "description": "Exclusive end, default asOfUtc; must not exceed asOfUtc."}`

- `q` (query, optional): Literal substring, maximum 100 characters. Monitors/topics search name; content/analytics search safe external IDs and RSS titles only. `{"type": "string", "maxLength": 100}`

- `language` (query, optional): Exact case-insensitive language tag, for example en, fr or en-GB; independent of geography. en does not match en-GB. Missing language never matches. No translation or identity inference. `{"type": "string", "maxLength": 35}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/analytics/sentiment?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/analytics/sentiment?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/analytics/sentiment?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalAnalytics"
}
```

Synthetic response shape (not a live result):

```json
{
  "generatedAtUtc": "2026-11-05T08:00:00Z",
  "asOfUtc": "2026-11-05T08:00:00Z",
  "fromUtc": "2026-11-04T08:00:00Z",
  "toUtc": "2026-11-05T08:00:00Z",
  "dateBasis": "publication time, with collected time only where publication time is unknown",
  "totalEvidenceCount": 8,
  "sampleSize": 8,
  "truncated": false,
  "sentiment": [
    {
      "key": "positive",
      "count": 2
    },
    {
      "key": "negative",
      "count": 1
    },
    {
      "key": "neutral",
      "count": 1
    },
    {
      "key": "not_analyzed",
      "count": 2
    },
    {
      "key": "uncertain",
      "count": 1
    },
    {
      "key": "failed",
      "count": 1
    }
  ],
  "countries": [
    {
      "countryIsoCode": "GB",
      "name": "United Kingdom",
      "evidenceCount": 2,
      "geographyRole": "subject",
      "provenance": [
        "Manual"
      ]
    },
    {
      "countryIsoCode": "US",
      "name": "United States",
      "evidenceCount": 1,
      "geographyRole": "subject",
      "provenance": [
        "Manual"
      ]
    },
    {
      "countryIsoCode": "NG",
      "name": "Nigeria",
      "evidenceCount": 1,
      "geographyRole": "subject",
      "provenance": [
        "Manual"
      ]
    }
  ],
  "unknownLocationCount": 4,
  "sources": [
    {
      "key": "Rss",
      "count": 8
    }
  ],
  "trends": [
    {
      "dateUtc": "2026-11-05",
      "evidenceCount": 8,
      "positive": 2,
      "negative": 1,
      "neutral": 1,
      "mixed": 0,
      "unclear": 0,
      "notAnalyzed": 2,
      "failed": 1,
      "uncertain": 1,
      "withheld": 0
    }
  ],
  "sourceCoverage": [
    {
      "provider": "Rss",
      "collectionVerified": true,
      "storedEvidenceCount": 8,
      "successfulRuns": 3,
      "failedRuns": 1,
      "lastSuccessfulCollectionAtUtc": "2026-11-05T07:45:00Z",
      "state": "sample_partially_stale",
      "storedContentCoverage": true
    }
  ],
  "evidenceReferences": [
    {
      "contentId": "20000000-0000-4000-8000-000000000001",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/1"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000002",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/2"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000003",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/3"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000004",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/4"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000005",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/5"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000006",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/6"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000007",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/7"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000008",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/8"
    }
  ],
  "limitations": [
    "SYNTHETIC SAMPLE DATA: no live collection or sentiment is demonstrated.",
    "Collected evidence is not a representative public poll.",
    "Unknown country is retained; nationality or diaspora membership is never inferred."
  ],
  "evidenceFingerprint": "5c627018beb312d732ccbf735f5716270153c2567bb6746743517e14eb7fe383",
  "withheldLocationCount": 0
}
```

## GET /api/external/v1/analytics/countries

Required scope: `analytics.read`.

Required machine scope: analytics.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `page` (query, optional): One-based page number; default 1, maximum 100000. Validated only; analytics aggregates are not paginated. `{"type": "integer", "minimum": 1, "maximum": 100000, "default": 1}`

- `pageSize` (query, optional): Items per page; default 25, maximum 100. Validated only; analytics aggregates are not paginated. `{"type": "integer", "minimum": 1, "maximum": 100, "default": 25}`

- `asOfUtc` (query, optional): Explicit ISO8601 timestamp with offset, default now. Carry unchanged between pages; stabilizes additions but not deletions/policy changes. `{"type": "string", "format": "date-time", "description": "Default now. Carry unchanged between list pages; stabilises additions, not privacy deletions or policy changes."}`

- `monitorId` (query, optional): Owned monitor UUID; restrict evidence to unsuppressed matches. Foreign identifiers return an empty list. `{"type": "string", "format": "uuid"}`

- `topicId` (query, optional): Alias for monitorId. If both are supplied they must match. `{"type": "string", "format": "uuid"}`

- `provider` (query, optional): One provider name, case-insensitive; selection does not enable collection. `{"type": "string", "enum": ["X", "Facebook", "Instagram", "YouTube", "Rss", "Manual"]}`

- `country` (query, optional): Assigned ISO2 codes separated by commas with OR semantics and deduplication; case-insensitive. unknown matches no recorded geography for the selected role (excludes policy-withheld evidence). Omit or use global alone for no geography restriction. Global cannot be combined with country codes. `{"type": "string", "maxLength": 1100}`

- `geographyRole` (query, optional): Evidence role: subject (default), origin or source. No claim of residence, nationality or identity. `{"type": "string", "enum": ["subject", "origin", "source"], "default": "subject"}`

- `sentiment` (query, optional): One available sentiment state/label; excludes policy-withheld metrics. `{"type": "string", "enum": ["positive", "negative", "neutral", "mixed", "unclear", "not_analyzed", "failed", "uncertain"]}`

- `from` (query, optional): Inclusive publication time with collection-time fallback. Explicit ISO8601 offset required. Defaults to seven days before to; maximum interval 31 days. `{"type": "string", "format": "date-time", "description": "Inclusive publication time (collection fallback). Default to minus 7 days; explicit offset required. Maximum interval 31 days."}`

- `to` (query, optional): Exclusive end, default asOfUtc. Must be greater than from and no later than asOfUtc. `{"type": "string", "format": "date-time", "description": "Exclusive end, default asOfUtc; must not exceed asOfUtc."}`

- `q` (query, optional): Literal substring, maximum 100 characters. Monitors/topics search name; content/analytics search safe external IDs and RSS titles only. `{"type": "string", "maxLength": 100}`

- `language` (query, optional): Exact case-insensitive language tag, for example en, fr or en-GB; independent of geography. en does not match en-GB. Missing language never matches. No translation or identity inference. `{"type": "string", "maxLength": 35}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/analytics/countries?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/analytics/countries?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/analytics/countries?country=GB&geographyRole=subject&from=2026-11-04T00%3A00%3A00Z&to=2026-11-05T00%3A00%3A00Z&asOfUtc=2026-11-05T08%3A00%3A00Z&page=1&pageSize=25", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalAnalytics"
}
```

Synthetic response shape (not a live result):

```json
{
  "generatedAtUtc": "2026-11-05T08:00:00Z",
  "asOfUtc": "2026-11-05T08:00:00Z",
  "fromUtc": "2026-11-04T08:00:00Z",
  "toUtc": "2026-11-05T08:00:00Z",
  "dateBasis": "publication time, with collected time only where publication time is unknown",
  "totalEvidenceCount": 8,
  "sampleSize": 8,
  "truncated": false,
  "sentiment": [
    {
      "key": "positive",
      "count": 2
    },
    {
      "key": "negative",
      "count": 1
    },
    {
      "key": "neutral",
      "count": 1
    },
    {
      "key": "not_analyzed",
      "count": 2
    },
    {
      "key": "uncertain",
      "count": 1
    },
    {
      "key": "failed",
      "count": 1
    }
  ],
  "countries": [
    {
      "countryIsoCode": "GB",
      "name": "United Kingdom",
      "evidenceCount": 2,
      "geographyRole": "subject",
      "provenance": [
        "Manual"
      ]
    },
    {
      "countryIsoCode": "US",
      "name": "United States",
      "evidenceCount": 1,
      "geographyRole": "subject",
      "provenance": [
        "Manual"
      ]
    },
    {
      "countryIsoCode": "NG",
      "name": "Nigeria",
      "evidenceCount": 1,
      "geographyRole": "subject",
      "provenance": [
        "Manual"
      ]
    }
  ],
  "unknownLocationCount": 4,
  "sources": [
    {
      "key": "Rss",
      "count": 8
    }
  ],
  "trends": [
    {
      "dateUtc": "2026-11-05",
      "evidenceCount": 8,
      "positive": 2,
      "negative": 1,
      "neutral": 1,
      "mixed": 0,
      "unclear": 0,
      "notAnalyzed": 2,
      "failed": 1,
      "uncertain": 1,
      "withheld": 0
    }
  ],
  "sourceCoverage": [
    {
      "provider": "Rss",
      "collectionVerified": true,
      "storedEvidenceCount": 8,
      "successfulRuns": 3,
      "failedRuns": 1,
      "lastSuccessfulCollectionAtUtc": "2026-11-05T07:45:00Z",
      "state": "sample_partially_stale",
      "storedContentCoverage": true
    }
  ],
  "evidenceReferences": [
    {
      "contentId": "20000000-0000-4000-8000-000000000001",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/1"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000002",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/2"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000003",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/3"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000004",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/4"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000005",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/5"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000006",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/6"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000007",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/7"
    },
    {
      "contentId": "20000000-0000-4000-8000-000000000008",
      "provider": "Rss",
      "externalId": null,
      "canonicalUrl": "https://example.com/spectra-sample/8"
    }
  ],
  "limitations": [
    "SYNTHETIC SAMPLE DATA: no live collection or sentiment is demonstrated.",
    "Collected evidence is not a representative public poll.",
    "Unknown country is retained; nationality or diaspora membership is never inferred."
  ],
  "evidenceFingerprint": "5c627018beb312d732ccbf735f5716270153c2567bb6746743517e14eb7fe383",
  "withheldLocationCount": 0
}
```

## GET /api/external/v1/reports

Required scope: `reports.read`.

Required machine scope: reports.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `page` (query, optional): One-based page number; default 1, maximum 100000. `{"type": "integer", "minimum": 1, "maximum": 100000, "default": 1}`

- `pageSize` (query, optional): Items per page; default 25, maximum 100. `{"type": "integer", "minimum": 1, "maximum": 100, "default": 25}`

- `status` (query, optional): Stored state. Monitor values are case-insensitive; reports match stored state exactly. `{"type": "string"}`

- `asOfUtc` (query, optional): Explicit ISO8601 timestamp with offset, default now. Carry unchanged between pages; stabilizes additions but not deletions/policy changes. `{"type": "string", "format": "date-time", "description": "Default now. Carry unchanged between list pages; stabilises additions, not privacy deletions or policy changes."}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/reports?page=1&pageSize=25"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/reports?page=1&pageSize=25", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/reports?page=1&pageSize=25", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalReportSummaryPage"
}
```

Synthetic response shape (not a live result):

```json
{
  "items": [
    {
      "id": "30000000-0000-4000-8000-000000000001",
      "title": "[SAMPLE] Public-service evidence snapshot",
      "state": "Completed",
      "requestedAtUtc": "2026-11-05T07:59:00Z",
      "generatedAtUtc": "2026-11-05T08:00:00Z",
      "expiresAtUtc": "2026-12-05T08:00:00Z",
      "snapshotSha256": "13118df3c9b79cc3dfe0293b9f23c9596e9430f3d14f58cdb51439a96f9c69b9",
      "failureCode": null
    }
  ],
  "page": 1,
  "pageSize": 25,
  "totalCount": 1,
  "hasMore": false,
  "asOfUtc": "2026-11-05T08:00:00Z",
  "requestId": "synthetic-request-id"
}
```

## GET /api/external/v1/reports/{id}

Required scope: `reports.read`.

Required machine scope: reports.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `id` (path, required): Owned record UUID returned by the corresponding list; inaccessible, absent or expired records return 404. `{"type": "string", "format": "uuid"}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/reports/00000000-0000-4000-8000-000000000001"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/reports/00000000-0000-4000-8000-000000000001", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/reports/00000000-0000-4000-8000-000000000001", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **404**: Absent, expired or inaccessible resource. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalReportDetail"
}
```

Synthetic response shape (not a live result):

```json
{
  "report": {
    "id": "30000000-0000-4000-8000-000000000001",
    "title": "[SAMPLE] Public-service evidence snapshot",
    "state": "Completed",
    "requestedAtUtc": "2026-11-05T07:59:00Z",
    "generatedAtUtc": "2026-11-05T08:00:00Z",
    "expiresAtUtc": "2026-12-05T08:00:00Z",
    "snapshotSha256": "13118df3c9b79cc3dfe0293b9f23c9596e9430f3d14f58cdb51439a96f9c69b9",
    "failureCode": null
  },
  "results": {
    "generatedAtUtc": "2026-11-05T08:00:00Z",
    "asOfUtc": "2026-11-05T08:00:00Z",
    "fromUtc": "2026-11-04T08:00:00Z",
    "toUtc": "2026-11-05T08:00:00Z",
    "dateBasis": "publication time, with collected time only where publication time is unknown",
    "totalEvidenceCount": 8,
    "sampleSize": 8,
    "truncated": false,
    "sentiment": [
      {
        "key": "positive",
        "count": 2
      },
      {
        "key": "negative",
        "count": 1
      },
      {
        "key": "neutral",
        "count": 1
      },
      {
        "key": "not_analyzed",
        "count": 2
      },
      {
        "key": "uncertain",
        "count": 1
      },
      {
        "key": "failed",
        "count": 1
      }
    ],
    "countries": [
      {
        "countryIsoCode": "GB",
        "name": "United Kingdom",
        "evidenceCount": 2,
        "geographyRole": "subject",
        "provenance": [
          "Manual"
        ]
      },
      {
        "countryIsoCode": "US",
        "name": "United States",
        "evidenceCount": 1,
        "geographyRole": "subject",
        "provenance": [
          "Manual"
        ]
      },
      {
        "countryIsoCode": "NG",
        "name": "Nigeria",
        "evidenceCount": 1,
        "geographyRole": "subject",
        "provenance": [
          "Manual"
        ]
      }
    ],
    "unknownLocationCount": 4,
    "sources": [
      {
        "key": "Rss",
        "count": 8
      }
    ],
    "trends": [
      {
        "dateUtc": "2026-11-05",
        "evidenceCount": 8,
        "positive": 2,
        "negative": 1,
        "neutral": 1,
        "mixed": 0,
        "unclear": 0,
        "notAnalyzed": 2,
        "failed": 1,
        "uncertain": 1,
        "withheld": 0
      }
    ],
    "sourceCoverage": [
      {
        "provider": "Rss",
        "collectionVerified": true,
        "storedEvidenceCount": 8,
        "successfulRuns": 3,
        "failedRuns": 1,
        "lastSuccessfulCollectionAtUtc": "2026-11-05T07:45:00Z",
        "state": "sample_partially_stale",
        "storedContentCoverage": true
      }
    ],
    "evidenceReferences": [
      {
        "contentId": "20000000-0000-4000-8000-000000000001",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/1"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000002",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/2"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000003",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/3"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000004",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/4"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000005",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/5"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000006",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/6"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000007",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/7"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000008",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/8"
      }
    ],
    "limitations": [
      "SYNTHETIC SAMPLE DATA: no live collection or sentiment is demonstrated.",
      "Collected evidence is not a representative public poll.",
      "Unknown country is retained; nationality or diaspora membership is never inferred."
    ],
    "evidenceFingerprint": "5c627018beb312d732ccbf735f5716270153c2567bb6746743517e14eb7fe383",
    "withheldLocationCount": 0
  },
  "schemaVersion": "spectra.ddm.aggregate-report/v1"
}
```

## GET /api/external/v1/reports/{id}/download

Required scope: `reports.read`.

Required machine scope: reports.read. Tenant scope is fixed by the credential; no tenant selector is accepted.

### Parameters

- `id` (path, required): Owned record UUID returned by the corresponding list; inaccessible, absent or expired records return 404. `{"type": "string", "format": "uuid"}`

- `format` (query, optional): json (default) for the report envelope, or csv for aggregate rows. Case-sensitive. `{"type": "string", "enum": ["json", "csv"], "default": "json"}`

### cURL (server shell)

```sh
# Secret-manager environment; tracing disabled. Token stays out of curl argv.
curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 --config - <<EOF
url = "https://api.spectra-ai.app/api/external/v1/reports/00000000-0000-4000-8000-000000000001/download?format=json"
header = "Authorization: Bearer ${SPECTRA_API_TOKEN}"
header = "Accept: application/json"
EOF
```

### JavaScript (server only)

```javascript
const response = await fetch("https://api.spectra-ai.app/api/external/v1/reports/00000000-0000-4000-8000-000000000001/download?format=json", {
  headers: {Authorization: `Bearer ${process.env.SPECTRA_API_TOKEN}`, Accept: "application/json"},
  redirect: "error",
  signal: AbortSignal.timeout(15000)
});
if (!response.ok) throw new Error(`SPECTRA HTTP ${response.status}`);
const data = await response.json();
```

### Python (server only)

```python
import json, os, urllib.request, urllib.error
class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        raise urllib.error.HTTPError(req.full_url, code, "Redirect refused", headers, fp)
request = urllib.request.Request("https://api.spectra-ai.app/api/external/v1/reports/00000000-0000-4000-8000-000000000001/download?format=json", headers={
    "Authorization": "Bearer " + os.environ["SPECTRA_API_TOKEN"],
    "Accept": "application/json"})
with urllib.request.build_opener(NoRedirect()).open(request, timeout=15) as response:
    data = json.load(response)
```

### Responses

- **200**: Authorised stored result; no collection, paid analysis or publication is triggered. Content types: application/json, text/csv.

- **400**: Invalid filters or caller-selected tenant. Content types: application/problem+json.

- **401**: Invalid, expired or revoked machine credential. Content types: application/problem+json.

- **403**: insufficient_scope or access_denied after live approval/tenant recheck. Content types: application/problem+json.

- **404**: Absent, expired or inaccessible resource. Content types: application/problem+json.

- **405**: External API is read-only. Content types: application/problem+json.

- **409**: report_unavailable: metadata is visible but results are unavailable. Includes state. Content types: application/problem+json.

- **429**: Rate limited; respect Retry-After. Content types: application/problem+json.

- **503**: temporarily_unavailable for handled database or service-state failure. Other unexpected server failures do not guarantee a Problem body. Content types: application/problem+json.

Successful response schema:

```json
{
  "$ref": "#/components/schemas/ExternalReportDetail"
}
```

Synthetic response shape (not a live result):

```json
{
  "report": {
    "id": "30000000-0000-4000-8000-000000000001",
    "title": "[SAMPLE] Public-service evidence snapshot",
    "state": "Completed",
    "requestedAtUtc": "2026-11-05T07:59:00Z",
    "generatedAtUtc": "2026-11-05T08:00:00Z",
    "expiresAtUtc": "2026-12-05T08:00:00Z",
    "snapshotSha256": "13118df3c9b79cc3dfe0293b9f23c9596e9430f3d14f58cdb51439a96f9c69b9",
    "failureCode": null
  },
  "results": {
    "generatedAtUtc": "2026-11-05T08:00:00Z",
    "asOfUtc": "2026-11-05T08:00:00Z",
    "fromUtc": "2026-11-04T08:00:00Z",
    "toUtc": "2026-11-05T08:00:00Z",
    "dateBasis": "publication time, with collected time only where publication time is unknown",
    "totalEvidenceCount": 8,
    "sampleSize": 8,
    "truncated": false,
    "sentiment": [
      {
        "key": "positive",
        "count": 2
      },
      {
        "key": "negative",
        "count": 1
      },
      {
        "key": "neutral",
        "count": 1
      },
      {
        "key": "not_analyzed",
        "count": 2
      },
      {
        "key": "uncertain",
        "count": 1
      },
      {
        "key": "failed",
        "count": 1
      }
    ],
    "countries": [
      {
        "countryIsoCode": "GB",
        "name": "United Kingdom",
        "evidenceCount": 2,
        "geographyRole": "subject",
        "provenance": [
          "Manual"
        ]
      },
      {
        "countryIsoCode": "US",
        "name": "United States",
        "evidenceCount": 1,
        "geographyRole": "subject",
        "provenance": [
          "Manual"
        ]
      },
      {
        "countryIsoCode": "NG",
        "name": "Nigeria",
        "evidenceCount": 1,
        "geographyRole": "subject",
        "provenance": [
          "Manual"
        ]
      }
    ],
    "unknownLocationCount": 4,
    "sources": [
      {
        "key": "Rss",
        "count": 8
      }
    ],
    "trends": [
      {
        "dateUtc": "2026-11-05",
        "evidenceCount": 8,
        "positive": 2,
        "negative": 1,
        "neutral": 1,
        "mixed": 0,
        "unclear": 0,
        "notAnalyzed": 2,
        "failed": 1,
        "uncertain": 1,
        "withheld": 0
      }
    ],
    "sourceCoverage": [
      {
        "provider": "Rss",
        "collectionVerified": true,
        "storedEvidenceCount": 8,
        "successfulRuns": 3,
        "failedRuns": 1,
        "lastSuccessfulCollectionAtUtc": "2026-11-05T07:45:00Z",
        "state": "sample_partially_stale",
        "storedContentCoverage": true
      }
    ],
    "evidenceReferences": [
      {
        "contentId": "20000000-0000-4000-8000-000000000001",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/1"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000002",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/2"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000003",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/3"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000004",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/4"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000005",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/5"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000006",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/6"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000007",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/7"
      },
      {
        "contentId": "20000000-0000-4000-8000-000000000008",
        "provider": "Rss",
        "externalId": null,
        "canonicalUrl": "https://example.com/spectra-sample/8"
      }
    ],
    "limitations": [
      "SYNTHETIC SAMPLE DATA: no live collection or sentiment is demonstrated.",
      "Collected evidence is not a representative public poll.",
      "Unknown country is retained; nationality or diaspora membership is never inferred."
    ],
    "evidenceFingerprint": "5c627018beb312d732ccbf735f5716270153c2567bb6746743517e14eb7fe383",
    "withheldLocationCount": 0
  },
  "schemaVersion": "spectra.ddm.aggregate-report/v1"
}
```

## Complete response schemas

### ExternalCollectionRun

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "provider": {
      "type": "string"
    },
    "state": {
      "type": "string"
    },
    "startedAtUtc": {
      "type": "string",
      "format": "date-time"
    },
    "completedAtUtc": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ]
    },
    "returnedOccurrences": {
      "type": "integer",
      "format": "int32"
    },
    "newRecords": {
      "type": "integer",
      "format": "int32"
    },
    "duplicates": {
      "type": "integer",
      "format": "int32"
    },
    "errors": {
      "type": "integer",
      "format": "int32"
    },
    "errorCode": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "id",
    "provider",
    "state",
    "startedAtUtc",
    "returnedOccurrences",
    "newRecords",
    "duplicates",
    "errors"
  ]
}
```

### ExternalMonitor

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string"
    },
    "objective": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "state": {
      "type": "string"
    },
    "sources": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "geographicScope": {
      "type": "string",
      "description": "Anywhere means Global/unrestricted. TargetLocationsOnly and AnywhereMentioningTargetLocations use saved targets."
    },
    "intendedCountries": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Distinct sorted country codes derived from available saved target references. Does not guarantee provider coverage."
    },
    "collectionIntervalSeconds": {
      "anyOf": [
        {
          "type": "integer",
          "format": "int32"
        },
        {
          "type": "null"
        }
      ]
    },
    "startsAtUtc": {
      "type": "string",
      "format": "date-time"
    },
    "endsAtUtc": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ]
    },
    "continuous": {
      "type": "boolean"
    },
    "lastSuccessfulCollectionAtUtc": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ]
    },
    "lastRun": {
      "anyOf": [
        {
          "$ref": "#/components/schemas/ExternalCollectionRun"
        },
        {
          "type": "null"
        }
      ]
    },
    "storedEvidenceCount": {
      "type": "integer",
      "format": "int32"
    },
    "stale": {
      "type": "boolean"
    },
    "geographyTargets": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalMonitorGeography"
      },
      "description": "Saved target IDs and labels, including country, region and city. Unavailable references retain ID with available=false and null metadata. ParentId links hierarchy, not necessarily another selected target."
    },
    "languages": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Saved language preferences; empty means no language restriction."
    },
    "geographyBasis": {
      "type": "string",
      "description": "Saved basis: subject, provider, source or legacy (default for old configurations). Provider basis corresponds to origin evidence; legacy follows the saved geographicScope."
    },
    "timeZone": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "description": "Saved IANA timezone preference; null for legacy configurations. startsAtUtc and endsAtUtc remain UTC instants."
    }
  },
  "required": [
    "id",
    "name",
    "state",
    "sources",
    "geographicScope",
    "intendedCountries",
    "startsAtUtc",
    "continuous",
    "storedEvidenceCount",
    "stale"
  ]
}
```

### ExternalMonitorGeography

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "countryIsoCode": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "type": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "adminLevel": {
      "anyOf": [
        {
          "type": "integer",
          "format": "int32"
        },
        {
          "type": "null"
        }
      ]
    },
    "parentId": {
      "anyOf": [
        {
          "type": "string",
          "format": "uuid"
        },
        {
          "type": "null"
        }
      ]
    },
    "available": {
      "type": "boolean"
    }
  },
  "required": [
    "id",
    "available"
  ]
}
```

### ExternalTopic

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string"
    },
    "objective": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "keywords": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "phrases": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "exclusions": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "sources": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "id",
    "name",
    "keywords",
    "phrases",
    "exclusions",
    "sources"
  ]
}
```

### ExternalSentiment

```json
{
  "type": "object",
  "properties": {
    "state": {
      "type": "string"
    },
    "label": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "analyzedAtUtc": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ]
    },
    "method": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "analyzerVersion": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "score": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ]
    },
    "uncertainty": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "confidenceMeaning": {
      "type": "string"
    }
  },
  "required": [
    "state",
    "confidenceMeaning"
  ]
}
```

### ExternalContentGeography

```json
{
  "type": "object",
  "properties": {
    "countryIsoCode": {
      "type": "string"
    },
    "countryName": {
      "type": "string"
    },
    "role": {
      "type": "string"
    },
    "provenance": {
      "type": "string"
    },
    "resolutionAdminLevel": {
      "type": "integer",
      "format": "int32"
    },
    "uncertainty": {
      "type": "string"
    },
    "confidence": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "countryIsoCode",
    "countryName",
    "role",
    "provenance",
    "resolutionAdminLevel",
    "uncertainty"
  ]
}
```

### ExternalContent

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "provider": {
      "type": "string"
    },
    "externalId": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "canonicalUrl": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "title": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "language": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "publishedAtUtc": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ]
    },
    "collectedAtUtc": {
      "type": "string",
      "format": "date-time"
    },
    "sentiment": {
      "$ref": "#/components/schemas/ExternalSentiment"
    },
    "geography": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalContentGeography"
      }
    },
    "monitorIds": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "uuid"
      }
    },
    "redistributionMode": {
      "type": "string"
    },
    "fullTextAvailable": {
      "type": "boolean"
    },
    "geographyState": {
      "type": "string"
    }
  },
  "required": [
    "id",
    "provider",
    "collectedAtUtc",
    "sentiment",
    "geography",
    "monitorIds",
    "redistributionMode",
    "fullTextAvailable",
    "geographyState"
  ]
}
```

### ExternalCountry

```json
{
  "type": "object",
  "properties": {
    "countryIsoCode": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "coverageState": {
      "type": "string"
    },
    "providerCountryFilter": {
      "type": "string"
    }
  },
  "required": [
    "countryIsoCode",
    "name",
    "coverageState",
    "providerCountryFilter"
  ]
}
```

### ExternalCount

```json
{
  "type": "object",
  "properties": {
    "key": {
      "type": "string"
    },
    "count": {
      "type": "integer",
      "format": "int32"
    }
  },
  "required": [
    "key",
    "count"
  ]
}
```

### ExternalCountryCount

```json
{
  "type": "object",
  "properties": {
    "countryIsoCode": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "evidenceCount": {
      "type": "integer",
      "format": "int32"
    },
    "geographyRole": {
      "type": "string"
    },
    "provenance": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "countryIsoCode",
    "name",
    "evidenceCount",
    "geographyRole",
    "provenance"
  ]
}
```

### ExternalTrend

```json
{
  "type": "object",
  "properties": {
    "dateUtc": {
      "type": "string"
    },
    "evidenceCount": {
      "type": "integer",
      "format": "int32"
    },
    "positive": {
      "type": "integer",
      "format": "int32"
    },
    "negative": {
      "type": "integer",
      "format": "int32"
    },
    "neutral": {
      "type": "integer",
      "format": "int32"
    },
    "mixed": {
      "type": "integer",
      "format": "int32"
    },
    "unclear": {
      "type": "integer",
      "format": "int32"
    },
    "notAnalyzed": {
      "type": "integer",
      "format": "int32"
    },
    "failed": {
      "type": "integer",
      "format": "int32"
    },
    "uncertain": {
      "type": "integer",
      "format": "int32"
    },
    "withheld": {
      "type": "integer",
      "format": "int32"
    }
  },
  "required": [
    "dateUtc",
    "evidenceCount",
    "positive",
    "negative",
    "neutral",
    "mixed",
    "unclear",
    "notAnalyzed",
    "failed",
    "uncertain",
    "withheld"
  ]
}
```

### ExternalSourceCoverage

```json
{
  "type": "object",
  "properties": {
    "provider": {
      "type": "string"
    },
    "collectionVerified": {
      "type": "boolean"
    },
    "storedEvidenceCount": {
      "type": "integer",
      "format": "int32"
    },
    "successfulRuns": {
      "type": "integer",
      "format": "int32"
    },
    "failedRuns": {
      "type": "integer",
      "format": "int32"
    },
    "lastSuccessfulCollectionAtUtc": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ]
    },
    "state": {
      "type": "string"
    },
    "storedContentCoverage": {
      "type": "boolean"
    }
  },
  "required": [
    "provider",
    "collectionVerified",
    "storedEvidenceCount",
    "successfulRuns",
    "failedRuns",
    "state",
    "storedContentCoverage"
  ]
}
```

### ExternalAnalytics

```json
{
  "type": "object",
  "properties": {
    "generatedAtUtc": {
      "type": "string",
      "format": "date-time"
    },
    "asOfUtc": {
      "type": "string",
      "format": "date-time"
    },
    "fromUtc": {
      "type": "string",
      "format": "date-time"
    },
    "toUtc": {
      "type": "string",
      "format": "date-time"
    },
    "dateBasis": {
      "type": "string"
    },
    "totalEvidenceCount": {
      "type": "integer",
      "format": "int32"
    },
    "sampleSize": {
      "type": "integer",
      "format": "int32"
    },
    "truncated": {
      "type": "boolean"
    },
    "sentiment": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalCount"
      }
    },
    "countries": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalCountryCount"
      }
    },
    "unknownLocationCount": {
      "type": "integer",
      "format": "int32"
    },
    "sources": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalCount"
      }
    },
    "trends": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalTrend"
      }
    },
    "sourceCoverage": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalSourceCoverage"
      }
    },
    "evidenceReferences": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalSourceReference"
      }
    },
    "limitations": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "evidenceFingerprint": {
      "type": "string"
    },
    "withheldLocationCount": {
      "type": "integer",
      "format": "int32"
    }
  },
  "required": [
    "generatedAtUtc",
    "asOfUtc",
    "fromUtc",
    "toUtc",
    "dateBasis",
    "totalEvidenceCount",
    "sampleSize",
    "truncated",
    "sentiment",
    "countries",
    "unknownLocationCount",
    "sources",
    "trends",
    "sourceCoverage",
    "evidenceReferences",
    "limitations",
    "evidenceFingerprint",
    "withheldLocationCount"
  ]
}
```

### ExternalSourceReference

```json
{
  "type": "object",
  "properties": {
    "contentId": {
      "type": "string",
      "format": "uuid"
    },
    "provider": {
      "type": "string"
    },
    "externalId": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "canonicalUrl": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "contentId",
    "provider"
  ]
}
```

### ExternalStatus

```json
{
  "type": "object",
  "properties": {
    "schemaVersion": {
      "type": "string"
    },
    "observedAtUtc": {
      "type": "string",
      "format": "date-time"
    },
    "activeMonitors": {
      "type": "integer",
      "format": "int32"
    },
    "maximumActiveMonitors": {
      "type": "integer",
      "format": "int32"
    },
    "storedEvidenceCount": {
      "type": "integer",
      "format": "int32"
    },
    "collectionState": {
      "type": "string"
    },
    "sources": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalSourceCoverage"
      }
    },
    "alerts": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "paidAnalysisTriggered": {
      "type": "boolean"
    }
  },
  "required": [
    "schemaVersion",
    "observedAtUtc",
    "activeMonitors",
    "maximumActiveMonitors",
    "storedEvidenceCount",
    "collectionState",
    "sources",
    "alerts",
    "paidAnalysisTriggered"
  ]
}
```

### ExternalReportSummary

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "title": {
      "type": "string"
    },
    "state": {
      "type": "string"
    },
    "requestedAtUtc": {
      "type": "string",
      "format": "date-time"
    },
    "generatedAtUtc": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time"
        },
        {
          "type": "null"
        }
      ]
    },
    "expiresAtUtc": {
      "type": "string",
      "format": "date-time"
    },
    "snapshotSha256": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "failureCode": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "id",
    "title",
    "state",
    "requestedAtUtc",
    "expiresAtUtc"
  ]
}
```

### ExternalReportDetail

```json
{
  "type": "object",
  "properties": {
    "report": {
      "$ref": "#/components/schemas/ExternalReportSummary"
    },
    "results": {
      "anyOf": [
        {
          "$ref": "#/components/schemas/ExternalAnalytics"
        },
        {
          "type": "null"
        }
      ]
    },
    "schemaVersion": {
      "type": "string"
    }
  },
  "required": [
    "report",
    "schemaVersion"
  ]
}
```

### ExternalMonitorPage

```json
{
  "type": "object",
  "required": [
    "items",
    "page",
    "pageSize",
    "totalCount",
    "hasMore",
    "asOfUtc",
    "requestId"
  ],
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalMonitor"
      }
    },
    "page": {
      "type": "integer",
      "minimum": 1
    },
    "pageSize": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "totalCount": {
      "type": "integer",
      "minimum": 0
    },
    "hasMore": {
      "type": "boolean"
    },
    "asOfUtc": {
      "type": "string",
      "format": "date-time"
    },
    "requestId": {
      "type": "string"
    }
  }
}
```

### ExternalTopicPage

```json
{
  "type": "object",
  "required": [
    "items",
    "page",
    "pageSize",
    "totalCount",
    "hasMore",
    "asOfUtc",
    "requestId"
  ],
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalTopic"
      }
    },
    "page": {
      "type": "integer",
      "minimum": 1
    },
    "pageSize": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "totalCount": {
      "type": "integer",
      "minimum": 0
    },
    "hasMore": {
      "type": "boolean"
    },
    "asOfUtc": {
      "type": "string",
      "format": "date-time"
    },
    "requestId": {
      "type": "string"
    }
  }
}
```

### ExternalContentPage

```json
{
  "type": "object",
  "required": [
    "items",
    "page",
    "pageSize",
    "totalCount",
    "hasMore",
    "asOfUtc",
    "requestId"
  ],
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalContent"
      }
    },
    "page": {
      "type": "integer",
      "minimum": 1
    },
    "pageSize": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "totalCount": {
      "type": "integer",
      "minimum": 0
    },
    "hasMore": {
      "type": "boolean"
    },
    "asOfUtc": {
      "type": "string",
      "format": "date-time"
    },
    "requestId": {
      "type": "string"
    }
  }
}
```

### ExternalReportSummaryPage

```json
{
  "type": "object",
  "required": [
    "items",
    "page",
    "pageSize",
    "totalCount",
    "hasMore",
    "asOfUtc",
    "requestId"
  ],
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ExternalReportSummary"
      }
    },
    "page": {
      "type": "integer",
      "minimum": 1
    },
    "pageSize": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "totalCount": {
      "type": "integer",
      "minimum": 0
    },
    "hasMore": {
      "type": "boolean"
    },
    "asOfUtc": {
      "type": "string",
      "format": "date-time"
    },
    "requestId": {
      "type": "string"
    }
  }
}
```

### Problem

```json
{
  "type": "object",
  "required": [
    "title",
    "status",
    "code",
    "requestId"
  ],
  "properties": {
    "type": {
      "type": "string"
    },
    "title": {
      "type": "string"
    },
    "status": {
      "type": "integer"
    },
    "code": {
      "type": "string",
      "description": "Known codes below; clients must tolerate future safe codes within the status class.",
      "examples": [
        "invalid_credentials",
        "insufficient_scope",
        "caller_tenant_forbidden",
        "read_only_api",
        "invalid_request",
        "access_denied",
        "resource_not_found",
        "rate_limit_exceeded",
        "report_unavailable",
        "temporarily_unavailable"
      ]
    },
    "requestId": {
      "type": "string"
    },
    "state": {
      "type": "string",
      "description": "Optional current report state on download conflict."
    }
  }
}
```

## Version

Production contract 1.1.0-rc.3. OpenAPI SHA-256 `b196cd2597c660fe692d8a227f6c1144f8032ba0d1b358d3d1d4ef5a70003d1f`.
