---
title: "Get SPF diagnostics"
method: GET
path: "/domains/{domainId}/spf"
tags: ["Spf"]
---

# Get SPF diagnostics

`GET /domains/{domainId}/spf`

## Request

- Base URL: `https://api.palisade.email`
- URL: `https://api.palisade.email/domains/{domainId}/spf`
- Auth: one of:
  - HTTP bearer
  - HTTP basic

## Path parameters

- `domainId` string, uuid, required

## Headers

- `Palisade-Version` '2025-04-30' | 'unstable', required

## Response `200`

Success

- SpfDiagnostics
  - `domain_id` string, uuid, required: Id of the domain the diagnostics are for.
  - `domain` string, required: The domain name whose live DNS was read, for example "example.com".
  - `record` string, nullable, required: The SPF TXT record live DNS serves for the domain, or null when none is published. When more than one v=spf1 record exists they are joined with newlines here; see records for them individually.
  - `records` string[], required: Every v=spf1 TXT record found at the domain. More than one entry is itself an error — receivers treat it as a permanent failure.
  - `ttl` number, nullable, required: TTL of the SPF TXT record in seconds, when it could be read.
  - `status` string, required: Overall SPF health: "pass", "warn" or "fail".
  - `lookup_count` integer, required: DNS lookups the record costs receivers: every include, a, mx, ptr, exists and redirect term, counted recursively through every include and redirect. Terms receivers never reach are not charged: anything to the right of a matching `all`, and a `redirect=` in a record that also carries an `all`, which RFC 7208 section 6.1 says must be ignored.
  - `max_lookups` integer, required: The limit SPF imposes before receivers return a permanent error: 10.
  - `lookups_remaining` integer, required: How many more lookups the record could add before breaking. 0 when at or over the limit.
  - `lookup_breakdown` SpfLookup[], required: Every lookup in the chain, nested entries following their parent.
    - `mechanism` string, required: The SPF term that costs the lookup: "include", "a", "mx", "ptr", "exists" or "redirect". "expanded-domain" marks a macro expansion, which costs none.
    - `host` string, nullable, required: The operand the term queries, as published. Usually a hostname. A macro-bearing `exists:` carries the template here, and the substituted name follows as an `expanded-domain` entry when every macro in it could be resolved. A macro-bearing `a:`, `mx:` or `ptr:` carries the template here too, but is never expanded, so no `expanded-domain` entry follows it; only `include:` and `redirect=` operands are put through the macro resolver, and one whose macros all resolved reports the substituted name it queried here rather than the published template, under the note "macro-expanded" when that name publishes exactly one SPF record, and with `error` when it does not.
    - `depth` integer, required: Nesting depth: 0 for mechanisms in the domain's own record, 1 for those inside an include it references, and so on.
    - `lookups` integer, required: DNS lookups this entry accounts for: itself plus everything nested under it. The expensive includes are the ones with the highest number here.
    - `error` boolean, required: True when this lookup failed to resolve to a usable record.
    - `note` string, nullable, required: Extra context, such as "loop", "unresolved", or one of "macro-expanded" / "macro-unexpanded". Any term whose operand carries a macro -- `exists:`, `include:` or `redirect=` -- carries one of those two, and costs one lookup either way, except that an expanded `include:` or `redirect=` carries no macro note once its target disappoints: "unresolved" when the lookup could not be completed, "loop" when it refers back, "no-txt" alongside `error` when the target returned no TXT records at all, and no note at all -- just `error` -- when the target returned TXT records but not a single SPF record among them.
  - `problems` SpfProblem[], required: Problems found, errors before warnings. Empty when SPF is healthy.
    - `code` string, required: Stable identifier for the problem, safe to branch on — for example "spf-max-10-lookups", "spf-multiple-records", "spf-lookup-failed" or "spf-no-record".
    - `severity` 'error' | 'warning', required: "error" when receivers fail SPF (or get no protection from it) because of this; "warning" for a risk that does not break evaluation.
    - `host` string, nullable, required: The hostname the problem is about, when it concerns a specific lookup.
    - `message` string, required: What is wrong and what it means for mail, in plain language.
  - `hosted_spf` object, required: State of Palisade-hosted SPF for the domain. When managed, the domain publishes a single Palisade include and Palisade keeps the flattened chain under the 10-lookup limit.
    - `is_managed` boolean, required: True when Palisade hosts and flattens the SPF record for this domain.
    - `status` 'unknown' | 'syncing' | 'ready' | 'error', nullable, required: Live status of the hosted SPF chain, when managed.
    - `target_spf` string, nullable, required: The record Palisade flattens and publishes, when hosted SPF is configured.
    - `error_reason` 'setup_timeout' | 'drift' | 'certificate_invalid' | 'record_conflict' | 'delegation_missing' | 'publish_failed' | 'dkim_key_error' | 'check_failed', nullable, required: Why the hosted SPF chain is in error, when it is.
    - `last_flattened_at` string, nullable, required: When Palisade last flattened and published the hosted chain, ISO 8601.
  - `next_step` string, required: The single action Palisade suggests taking next.

---

[API](https://skmtc.dev/palisade/apis/palisade-api.md) · [All operations](https://skmtc.dev/palisade/apis/palisade-api/llms.txt) · [OpenAPI document](https://skmtc.dev/palisade/apis/palisade-api/revisions/37b4ae5a7766?raw)
