Zedmos
Zedmos CTI
Developer Guide

Developer Guide

How to consume the Zedmos CTI feed API from your firewall, SIEM, or EDR. Every endpoint is HTTPS + bearer token; every response carries audit headers.

1. Authentication

Every /v1/feeds/* and /v1/lookup/* endpoint requires a bearer token. Tokens are tenant-scoped and have one or more "kinds" (ti, security, waf) attached at issue time.

curl -H "Authorization: Bearer $TOKEN" \
  https://cti.zedmos.net/v1/feeds/security/phishing/domains.txt | head

A few notes:

1A. Self-service portal

Anyone can request a Zedmos CTI account through the public portal. There is no email-verification step right now — every new account is queued for admin review and contacted manually once approved.

  1. Sign up at /register with email + password (≥12 chars, mixed case, digit). Response: 202 pending_approval.
  2. Wait for admin approval. An operator flips enabled=true via PATCH /admin/v1/users/:id. You will be notified out-of-band.
  3. Sign in at /login → receive a 7-day session JWT (stored in localStorage).
  4. Issue API tokens at /dashboard. Each token is shown once (copy immediately). Scope by kinds (ti, security, waf) and tiers (verified, trusted, community). Max 10 active tokens per user.
  5. Call the feed API with the token (see Section 1 above).

Security: bcrypt cost 12, zod strict-mode input validation, NoSQL-injection guard, 5/hour signup rate-limit, 10-attempt account lockout (15 min), 2-phase login (wrong password and pending account return distinguishable errors only to the password holder).

2. Pulling feeds

All feeds follow the URL convention:

/v1/feeds/<kind>/<category>/<format>

Common combinations:

URLFormat
/v1/feeds/security/phishing/domains.txtplain domain list
/v1/feeds/ti/botnet_cc/ips.txtplain IP / CIDR list
/v1/feeds/security/malware_virus/suricata.rulesSuricata 7 DNS-block rules with priority + threat metadata
/v1/feeds/security/ransomware/sigma.ymlSigma rule (MITRE ATT&CK tagged)
/v1/feeds/ti/file_sha256_malware/yara.yarYARA hash rule
/v1/feeds/security/phishing/stix.jsonSTIX 2.1 bundle
/v1/feeds/security/phishing/unbound.rpzRPZ DNS sinkhole zone

The full list of categories is available at GET /v1/public/sources (no auth required).

3. IOC scoring

Each IOC carries four 0-100 scores. The most useful in production is composite: a weighted aggregate of the other three.

ScoreMeasures
confidenceSource quality × consensus × enrichment
threatSeverity × category weight × tier multiplier
popularityDistinct sources × sightings × age window
compositeconfidence × 0.45 + threat × 0.35 + popularity × 0.20

Filtering by score (?min_score=N)

Vendor-native feed exports (fortinet.txt, paloalto.txt, checkpoint.csv, etc.) accept a min_score query parameter (0-100). The value is matched against the per-IOC confidence score recorded in the metadata sidecar.

Plain domains.txt / ips.txt serve the pre-built tier snapshot file as-is — they ignore min_score. Use a vendor format if you need score-gated output, or call /v1/lookup/... for per-IOC composite scores.

# Full feed (default)
curl -H "Authorization: Bearer $TOKEN" \
  https://cti.zedmos.net/v1/feeds/security/phishing/domains.txt

# Confidence ≥ 70 only (verified + corroborated)
curl -H "Authorization: Bearer $TOKEN" \
  "https://cti.zedmos.net/v1/feeds/security/phishing/fortinet.txt?min_score=70"

# Response carries X-Tihub-Min-Score, X-Tihub-Row-Count, X-Tihub-Row-Limit headers

Filtering by tier (?tiers=...)

The default ship list is verified ∪ trusted. To opt in to single-source community indicators, or to restrict to a single tier, pass ?tiers=:

TierDefinition
verified≥ 2 distinct upstream sources OR active enrichment confirmed (GreyNoise / AbuseIPDB / VirusTotal) OR honeypot-observed OR operator-approved
trustedsingle-source from a strict T1 feed: USOM (TR-CERT), CERT.pl, Spamhaus DROP, abuse.ch Feodo low-FP
communitysingle-source from T2–T5 feeds — useful wide-net but not independently corroborated; opt-in only
# Only verified (highest precision)
curl -H "Authorization: Bearer $TOKEN" \
  "https://cti.zedmos.net/v1/feeds/security/phishing/fortinet.txt?tiers=verified"

# Verified + trusted + community (widest net)
curl -H "Authorization: Bearer $TOKEN" \
  "https://cti.zedmos.net/v1/feeds/security/phishing/fortinet.txt?tiers=verified,trusted,community"

Row caps + ?limit=N

Each firewall vendor has a documented EDL / block-list import limit. Zedmos applies a per-vendor default so the importer never silently truncates a too-large feed. Override with ?limit=N up to 500,000.

Vendor formatDefault capSource
fortinet.txt128,000FortiOS 7.x EDL ceiling
paloalto.txt (IPs)150,000PAN-OS EDL — IP list
paloalto-domain.txt (Domains)50,000PAN-OS EDL — Domain list
checkpoint.csv500,000Quantum Custom IOC recommended
sophos.csv100,000Central CSV upload limit
cisco-umbrella.txt250,000Umbrella block-list cap
meraki.txt50,000MX custom URL list
juniper-srx.txt100,000dynamic-address-set practical max
others (opnsense, mikrotik, pihole, unbound, suricata)unlimited / format-specific

When the cap kicks in, indicators with metadata-sidecar enrichment (malware family, threat actor, MITRE) ship first.

Caching contract — 202 "warming"

Heavy aggregate endpoints (/v1/public/stats, /v1/public/transparency, /v1/public/verification, /v1/public/cti/categories, /v1/public/mitre/coverage, /v1/public/dashboard) run against an 11M-row catalog. Right after a hub restart the in-process cache is empty and the first request would otherwise hang for 10-30 s.

To prevent timeouts, those endpoints return 202 Accepted with body {"status":"warming","retry_after_s":5} until the background prime job populates the cache. Subsequent calls return 200 OK in < 100 ms. Treat 202 as transient — retry after the suggested interval. Response header X-Tihub-Cache distinguishes warming / process-hit / stale-revalidating.

Per-IOC lookup

curl -H "Authorization: Bearer $TOKEN" \
  https://cti.zedmos.net/v1/lookup/domain/example.com

Response includes tier, sources, categories, the four scores, effective MITRE techniques, and a recommendation string.

4. MITRE ATT&CK mapping

Each Zedmos category maps to a curated list of ATT&CK tactics + techniques (Enterprise v15). The mapping surfaces in:

Coverage matrix (public)

curl https://cti.zedmos.net/v1/public/mitre/coverage | jq '.techniques[0:5]'

The same data rendered as a grid lives at /mitre-coverage.html — no authentication required.

5. Vendor-native exports

The same IOCs, packaged for direct paste into the firewall console. All endpoints accept ?min_score=N.

Fortinet FortiGate

config system external-resource
  edit "zedmos-phishing"
    set type domain
    set resource "https://cti.zedmos.net/v1/feeds/security/phishing/fortinet.txt?min_score=70"
  next
end

Palo Alto Networks (External Dynamic List)

PAN-OS uses two separate EDL types — register each as its own object:

# Objects → External Dynamic Lists → Add → Type: IP List
# Source: https://cti.zedmos.net/v1/feeds/security/phishing/paloalto.txt
# Repeat every: Hour

# Objects → External Dynamic Lists → Add → Type: Domain List
# Source: https://cti.zedmos.net/v1/feeds/security/phishing/paloalto-domain.txt
# Repeat every: Hour

Check Point Quantum

ioc_feeds add \
  --feed_name Zedmos_Phishing \
  --transport_type web_url \
  --resource https://cti.zedmos.net/v1/feeds/security/phishing/checkpoint.csv \
  --feed_action prevent \
  --feed_format CSV

Sophos · Cisco Umbrella · Meraki · Juniper SRX · MikroTik · Pi-hole · OPNsense · Unbound RPZ

Config snippets for all 16 supported integrations: /integrations.

6. API reference

Public endpoints (no auth)

EndpointReturns
GET /v1/public/healthhealth probe (always 200, never throttled)
GET /v1/public/statscatalog summary, tier mix, feed health
GET /v1/public/sourcesper-source feed catalogue
GET /v1/public/threats-by-countrytop-25 source countries (cached 5 min)
GET /v1/public/methodologyFP filter + tier rules (static)
GET /v1/public/transparencylive FP filter counts + signing key info
GET /v1/public/verificationper-tier counts, cross-validation result
GET /v1/public/dashboardaggregate stats for landing page
GET /v1/public/quality-reporttier mix, per-feed contribution
GET /v1/public/cti/categories33 category cards with IOC counts (5-min cached)
GET /v1/public/mitre/coverageMITRE ATT&CK coverage matrix (5-min cached)
GET /v1/public/sightings/toptop observed indicators
GET /v1/public/keys/signed25519 public key (JSON)
GET /v1/public/keys/sign.pemsame key (PEM)
GET /v1/public/lookup/<value>public IOC lookup (auto-detects type)
POST /v1/public/lookupbulk lookup {values:[...]} (≤ 200)
GET /v1/public/ct/recentrecent Certificate Transparency hits
GET /v1/integrations/vendorsvendor catalog (drives /integrations page)

Self-service portal (session JWT or public)

EndpointReturns
POST /v1/portal/registercreate account {email,password,name?}202 pending_approval
POST /v1/portal/loginsession JWT {email,password} → 200 + {token,user}; 403 if pending; 401 on bad creds
POST /v1/portal/logoutaudit-log logout intent (client drops JWT)
GET /v1/portal/mecurrent user profile (requires Bearer session JWT)
GET /v1/portal/tokenslist your API tokens with use_count, last_used_at, last_used_ip
POST /v1/portal/tokenscreate token {name,kinds[],tiers[]} → raw secret once; max 10 active per user
POST /v1/portal/tokens/:id/revokerevoke (token stays in audit log)
DELETE /v1/portal/tokens/:idhard-delete the token

TAXII 2.1 / STIX (bearer)

EndpointReturns
GET /taxii2/discovery
GET /taxii2/api1/API root
GET /taxii2/api1/collections/collection list (one per kind+category+type)
GET /taxii2/api1/collections/{id}/objects/STIX bundle (?limit, ?added_after)
GET /taxii2/api1/collections/{id}/manifest/collection manifest
GET /v1/stix/infoSTIX version + signer info

Sightings + closed loop (bearer)

EndpointReturns
POST /v1/sightings/bulksubmit firewall hit aggregates {sightings:[{value,type,count,…}]}
POST /v1/sightings/backchannelcti2 v2 backchannel events
GET /v1/meinspect your API token (scope, quota, expiry)
GET /v1/feeds/indexlist every shipped (kind, category, file, URL, etag, line_count)

Bearer-token endpoints

EndpointReturns
GET /v1/feeds/<kind>/<cat>/domains.txtplain domain list
GET /v1/feeds/<kind>/<cat>/ips.txtplain IP / CIDR list
GET /v1/feeds/<kind>/<cat>/suricata.rulesSuricata rules (priority + threat metadata)
GET /v1/feeds/<kind>/<cat>/sigma.ymlSigma rule (MITRE tagged)
GET /v1/feeds/<kind>/<cat>/yara.yarYARA rule
GET /v1/feeds/<kind>/<cat>/stix.jsonSTIX 2.1 bundle
GET /v1/feeds/<kind>/<cat>/fortinet.txtFortinet External Block List
GET /v1/feeds/<kind>/<cat>/paloalto.txtPAN External Dynamic List (IP)
GET /v1/feeds/<kind>/<cat>/paloalto-domain.txtPAN External Dynamic List (Domain)
GET /v1/feeds/<kind>/<cat>/checkpoint.csvCheck Point Custom IOC Feed
GET /v1/feeds/<kind>/<cat>/sophos.csvSophos Active Threat Response
GET /v1/feeds/<kind>/<cat>/cisco-umbrella.txtCisco Umbrella block list
GET /v1/feeds/<kind>/<cat>/meraki.txtMeraki MX URL list
GET /v1/feeds/<kind>/<cat>/juniper-srx.txtJunos SRX Dynamic Address Set
GET /v1/feeds/<kind>/<cat>/mikrotik.rscMikroTik /import script
GET /v1/feeds/<kind>/<cat>/pihole.txtPi-hole adlist
GET /v1/feeds/<kind>/<cat>/opnsense.txtOPNsense URL-table
GET /v1/feeds/<kind>/<cat>/unbound.rpzUnbound RPZ zone
GET /v1/lookup/<type>/<value>per-IOC lookup with scores + MITRE

Response headers

Every feed response sets:


Need a token, a custom export format, or have a question? Contact your Zedmos representative.