{"name":"palisade","version":"1.0.0","description":"Monitor and manage email authentication (SPF, DKIM, DMARC, MTA-STS, BIMI) for your domains.","icon":"https://www.palisade.email/icon.png","url":"https://api.palisade.email/mcp","transport":"streamable-http","instructions":"Palisade monitors and manages email authentication (SPF, DKIM, DMARC, MTA-STS, BIMI) for your domains. Every tool that reads or changes Palisade's own records operates only on the organization you are signed in to. The two exceptions are audit_domain and validate_spf_include, which take a domain name rather than a domain id and read public DNS for any domain, including ones nobody has added to Palisade - use them to size up a domain before adding it, or to cost an SPF include before making a change.\n\nTo check the status of a domain (is DMARC set up, is SPF passing, is MTA-STS on): for a domain in the account, get_domain with its url returns Palisade's status per record, the monitoring status and 14-day pass rates, and get_dmarc_summary reports what receivers say; for a domain that is not in the account, or when you do not know, audit_domain reads its live DNS and scores it. Neither changes anything.\n\nNo tool in this server writes a DNS record at an external provider. It tells you exactly which records to publish; you apply them yourself at whatever DNS provider hosts the domain (Cloudflare, Route 53, your registrar, etc.) using your own DNS tool, connector, or that provider's API. The records are portable and provider-agnostic, so use whichever DNS integration you have available.\n\nThat is a limit of this server, not of Palisade. Palisade can put the records in place for the user two ways, and both live in the Palisade app rather than here, so the move is to point the user at the app rather than to tell them it cannot be done:\n- Into their own DNS, written for them. The user authorises the connection in their DNS provider's own window, and Palisade then creates the records they approve directly through that provider's or registrar's API. Nothing is copied by hand. No credentials are shared with Palisade, the access is scoped to email-authentication records, and the user can revoke it. Automatic configuration covers most mainstream providers and registrars; where a provider is not covered, fall back to returning the records for the user to publish.\n- Hosted by Palisade. Palisade serves the records from its own DNS instead, which is what enable_hosted_dmarc and enable_mta_sts switch on — and those two you can call from this server.\n\nSo if a user asks why they are copying records by hand, the answer is that they do not have to.\n\nTypical workflow to onboard a domain:\n1. create_domain — add the domain you already own to Palisade. It creates the Palisade record, not the domain name itself.\n2. get_dns_records — fetch the exact DNS records to publish (host, type, value, TTL, and required action per record) plus dns_provider, the DNS host detected from the domain's live NS records. Then apply each record at that provider using your own DNS tooling — Palisade will not create them for you. Prefer a DNS tool or connector matching dns_provider.id when you have one; when it is null the provider is unknown, so check dns_provider.nameservers rather than guessing. Each record's action tells you what to do: create a new record, replace an existing one, or delete the record.\n3. verify_domain — after you have published the records at your DNS provider, trigger verification (check: all | spf | dkim), then poll get_domain (or get_dns_records) until monitoring_status becomes \"setup\" and all_verified is true. Palisade checks live DNS regardless of which provider you used. If you can receive HTTP requests, create_webhook_endpoint is the alternative to polling: subscribe to domain.updated and Palisade will POST the domain whenever its monitoring state changes.\n4. list_tasks — Palisade opens remediation tasks for failing senders and record issues; get_task with expand: [\"instructions\"] can include provider-specific fix instructions. get_domain_plan answers the next question, which list_tasks cannot: which stage of the DMARC journey the domain is on and what Palisade works next, including steps it has not opened tasks for yet.\n5. complete_task — after verify_domain confirms the fix is live, close the task that tracked it. A task left open keeps the domain looking unremediated to everyone reading Palisade, and it is the only signal that the work is done. Close it only once verification actually passed: completing a task re-checks nothing, so a task closed over a domain that is still broken is reopened by the next monitoring sweep. When a task does not apply at all — a sending source the organization does not own and will not authenticate — dismiss_task closes it instead, and its ignore_spf / ignore_dkim flags stop the next sweep opening the same task again.\n\nIf you do not have a DNS tool or connector for the domain's provider, return the records from get_dns_records to the user so they can publish them, then continue with verify_domain once they confirm.\n\nReports: get_dmarc_summary reports what a domain's DMARC aggregate reports say — volume, pass rates, the change against the previous window, and the domain's policy. list_dmarc_senders breaks the same window down by sending source, including sources nobody has confirmed and sources Palisade cannot identify, which is what makes it the right tool for finding unauthorised or misconfigured senders. Neither returns raw report XML.\n\nWhen a probe surfaces a problem, do not design the fix yourself. Palisade has usually already diagnosed it and worked out the remediation: a failing sender in list_dmarc_senders carries open_task_id, and get_dmarc_summary returns open_tasks for the domain along with policy_readiness. Call get_task with expand: [\"instructions\"] for that id and follow the instructions it returns. Improvise only when there is no task covering the problem, and say so when you do — an SPF or DKIM change made against Palisade's own guidance is how a working domain stops delivering mail. Once you have published the change and verify_domain confirms it, call complete_task on that same task id to close the loop.\n\nBilling: get_subscription reports the plan, subscription status, and current-month usage. If the organization has no active subscription, start_checkout returns a Stripe Checkout URL — open it in a browser and hand it to a human unless you can complete Stripe Checkout. start_billing_portal returns a Stripe billing-portal URL for payment-method changes and cancellation.\n\nWebhooks: list_webhook_events shows the event types available; create_webhook_endpoint registers an https URL Palisade POSTs them to. Deliveries are signed — the response to create_webhook_endpoint contains the signing secret and is the only time it is returned, so surface it to the user immediately. Prefer webhooks over repeated polling for anything long-running.\n\nSPF: get_spf reads a domain's live SPF record and reports its DNS lookup count against the 10-lookup limit, the chain of includes with what each costs, and the problems found (an over-limit record, duplicate v=spf1 records, broken includes). It is read-only. A record over the limit is what Palisade-hosted SPF flattening fixes: set target_spf with update_domain and Palisade flattens the record and serves it from a single include.\n\nUse get_mta_sts / enable_mta_sts / disable_mta_sts to manage Palisade-hosted MTA-STS, and get_account for organization details.\n\nChanging what is already there: update_domain changes a domain's settings — its group, whether it is parked, its DMARC policy options, and the SPF record Palisade publishes for it — without re-adding it. Turning Palisade-hosted SPF or BIMI off is not exposed here; point the user at the app for that. Groups are managed with list_groups, create_group, update_group and delete_group; deleting a group leaves its domains monitored but ungrouped. Any change that alters a managed record still has to reach DNS, so follow it with get_dns_records and verify_domain.\n\nTools annotated as destructive (delete_domain, delete_group, disable_mta_sts, delete_webhook_endpoint) discard state that calling the tool again will not bring back. Confirm with the user before calling one.","authentication":{"required":true,"schemes":["oauth2"]},"capabilities":{"tools":true,"prompts":true,"resources":false},"serverCard":"https://api.palisade.email/.well-known/mcp/server-card.json","tools":[{"name":"get_account","title":"Get account","description":"Get the Palisade organization (account) you are signed in to: name, contact details, and settings.","inputSchema":{"type":"object","properties":{},"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Organization id."},"name":{"type":["string","null"],"description":"Organization name."},"email":{"type":["string","null"],"description":"Primary contact email."},"phone_number":{"type":["string","null"],"description":"Primary contact phone number."},"website":{"type":["string","null"],"description":"Organization website."},"logo_url":{"type":["string","null"],"description":"URL of the organization logo."},"is_active":{"type":["boolean","null"],"description":"False when the organization has been disabled."},"support_provider":{"type":["string","null"],"description":"Who provides support for this organization: \"palisade\" or a reseller, such as \"sherweb\"."},"settings":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Organization-level settings."},"created_at":{"type":["string","null"],"description":"When the organization was created, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the organization last changed, ISO 8601."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"audit_domain","title":"Check any domain's email authentication","description":"Check the status of any domain's email authentication from live DNS in one call: MX, SPF, DKIM, DMARC, BIMI, MTA-STS and TLS-RPT, each with a verdict and the records behind it, plus a score out of 100. Use it to answer \"is DMARC set up on example.com\", \"how bad is this domain\" or \"what does this vendor publish\". Unlike the rest of this server it takes a domain name rather than a domain id and works on domains that are not in the caller's Palisade account, so it also sizes up a domain before adding it. It reads public DNS only and writes nothing. For a domain already in the account prefer get_domain (by id or by url) and get_dns_records, which additionally know what Palisade is publishing and what it expects; and read list_tasks before acting on anything found here, because Palisade usually has worked-out instructions for a problem it already knows about. Results are cached for ten minutes per domain, so re-calling it will not show a DNS change published seconds ago.","inputSchema":{"type":"object","properties":{"domain":{"type":"string","minLength":3,"description":"The domain to audit, e.g. example.com. Does not need to be in your account."}},"required":["domain"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"status":{"type":["string","null"],"description":"\"success\", or \"error\" when the audit could not run because the domain's nameservers did not resolve or belong to a domain-parking advertiser. On \"error\" only `summary` is present, with the reason in `summary.errors`."},"summary":{"anyOf":[{"type":"object","properties":{"hostname":{"type":["string","null"],"description":"The domain audited, as requested."},"email":{"type":["string","null"],"description":"Always empty: audit_domain takes no email address."},"parked":{"type":["boolean","null"],"description":"True when Palisade has the domain on record as parked. A parked domain should send no mail, so only SPF and DMARC count toward its score."},"score":{"type":["number","null"],"description":"Overall score out of 100. Starts at 100 and subtracts a penalty per failing dimension, so it is a headline rather than a diagnosis - read the per-dimension verdicts to know what is wrong."},"mx":{"type":["string","null"],"description":"The MX verdict, the same value as `mx.status`."},"spf":{"type":["string","null"],"description":"The SPF verdict, the same value as `spf.status`."},"dkim":{"type":["string","null"],"description":"The DKIM verdict, the same value as `dkim.status`."},"dmarc":{"type":["string","null"],"description":"The DMARC verdict, the same value as `dmarc.status`."},"bimi":{"type":["string","null"],"description":"The BIMI verdict, the same value as `bimi.status`."},"mtaSts":{"type":["string","null"],"description":"The MTA-STS verdict, the same value as `mtaSts.status`."},"tlsRpt":{"type":["string","null"],"description":"The TLS-RPT verdict, the same value as `tlsRpt.status`."},"errors":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Why the audit could not run: \"ns-error\" when the nameservers did not resolve, \"ns-for-advertising-domain\" when they belong to a domain-parking advertiser. Empty on success."},"warnings":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Always empty. Findings are reported on each dimension."},"assessments":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Always empty. Findings are reported on each dimension."}},"additionalProperties":true},{"type":"null"}],"description":"The headline verdict per dimension. Start here, then read the matching object below for why."},"nameservers":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"The NS records the audit resolved: the domain's own, or its parent's when it has none."},"mx":{"anyOf":[{"type":"object","properties":{"score":{"type":["number","null"],"description":"Points this dimension takes off the overall score: 0 at full marks, negative otherwise. Only MX, SPF, DKIM, DMARC and BIMI are counted, and only SPF and DMARC when the domain is parked."},"status":{"type":["string","null"],"description":"Verdict: \"pass\", \"warn\" or \"fail\"; \"in progress\" for a DMARC policy short of full reject or MTA-STS in testing mode; \"info\" when optional MTA-STS or TLS-RPT is not set up. DKIM reports \"error\" when the catalogue of sending services Palisade probes was unavailable, so no selector was probed and the result says nothing about the domain."},"assessments":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Problems that break or undermine the mechanism, as stable codes such as \"spf-no-record\". A host can follow a colon, as in \"spf-lookup-failed:spf.example.com\". MTA-STS and TLS-RPT write sentences instead of codes."},"warnings":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Risks worth fixing that do not break the mechanism, such as \"spf-low-ttl\". MTA-STS and TLS-RPT write sentences instead of codes."},"mxPresent":{"anyOf":[{"type":"object","properties":{"status":{"type":["string","null"],"description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":["string","null"],"description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":["number","null"],"description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"additionalProperties":true},{"type":"null"}],"description":"The MX check as a state: \"mx-present\" or \"mx-missing\"."},"mxHost":{"type":["string","null"],"description":"The name the MX records were read from: the audited domain, or for a subdomain with no nameservers of its own, the parent whose nameservers answered."},"mxValue":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"exchange":{"type":["string","null"],"description":"The mail server hostname."},"priority":{"type":["number","null"],"description":"MX preference. Lower values are tried first."}},"additionalProperties":true},{"type":"null"}]}]}},{"type":"null"}],"description":"The MX records found, most preferred first."},"resolutionFailed":{"type":["boolean","null"],"description":"True when the MX lookup itself failed, so an empty `mxValue` is not evidence of absence."}},"additionalProperties":true},{"type":"null"}],"description":"The MX check."},"spf":{"anyOf":[{"type":"object","properties":{"score":{"type":["number","null"],"description":"Points this dimension takes off the overall score: 0 at full marks, negative otherwise. Only MX, SPF, DKIM, DMARC and BIMI are counted, and only SPF and DMARC when the domain is parked."},"status":{"type":["string","null"],"description":"Verdict: \"pass\", \"warn\" or \"fail\"; \"in progress\" for a DMARC policy short of full reject or MTA-STS in testing mode; \"info\" when optional MTA-STS or TLS-RPT is not set up. DKIM reports \"error\" when the catalogue of sending services Palisade probes was unavailable, so no selector was probed and the result says nothing about the domain."},"assessments":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Problems that break or undermine the mechanism, as stable codes such as \"spf-no-record\". A host can follow a colon, as in \"spf-lookup-failed:spf.example.com\". MTA-STS and TLS-RPT write sentences instead of codes."},"warnings":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Risks worth fixing that do not break the mechanism, such as \"spf-low-ttl\". MTA-STS and TLS-RPT write sentences instead of codes."},"spfSoftFail":{"anyOf":[{"type":"object","properties":{"status":{"type":["string","null"],"description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":["string","null"],"description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":["number","null"],"description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"additionalProperties":true},{"type":"null"}],"description":"The SPF record as a state, such as \"spf-soft-fail\" (ends in `~all`), \"spf-closed\" (ends in `-all`), \"spf-missing\", \"spf-multiple-records\" or \"spf-invalid\"."},"spfValue":{"type":["string","null"],"description":"The SPF record evaluated: the redirect target's when the domain uses `redirect=` and that target publishes exactly one record, otherwise the domain's own; every record joined by newlines, longest first, when more than one is published. Empty only when no record was found, a deprecated record type is published, or the domain publishes a single record carrying `redirect=` alongside an `all` mechanism - a record that loops, over-runs the lookup limit or has an unresolved include is still reported here."},"lookupCount":{"type":["number","null"],"description":"DNS lookups counted from every term RFC 7208 section 4.6.4 charges one for -- `include`, `a`, `mx`, `ptr`, `exists` and `redirect` -- whether or not each one resolved, and whatever qualifier it carries. Over 10 is an SPF permerror. Each term counts once and names the host it queries, includes and redirects are followed recursively wherever they appear, and `ip4`, `ip6` and `all` cost nothing. Two terms are deliberately not charged, because receivers never query them: anything standing 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."},"lookupStructure":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"type":{"type":["string","null"],"description":"The term that costs the lookup: \"include\", \"redirect\", \"a\", \"mx\", \"ptr\" or \"exists\". \"expanded-domain\" is the name an `exists:` macro expands to, which costs no lookup."},"value":{"type":["string","null"],"description":"The operand the term queries, as published rather than resolved -- with one exception: an `include:` or `redirect=` whose macros all resolved reports the substituted name it queried, under the note \"macro-expanded\" when that name publishes exactly one SPF record, and with `error` when it does not. A bare `a`, `mx` or `ptr` names the domain whose record it stands in, since that is what it queries: the audited domain at the top level, and the included or redirect target inside the subtree belonging to it. Two cases are not a hostname: an `exists:` carrying a macro is the template, and a nested `expanded-domain` node carries the substituted name only when `%{i}`, `%{ir}`, `%{v}` and `%{d}` are the only macros it uses -- any other macro form leaves the term unexpanded, carrying the note \"macro-unexpanded\" and no nested node; and an `a:`, `mx:` or `ptr:` carrying a macro, such as `a:%{d}`, is the template unexpanded, since only `include:` and `redirect=` operands are put through the macro resolver. A qualifier does not change the operand: `~a:mail.example.com` and `-mx:relay.example.net` name the host each points at, exactly as the unqualified forms do."},"nested":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{},{"type":"null"}]}]}},{"type":"null"}],"description":"The lookups this one expands into, each an SpfLookupNode."},"note":{"type":["string","null"],"description":"\"loop\" for an include or redirect that refers back to a record already being evaluated, \"unresolved\" when the lookup could not be completed, \"macro-expanded\" for a term whose operand carried a macro that was substituted, and \"macro-unexpanded\" for one carrying a macro that cannot be resolved without a live sender. `exists:`, `include:` and `redirect=` can all carry either, and cost one lookup whichever it is -- 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. An expanded `exists:` puts the substituted name in a nested `expanded-domain` node; an expanded `include:` or `redirect=` queries the substituted name and reports it as the value."},"error":{"type":["boolean","null"],"description":"True when the include or redirect target publishes no single SPF record, or loops. A lookup that could not be completed carries the note \"unresolved\" and no `error` instead, and a target that returned no TXT records at all carries `error` together with the note \"no-txt\"."}},"additionalProperties":true},{"type":"null"}]}]}},{"type":"null"}],"description":"The DNS lookups the record costs receivers, as a tree: an include or redirect lists what it expands into under `nested`."},"resolutionFailed":{"type":["boolean","null"],"description":"True when the SPF lookup itself failed, for the domain or its `redirect=` target, so what `errors` reports missing is not evidence of absence."},"services":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{},{"type":"null"}]}]}},{"type":"null"}],"description":"Always empty."},"deprecated":{"type":["boolean","null"],"description":"True when the domain still publishes the obsolete SPF record type (DNS type 99), which receivers ignore."},"ip4Ranges":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"The `ip4:` ranges the record authorizes itself, not counting its includes. Empty when the record could not be evaluated."},"ip6Ranges":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"The `ip6:` ranges the record authorizes itself, not counting its includes. Empty when the record could not be evaluated."},"ttl":{"type":["number","null"],"description":"TTL of the SPF record in seconds, from the domain's authoritative nameservers. 0 or null when it could not be read."},"recommendations":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Improvements worth making beyond `errors` and `warnings`, as codes such as \"dmarc-policy-quarantine\", which urges moving on to reject. Usually empty."}},"additionalProperties":true},{"type":"null"}],"description":"The SPF check, including what the record costs in DNS lookups."},"dkim":{"anyOf":[{"type":"object","properties":{"score":{"type":["number","null"],"description":"Points this dimension takes off the overall score: 0 at full marks, negative otherwise. Only MX, SPF, DKIM, DMARC and BIMI are counted, and only SPF and DMARC when the domain is parked."},"status":{"type":["string","null"],"description":"Verdict: \"pass\", \"warn\" or \"fail\"; \"in progress\" for a DMARC policy short of full reject or MTA-STS in testing mode; \"info\" when optional MTA-STS or TLS-RPT is not set up. DKIM reports \"error\" when the catalogue of sending services Palisade probes was unavailable, so no selector was probed and the result says nothing about the domain."},"assessments":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Problems that break or undermine the mechanism, as stable codes such as \"spf-no-record\". A host can follow a colon, as in \"spf-lookup-failed:spf.example.com\". MTA-STS and TLS-RPT write sentences instead of codes."},"warnings":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Risks worth fixing that do not break the mechanism, such as \"spf-low-ttl\". MTA-STS and TLS-RPT write sentences instead of codes."},"hostname":{"type":["string","null"],"description":"The domain whose DKIM selectors were checked."},"dkimValues":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"record":{"type":["string","null"],"description":"The TXT record published at the selector."},"selector":{"type":["string","null"],"description":"The selector, published at `<selector>._domainkey.<domain>`."},"source":{"type":["string","null"],"description":"The sending service in Palisade's catalogue that uses this selector."},"k":{"type":["string","null"],"description":"The key type from `k=`, such as \"rsa\". Empty when the tag is absent, which means rsa."},"p":{"type":["string","null"],"description":"The public key from `p=`. Empty when the key is revoked (`p=` with no value) or the tag is missing."},"score":{"type":["number","null"],"description":"Points this record takes off the DKIM score. 0 when it is sound."},"assessments":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Problems that break or undermine the mechanism, as stable codes such as \"spf-no-record\". A host can follow a colon, as in \"spf-lookup-failed:spf.example.com\". MTA-STS and TLS-RPT write sentences instead of codes."},"warnings":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Risks worth fixing that do not break the mechanism, such as \"spf-low-ttl\". MTA-STS and TLS-RPT write sentences instead of codes."}},"additionalProperties":true},{"type":"null"}]}]}},{"type":"null"}],"description":"Every DKIM record found. Palisade probes only the selectors its catalogue of sending services uses, so a key under any other selector is not found: an empty list is not proof the domain has no DKIM."}},"additionalProperties":true},{"type":"null"}],"description":"The DKIM check, over the selectors Palisade knows to probe."},"dmarc":{"anyOf":[{"type":"object","properties":{"hostname":{"type":["string","null"],"description":"The domain the DMARC record was read from: the audited domain, or the parent it was found on when the audited domain publishes none."},"score":{"type":["number","null"],"description":"Points this dimension takes off the overall score: 0 at full marks, negative otherwise. Only MX, SPF, DKIM, DMARC and BIMI are counted, and only SPF and DMARC when the domain is parked."},"status":{"type":["string","null"],"description":"Verdict: \"pass\", \"warn\" or \"fail\"; \"in progress\" for a DMARC policy short of full reject or MTA-STS in testing mode; \"info\" when optional MTA-STS or TLS-RPT is not set up. DKIM reports \"error\" when the catalogue of sending services Palisade probes was unavailable, so no selector was probed and the result says nothing about the domain."},"assessments":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Problems that break or undermine the mechanism, as stable codes such as \"spf-no-record\". A host can follow a colon, as in \"spf-lookup-failed:spf.example.com\". MTA-STS and TLS-RPT write sentences instead of codes."},"warnings":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Risks worth fixing that do not break the mechanism, such as \"spf-low-ttl\". MTA-STS and TLS-RPT write sentences instead of codes."},"setupState":{"type":["string","null"],"description":"Whether the record already sends aggregate reports to Palisade: \"SETUP\" if so, otherwise \"NONE\"."},"dmarcClosed":{"anyOf":[{"type":"object","properties":{"status":{"type":["string","null"],"description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":["string","null"],"description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":["number","null"],"description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"additionalProperties":true},{"type":"null"}],"description":"The policy half as a state: \"dmarc-policy-none\", \"dmarc-policy-quarantine-*\" or \"dmarc-policy-reject-*\" by `pct=` level (low, medium, high or full), \"dmarc-missing\" or \"dmarc-invalid\"."},"dmarcMonitored":{"anyOf":[{"type":"object","properties":{"status":{"type":["string","null"],"description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":["string","null"],"description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":["number","null"],"description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"additionalProperties":true},{"type":"null"}],"description":"The reporting half as a state: \"dmarc-monitoring-managed\" (aggregate reports reach Palisade), \"dmarc-monitoring-external\" (they go elsewhere), \"dmarc-monitoring-missing\" or \"dmarc-monitoring-invalid\"."},"records":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Every `v=DMARC1` record found. More than one is an error: receivers then ignore DMARC."},"dmarcValue":{"type":["string","null"],"description":"The record evaluated. Null when none was found or more than one is published."},"ttl":{"type":["number","null"],"description":"TTL of the DMARC record in seconds, from the authoritative nameservers. Null when it could not be read."},"tags":{"anyOf":[{"type":"object","properties":{"policy":{"type":["string","null"],"description":"The `p=` policy, lowercased: \"none\", \"quarantine\" or \"reject\"."},"pct":{"type":["number","null"],"description":"The `pct=` percentage of failing mail the policy applies to. Null when absent, which means 100."},"fo":{"type":["string","null"],"description":"The `fo=` failure-reporting option: \"0\", \"1\", \"d\" or \"s\"."},"ruaMailto":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Where aggregate reports go: the `rua=` addresses, without `mailto:`."},"rufMailto":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Where failure reports go: the `ruf=` addresses, without `mailto:`."},"adkim":{"type":["string","null"],"description":"The `adkim=` DKIM alignment mode: \"r\" relaxed or \"s\" strict. Null when absent, which means relaxed."},"aspf":{"type":["string","null"],"description":"The `aspf=` SPF alignment mode: \"r\" relaxed or \"s\" strict. Null when absent, which means relaxed."},"sp":{"type":["string","null"],"description":"The `sp=` policy for subdomains. Null when absent, in which case subdomains take `p=`."}},"additionalProperties":true},{"type":"null"}],"description":"The tags the record sets, parsed. A tag it omits is null, or an empty list."},"monitoring":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Reporting findings, kept apart from `errors`: codes such as \"dmarc-missing-rua-tag\", which means no aggregate reports are requested."},"recommendations":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Improvements worth making beyond `errors` and `warnings`, as codes such as \"dmarc-policy-quarantine\", which urges moving on to reject. Usually empty."},"viaCname":{"type":["boolean","null"],"description":"True when the record is served through a CNAME rather than published directly."},"isPalisadeCname":{"type":["boolean","null"],"description":"True when that CNAME points at Palisade, which hosts the record."},"resolutionFailed":{"type":["boolean","null"],"description":"True when the DMARC lookup itself failed, so what `errors` reports missing is not evidence of absence."}},"additionalProperties":true},{"type":"null"}],"description":"The DMARC check, which scores the policy and the reporting separately."},"bimi":{"anyOf":[{"type":"object","properties":{"score":{"type":["number","null"],"description":"Points this dimension takes off the overall score: 0 at full marks, negative otherwise. Only MX, SPF, DKIM, DMARC and BIMI are counted, and only SPF and DMARC when the domain is parked."},"status":{"type":["string","null"],"description":"Verdict: \"pass\", \"warn\" or \"fail\"; \"in progress\" for a DMARC policy short of full reject or MTA-STS in testing mode; \"info\" when optional MTA-STS or TLS-RPT is not set up. DKIM reports \"error\" when the catalogue of sending services Palisade probes was unavailable, so no selector was probed and the result says nothing about the domain."},"assessments":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Problems that break or undermine the mechanism, as stable codes such as \"spf-no-record\". A host can follow a colon, as in \"spf-lookup-failed:spf.example.com\". MTA-STS and TLS-RPT write sentences instead of codes."},"warnings":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Risks worth fixing that do not break the mechanism, such as \"spf-low-ttl\". MTA-STS and TLS-RPT write sentences instead of codes."},"bimiCertified":{"anyOf":[{"type":"object","properties":{"status":{"type":["string","null"],"description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":["string","null"],"description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":["number","null"],"description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"additionalProperties":true},{"type":"null"}],"description":"The BIMI record as a state: \"bimi-certified\" (a logo and a certificate), \"bimi-uncertified\" (a logo without one), \"bimi-missing\", \"bimi-invalid\", \"bimi-requirements-not-met\" (DMARC is not at enforcement), or once the files are checked \"bimi-logo-invalid\", \"bimi-vmc-invalid\" or \"bimi-vmc-expired\"."},"bimiValue":{"type":["string","null"],"description":"The record at `default._bimi.<domain>`. Null when none, or more than one, is published."},"location":{"type":["string","null"],"description":"The logo URL from `l=`. Null when the tag is absent."},"assertion":{"type":["string","null"],"description":"The `a=` value as published, kept verbatim: an `https://` `.pem` URL where the tag carries one, otherwise the literal \"self\", an empty string, or whatever unusable value was set. A usable URL here is not a verdict on the certificate, because it is not cleared when the record later fails its checks - read `bimiCertified` for that. Null when the tag is absent or was not reached."},"recommendations":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Improvements worth making beyond `errors` and `warnings`, as codes such as \"dmarc-policy-quarantine\", which urges moving on to reject. Usually empty."},"vmcExpiryDate":{"type":["string","null"],"description":"When the certificate expires, taken from its `validTo`. Present when validation read a `validTo`, or when a cached expiry from an earlier read was reused; absent when the certificate could not be fetched or parsed, even though it was reached."},"viaCname":{"type":["boolean","null"],"description":"True when the record is served through a CNAME rather than published directly."},"isPalisadeCname":{"type":["boolean","null"],"description":"True when that CNAME points at Palisade, which hosts the record."},"deepValidated":{"type":["boolean","null"],"description":"True when this audit attempted deep validation: fetching the logo, and the certificate when `a=` names one. It is set before those fetches run, so it does not mean either succeeded - read `logoValidation` and `vmcValidation` for that. Absent when the record failed before that step, and when a VMC result checked within the last 24 hours was reused instead of re-reading it."},"logoValidation":{"anyOf":[{"type":"object","properties":{"fetched":{"type":["boolean","null"],"description":"True when the logo could be downloaded."},"valid":{"type":["boolean","null"],"description":"True when it passed Palisade's checks for a BIMI SVG."},"error":{"type":["string","null"],"description":"Why the logo failed, when it did."}},"additionalProperties":true},{"type":"null"}],"description":"What fetching and checking the logo at `location` found. Present when `deepValidated` is true."},"vmcValidation":{"anyOf":[{"type":"object","properties":{"fetched":{"type":["boolean","null"],"description":"True when the certificate file could be downloaded."},"valid":{"type":["boolean","null"],"description":"True when every certificate check passed. Warnings such as a logo mismatch do not count against it."},"certificateCount":{"type":["number","null"],"description":"PEM certificates in the file."},"chainComplete":{"type":["boolean","null"],"description":"True when the file carries every certificate from the leaf up to a self-signed root. Null until checked."},"chainTrusted":{"type":["boolean","null"],"description":"True when the chain, exactly as published, ends in a root Palisade trusts for BIMI. Null until checked."},"trustAnchor":{"type":["string","null"],"description":"The trusted root the chain ends in. Null when it is not trusted."},"markTypePresent":{"type":["boolean","null"],"description":"True when the certificate carries the BIMI mark-type attribute a VMC or CMC must have. Null until checked."},"isVmc":{"type":["boolean","null"],"description":"True when it is a mark certificate, one that embeds a logo; false when it is another kind of certificate; null when it could not be read."},"logoMatchesCertificate":{"type":["boolean","null"],"description":"True when the logo at `location` is byte for byte the one embedded in the certificate. Null when they were not compared."},"domainCovered":{"type":["boolean","null"],"description":"True when the certificate names the audited domain. Null until checked."},"issuer":{"type":["string","null"],"description":"Common name of the certificate's issuer."},"subjectCommonName":{"type":["string","null"],"description":"Common name the certificate was issued to."},"validFrom":{"type":["string","null"],"description":"Start of the validity period, ISO 8601."},"validTo":{"type":["string","null"],"description":"End of the validity period, ISO 8601."},"sanDomains":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"The domains the certificate covers, from its subject alternative names."},"error":{"type":["string","null"],"description":"Why the certificate failed, when it did."}},"additionalProperties":true},{"type":"null"}],"description":"What fetching and checking the certificate at `assertion` found. Present when `deepValidated` is true and `a=` names a certificate."}},"additionalProperties":true},{"type":"null"}],"description":"The BIMI check, including the logo and certificate the record links to."},"mtaSts":{"anyOf":[{"type":"object","properties":{"score":{"type":["number","null"],"description":"Points this dimension takes off the overall score: 0 at full marks, negative otherwise. Only MX, SPF, DKIM, DMARC and BIMI are counted, and only SPF and DMARC when the domain is parked."},"status":{"type":["string","null"],"description":"Verdict: \"pass\", \"warn\" or \"fail\"; \"in progress\" for a DMARC policy short of full reject or MTA-STS in testing mode; \"info\" when optional MTA-STS or TLS-RPT is not set up. DKIM reports \"error\" when the catalogue of sending services Palisade probes was unavailable, so no selector was probed and the result says nothing about the domain."},"assessments":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Problems that break or undermine the mechanism, as stable codes such as \"spf-no-record\". A host can follow a colon, as in \"spf-lookup-failed:spf.example.com\". MTA-STS and TLS-RPT write sentences instead of codes."},"warnings":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Risks worth fixing that do not break the mechanism, such as \"spf-low-ttl\". MTA-STS and TLS-RPT write sentences instead of codes."},"mtaStsStatus":{"anyOf":[{"type":"object","properties":{"status":{"type":["string","null"],"description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":["string","null"],"description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":["number","null"],"description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"additionalProperties":true},{"type":"null"}],"description":"The MTA-STS setup as a state: \"mta-sts-enforce\", \"mta-sts-testing\", \"mta-sts-none-mode\", \"mta-sts-missing\", \"mta-sts-dns-only\" (a TXT record but no reachable policy), \"mta-sts-mx-mismatch\" (the policy misses an MX host) or \"mta-sts-invalid\"."},"txtValue":{"type":["string","null"],"description":"The record at `_mta-sts.<domain>`. Null when none, or more than one, is published."},"policyFile":{"type":["string","null"],"description":"The policy served at `https://mta-sts.<domain>/.well-known/mta-sts.txt`, verbatim. Null when it could not be fetched."},"mode":{"type":["string","null"],"description":"The policy `mode`: \"enforce\", \"testing\" or \"none\". Null when there is no policy to read."},"mxEntries":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"The `mx:` patterns the policy covers."},"maxAge":{"type":["number","null"],"description":"The policy `max_age` in seconds. Null when there is no readable value."},"policyId":{"type":["string","null"],"description":"The `id=` of the TXT record. Senders fetch the policy again when it changes."}},"additionalProperties":true},{"type":"null"}],"description":"The MTA-STS check. It does not count toward the overall score."},"tlsRpt":{"anyOf":[{"type":"object","properties":{"score":{"type":["number","null"],"description":"Points this dimension takes off the overall score: 0 at full marks, negative otherwise. Only MX, SPF, DKIM, DMARC and BIMI are counted, and only SPF and DMARC when the domain is parked."},"status":{"type":["string","null"],"description":"Verdict: \"pass\", \"warn\" or \"fail\"; \"in progress\" for a DMARC policy short of full reject or MTA-STS in testing mode; \"info\" when optional MTA-STS or TLS-RPT is not set up. DKIM reports \"error\" when the catalogue of sending services Palisade probes was unavailable, so no selector was probed and the result says nothing about the domain."},"assessments":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Problems that break or undermine the mechanism, as stable codes such as \"spf-no-record\". A host can follow a colon, as in \"spf-lookup-failed:spf.example.com\". MTA-STS and TLS-RPT write sentences instead of codes."},"warnings":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Risks worth fixing that do not break the mechanism, such as \"spf-low-ttl\". MTA-STS and TLS-RPT write sentences instead of codes."},"tlsRptStatus":{"anyOf":[{"type":"object","properties":{"status":{"type":["string","null"],"description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":["string","null"],"description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":["number","null"],"description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"additionalProperties":true},{"type":"null"}],"description":"The TLS-RPT record as a state: \"tls-rpt-present\", \"tls-rpt-missing\" or \"tls-rpt-invalid\"."},"txtValue":{"type":["string","null"],"description":"The record at `_smtp._tls.<domain>`. Null when none, or more than one, is published."},"reportingEndpoints":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}]}},{"type":"null"}],"description":"Where TLS failure reports go: the valid `rua=` destinations, each `mailto:` or `https://`."}},"additionalProperties":true},{"type":"null"}],"description":"The TLS-RPT check. It does not count toward the overall score."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"validate_spf_include","title":"Validate an SPF include","description":"Check whether a domain can be used as an SPF `include:` and how many DNS lookups adding it would cost. Use it before adding a sender to an SPF record: SPF fails permanently above 10 lookups, and this is how to tell in advance whether the change fits, by adding the count it returns to the lookup_count get_spf reports for the domain being changed. A domain that publishes no SPF record, or more than one, cannot be included at all and comes back valid false with the reason. Reads live DNS and changes nothing. When the answer is that the record will not fit, Palisade-hosted SPF flattening is the way out: set target_spf with update_domain rather than dropping senders.","inputSchema":{"type":"object","properties":{"domain":{"type":"string","minLength":3,"description":"The include target to test, e.g. _spf.google.com."}},"required":["domain"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"valid":{"type":["boolean","null"],"description":"True when the domain publishes exactly one SPF record and so can be used as an include."},"lookup_count":{"type":["number","null"],"description":"DNS lookups adding this include would cost, counting the include itself. Add it to the lookup_count get_spf reports for the domain being changed; SPF fails permanently above 10."},"error":{"anyOf":[{"anyOf":[{"type":"string"},{"type":"object","properties":{},"additionalProperties":true}]},{"type":"null"}],"description":"Why the domain cannot be used as an include, when valid is false. When the call itself fails, this is the error object every tool returns instead: status, code and message."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"list_domains","title":"List domains","description":"List the domains in your Palisade account with their monitoring status, deliverability score, DMARC policy, and managed DNS record statuses. Supports filtering, sorting, and pagination.","inputSchema":{"type":"object","properties":{"url":{"type":"string","description":"Filter by domain name (substring match)."},"group":{"type":"string","format":"uuid","description":"Filter by group id."},"ungrouped":{"type":"boolean","description":"Filter to domains without a group (true) or with a group (false)."},"dmarc_policies":{"type":"array","items":{"type":"string","enum":["none","quarantine","reject"]},"description":"Filter by published DMARC policy: \"none\", \"quarantine\" or \"reject\"."},"monitoring_statuses":{"type":"array","items":{"type":"string","enum":["drifted","none","pending","setup","failed"]},"description":"Filter by monitoring status: \"none\", \"pending\", \"setup\", \"drifted\" or \"failed\"."},"is_parked":{"type":"boolean","description":"Filter to parked domains (true) or sending domains (false)."},"score":{"type":"object","properties":{"gt":{"type":"number","minimum":0,"maximum":100},"gte":{"$ref":"#/properties/score/properties/gt"},"lt":{"$ref":"#/properties/score/properties/gt"},"lte":{"$ref":"#/properties/score/properties/gt"},"eq":{"$ref":"#/properties/score/properties/gt"}},"additionalProperties":false,"description":"Filter by email security score, as a comparison object — for example {\"lt\": 70} for domains scoring under 70. Supports eq, gt, gte, lt and lte."},"sort":{"type":"string","enum":["created_at","url","monitoring_status","dmarc_policy","score","last_14_days_volume","last_14_days_dmarc_pass_pct","last_14_days_spf_pass_pct","last_14_days_dkim_pass_pct","open_tasks_count","severity","status","group"],"description":"Field to sort by, for example \"score\", \"url\", \"created_at\", \"open_tasks_count\", \"severity\" (most urgent open task first, breaking ties on the count), \"status\" (severity ordering, but every configured domain ahead of every domain still in setup) or \"last_14_days_volume\"."},"sort_direction":{"type":"string","enum":["desc","asc"],"description":"Sort direction: \"asc\" or \"desc\"."},"per_page":{"type":"number","minimum":1,"maximum":50,"default":20,"description":"Domains per page, 1 to 50. Defaults to 20."},"page":{"type":"number","minimum":1,"default":1,"description":"Page to return, 1-based. Defaults to 1."},"expand":{"type":"array","items":{"type":"string","enum":["group","tasks"]},"description":"Optional expansions: \"group\" for the domain's group, \"tasks\" for its open remediation tasks."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Domain id. Pass it to get_domain, get_dns_records, verify_domain and enable_mta_sts."},"url":{"type":["string","null"],"description":"The domain name itself, for example \"example.com\"."},"dmarc_policy":{"type":["string","null"],"description":"Published DMARC policy: \"none\", \"quarantine\" or \"reject\"."},"monitoring_status":{"type":["string","null"],"description":"Onboarding state: \"none\" (nothing published), \"pending\" or \"setup\" (Palisade is receiving DMARC reports), \"drifted\" (a record changed under us) or \"failed\"."},"monitoring_failure_reason":{"type":["string","null"],"description":"Why monitoring failed, when monitoring_status is \"failed\". Null otherwise."},"score":{"type":["number","null"],"description":"Email security score, 0-100."},"score_band":{"type":["string","null"],"description":"Score bucket: \"critical\", \"bad\", \"good\" or \"great\"."},"open_tasks_count":{"type":["number","null"],"description":"Open remediation tasks for this domain. Use list_tasks to read them."},"highest_open_task_priority":{"anyOf":[{"anyOf":[{"not":{}},{"type":["number","null"]}],"description":"Priority of the most urgent open task, on a task's own inverted scale: 0 critical, 1 high, 2 medium, 3 low. Null when nothing is open. Sort domains on \"severity\" to order by it."},{"type":"null"}],"description":"Priority of the most urgent open task, on a task's own inverted scale: 0 critical, 1 high, 2 medium, 3 low. Null when nothing is open. Sort domains on \"severity\" to order by it."},"managed_dns_status":{"anyOf":[{"type":"object","properties":{"dmarc":{"type":["string","null"],"description":"Live status of the DMARC record."},"spf":{"type":["string","null"],"description":"Live status of the SPF record."},"dkim":{"type":["string","null"],"description":"Live status of the DKIM record."},"bimi":{"type":["string","null"],"description":"Live status of the BIMI record."},"mta_sts":{"type":["string","null"],"description":"Live status of the MTA-STS record."}},"additionalProperties":true},{"type":"null"}],"description":"Per-record status of the DNS Palisade manages: \"ready\" when the record is live and correct, \"syncing\" while it waits on DNS, \"error\" when it broke or drifted, and \"unknown\" when the record is not managed. These are not the statuses get_dns_records reports per record, which are \"pending\", \"verifying\", \"verified\" and \"drifted\"."},"is_parked":{"type":["boolean","null"],"description":"True when the domain is not meant to send mail."},"is_dmarc_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the DMARC record, so its policy can change without a DNS edit."},"is_spf_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the SPF record."},"is_dkim_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the DKIM records."},"is_bimi_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the BIMI record."},"is_mta_sts_managed":{"type":["boolean","null"],"description":"True when Palisade hosts MTA-STS for the domain."},"dmarc_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"DMARC policy settings: policy, sp, pct, strict_spf, strict_dkim, additional_emails."},"spf_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"SPF settings: enabled, force_flattening, mechanism_descriptions."},"bimi_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"BIMI settings: enabled, logo, certificate."},"dns_conflicts":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]}],"description":"A conflict, with the offending records, the clause they violate and how to fix it."}},{"type":"null"}],"description":"Contradictory records found in the live zone, such as two DMARC TXT records. Empty in the normal case; anything here breaks mail before Palisade can help."},"group_id":{"type":["string","null"],"description":"Id of the group this domain belongs to, or null when ungrouped."},"registrar":{"type":["string","null"],"description":"Registrar of record. This is not necessarily where DNS is hosted — get_dns_records reports that."},"created_at":{"type":["string","null"],"description":"When the domain was added, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the domain last changed, ISO 8601."}},"additionalProperties":true}},{"type":"null"}],"description":"The domains on this page."},"page_info":{"anyOf":[{"type":"object","properties":{"count":{"type":["number","null"],"description":"Total number of records matching the filter, across all pages — not the size of this page."},"page":{"type":["number","null"],"description":"The page returned, 1-based."},"per_page":{"type":["number","null"],"description":"Page size used for this response."}},"additionalProperties":true},{"type":"null"}],"description":"Pagination for the list. Request the next page when count exceeds page * per_page."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"get_domain","title":"Get domain status","description":"Check the status of a domain in your Palisade account, by id or by name: the managed DNS status of each record (SPF, DKIM, DMARC, BIMI, MTA-STS), monitoring status, DMARC policy, score, and 14-day pass rates. This is the tool for \"what is the DMARC status of example.com\" when the domain is in the account; pass url to look it up by name without calling list_domains first. The response carries the domain id every other domain tool takes. For a domain that is not in the account, use audit_domain, which reads its live DNS instead.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The domain id. Pass this or url."},"url":{"type":"string","minLength":3,"description":"The domain name, e.g. example.com. Pass this or id."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Domain id. Pass it to get_domain, get_dns_records, verify_domain and enable_mta_sts."},"url":{"type":["string","null"],"description":"The domain name itself, for example \"example.com\"."},"dmarc_policy":{"type":["string","null"],"description":"Published DMARC policy: \"none\", \"quarantine\" or \"reject\"."},"monitoring_status":{"type":["string","null"],"description":"Onboarding state: \"none\" (nothing published), \"pending\" or \"setup\" (Palisade is receiving DMARC reports), \"drifted\" (a record changed under us) or \"failed\"."},"monitoring_failure_reason":{"type":["string","null"],"description":"Why monitoring failed, when monitoring_status is \"failed\". Null otherwise."},"score":{"type":["number","null"],"description":"Email security score, 0-100."},"score_band":{"type":["string","null"],"description":"Score bucket: \"critical\", \"bad\", \"good\" or \"great\"."},"open_tasks_count":{"type":["number","null"],"description":"Open remediation tasks for this domain. Use list_tasks to read them."},"highest_open_task_priority":{"anyOf":[{"anyOf":[{"not":{}},{"type":["number","null"]}],"description":"Priority of the most urgent open task, on a task's own inverted scale: 0 critical, 1 high, 2 medium, 3 low. Null when nothing is open. Sort domains on \"severity\" to order by it."},{"type":"null"}],"description":"Priority of the most urgent open task, on a task's own inverted scale: 0 critical, 1 high, 2 medium, 3 low. Null when nothing is open. Sort domains on \"severity\" to order by it."},"managed_dns_status":{"anyOf":[{"type":"object","properties":{"dmarc":{"type":["string","null"],"description":"Live status of the DMARC record."},"spf":{"type":["string","null"],"description":"Live status of the SPF record."},"dkim":{"type":["string","null"],"description":"Live status of the DKIM record."},"bimi":{"type":["string","null"],"description":"Live status of the BIMI record."},"mta_sts":{"type":["string","null"],"description":"Live status of the MTA-STS record."}},"additionalProperties":true},{"type":"null"}],"description":"Per-record status of the DNS Palisade manages: \"ready\" when the record is live and correct, \"syncing\" while it waits on DNS, \"error\" when it broke or drifted, and \"unknown\" when the record is not managed. These are not the statuses get_dns_records reports per record, which are \"pending\", \"verifying\", \"verified\" and \"drifted\"."},"is_parked":{"type":["boolean","null"],"description":"True when the domain is not meant to send mail."},"is_dmarc_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the DMARC record, so its policy can change without a DNS edit."},"is_spf_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the SPF record."},"is_dkim_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the DKIM records."},"is_bimi_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the BIMI record."},"is_mta_sts_managed":{"type":["boolean","null"],"description":"True when Palisade hosts MTA-STS for the domain."},"dmarc_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"DMARC policy settings: policy, sp, pct, strict_spf, strict_dkim, additional_emails."},"spf_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"SPF settings: enabled, force_flattening, mechanism_descriptions."},"bimi_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"BIMI settings: enabled, logo, certificate."},"dns_conflicts":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]}],"description":"A conflict, with the offending records, the clause they violate and how to fix it."}},{"type":"null"}],"description":"Contradictory records found in the live zone, such as two DMARC TXT records. Empty in the normal case; anything here breaks mail before Palisade can help."},"group_id":{"type":["string","null"],"description":"Id of the group this domain belongs to, or null when ungrouped."},"registrar":{"type":["string","null"],"description":"Registrar of record. This is not necessarily where DNS is hosted — get_dns_records reports that."},"created_at":{"type":["string","null"],"description":"When the domain was added, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the domain last changed, ISO 8601."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"create_domain","title":"Create domain","description":"Add a domain you already own to your Palisade account, to start email authentication monitoring. This does not register a domain name and does not touch the domain itself — it creates the Palisade record for it. After adding, call get_dns_records for the exact records to publish at your DNS provider, then call verify_domain.","inputSchema":{"type":"object","properties":{"url":{"type":"string","description":"The domain to add, for example \"example.com\". A registrable domain, not a hostname or URL."},"group_id":{"type":"string","format":"uuid","description":"Id of the group to file the domain under. Use list_groups or create_group to get one."}},"required":["url"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Domain id. Pass it to get_domain, get_dns_records, verify_domain and enable_mta_sts."},"url":{"type":["string","null"],"description":"The domain name itself, for example \"example.com\"."},"dmarc_policy":{"type":["string","null"],"description":"Published DMARC policy: \"none\", \"quarantine\" or \"reject\"."},"monitoring_status":{"type":["string","null"],"description":"Onboarding state: \"none\" (nothing published), \"pending\" or \"setup\" (Palisade is receiving DMARC reports), \"drifted\" (a record changed under us) or \"failed\"."},"monitoring_failure_reason":{"type":["string","null"],"description":"Why monitoring failed, when monitoring_status is \"failed\". Null otherwise."},"score":{"type":["number","null"],"description":"Email security score, 0-100."},"score_band":{"type":["string","null"],"description":"Score bucket: \"critical\", \"bad\", \"good\" or \"great\"."},"open_tasks_count":{"type":["number","null"],"description":"Open remediation tasks for this domain. Use list_tasks to read them."},"highest_open_task_priority":{"anyOf":[{"anyOf":[{"not":{}},{"type":["number","null"]}],"description":"Priority of the most urgent open task, on a task's own inverted scale: 0 critical, 1 high, 2 medium, 3 low. Null when nothing is open. Sort domains on \"severity\" to order by it."},{"type":"null"}],"description":"Priority of the most urgent open task, on a task's own inverted scale: 0 critical, 1 high, 2 medium, 3 low. Null when nothing is open. Sort domains on \"severity\" to order by it."},"managed_dns_status":{"anyOf":[{"type":"object","properties":{"dmarc":{"type":["string","null"],"description":"Live status of the DMARC record."},"spf":{"type":["string","null"],"description":"Live status of the SPF record."},"dkim":{"type":["string","null"],"description":"Live status of the DKIM record."},"bimi":{"type":["string","null"],"description":"Live status of the BIMI record."},"mta_sts":{"type":["string","null"],"description":"Live status of the MTA-STS record."}},"additionalProperties":true},{"type":"null"}],"description":"Per-record status of the DNS Palisade manages: \"ready\" when the record is live and correct, \"syncing\" while it waits on DNS, \"error\" when it broke or drifted, and \"unknown\" when the record is not managed. These are not the statuses get_dns_records reports per record, which are \"pending\", \"verifying\", \"verified\" and \"drifted\"."},"is_parked":{"type":["boolean","null"],"description":"True when the domain is not meant to send mail."},"is_dmarc_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the DMARC record, so its policy can change without a DNS edit."},"is_spf_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the SPF record."},"is_dkim_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the DKIM records."},"is_bimi_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the BIMI record."},"is_mta_sts_managed":{"type":["boolean","null"],"description":"True when Palisade hosts MTA-STS for the domain."},"dmarc_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"DMARC policy settings: policy, sp, pct, strict_spf, strict_dkim, additional_emails."},"spf_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"SPF settings: enabled, force_flattening, mechanism_descriptions."},"bimi_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"BIMI settings: enabled, logo, certificate."},"dns_conflicts":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]}],"description":"A conflict, with the offending records, the clause they violate and how to fix it."}},{"type":"null"}],"description":"Contradictory records found in the live zone, such as two DMARC TXT records. Empty in the normal case; anything here breaks mail before Palisade can help."},"group_id":{"type":["string","null"],"description":"Id of the group this domain belongs to, or null when ungrouped."},"registrar":{"type":["string","null"],"description":"Registrar of record. This is not necessarily where DNS is hosted — get_dns_records reports that."},"created_at":{"type":["string","null"],"description":"When the domain was added, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the domain last changed, ISO 8601."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"update_domain","title":"Update domain","description":"Change the settings of a domain already in your Palisade account: which group it belongs to, whether it is parked, its DMARC policy options, and the SPF record Palisade publishes for it. Pass the id plus only the fields you want to change; anything you omit is left alone, and nested option objects are merged rather than replaced. Unrecognised keys are ignored rather than rejected, so read the domain this returns and confirm the fields you meant to change actually changed before reporting success. Changing a managed record here updates what Palisade publishes but does not make it live at your DNS provider — follow with get_dns_records for the new values and verify_domain. To take a domain out of its group pass group_id: null. To remove the domain entirely use delete_domain instead. Turning Palisade-hosted SPF or BIMI off is not available through this server.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The domain id."},"group_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Move the domain into this group, or null to take it out of the group it is in. Group ids come from list_groups or create_group."},"is_parked":{"type":"boolean","description":"Whether this domain is parked — a domain that sends no mail. Parked domains are still monitored, but Palisade expects them to have a reject policy and no legitimate senders."},"dmarc_options":{"type":"object","properties":{"additional_emails":{"type":"array","items":{"type":"string","format":"email"}},"legacy_ruf_emails":{"type":"array","items":{"type":"string","format":"email"}},"policy":{"type":"string","enum":["none","quarantine","reject"]},"pct":{"type":"number","minimum":0,"maximum":100},"sp":{"type":"string","enum":["none","quarantine","reject","same"]},"strict_spf":{"type":"boolean"},"strict_dkim":{"type":"boolean"}},"additionalProperties":false,"description":"DMARC policy settings to change; only the keys you pass are touched. policy is the domain policy (none, quarantine, reject), sp the subdomain policy, pct the percentage of mail the policy applies to, strict_spf/strict_dkim the alignment modes, and additional_emails extra rua recipients. Changing any of these republishes the DMARC record. Tightening policy past none while a legitimate sender still fails authentication will get that sender's mail quarantined or rejected — read the pass rates from get_domain and clear the open tasks from list_tasks first."},"target_spf":{"type":"string","description":"The SPF record Palisade should publish for this domain when hosted SPF is on. Must be a valid v=spf1 record; redirect and ptr mechanisms are rejected. Changing it re-flattens and republishes the hosted SPF chain. Read the current record and its problems with get_spf before changing this."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Domain id. Pass it to get_domain, get_dns_records, verify_domain and enable_mta_sts."},"url":{"type":["string","null"],"description":"The domain name itself, for example \"example.com\"."},"dmarc_policy":{"type":["string","null"],"description":"Published DMARC policy: \"none\", \"quarantine\" or \"reject\"."},"monitoring_status":{"type":["string","null"],"description":"Onboarding state: \"none\" (nothing published), \"pending\" or \"setup\" (Palisade is receiving DMARC reports), \"drifted\" (a record changed under us) or \"failed\"."},"monitoring_failure_reason":{"type":["string","null"],"description":"Why monitoring failed, when monitoring_status is \"failed\". Null otherwise."},"score":{"type":["number","null"],"description":"Email security score, 0-100."},"score_band":{"type":["string","null"],"description":"Score bucket: \"critical\", \"bad\", \"good\" or \"great\"."},"open_tasks_count":{"type":["number","null"],"description":"Open remediation tasks for this domain. Use list_tasks to read them."},"highest_open_task_priority":{"anyOf":[{"anyOf":[{"not":{}},{"type":["number","null"]}],"description":"Priority of the most urgent open task, on a task's own inverted scale: 0 critical, 1 high, 2 medium, 3 low. Null when nothing is open. Sort domains on \"severity\" to order by it."},{"type":"null"}],"description":"Priority of the most urgent open task, on a task's own inverted scale: 0 critical, 1 high, 2 medium, 3 low. Null when nothing is open. Sort domains on \"severity\" to order by it."},"managed_dns_status":{"anyOf":[{"type":"object","properties":{"dmarc":{"type":["string","null"],"description":"Live status of the DMARC record."},"spf":{"type":["string","null"],"description":"Live status of the SPF record."},"dkim":{"type":["string","null"],"description":"Live status of the DKIM record."},"bimi":{"type":["string","null"],"description":"Live status of the BIMI record."},"mta_sts":{"type":["string","null"],"description":"Live status of the MTA-STS record."}},"additionalProperties":true},{"type":"null"}],"description":"Per-record status of the DNS Palisade manages: \"ready\" when the record is live and correct, \"syncing\" while it waits on DNS, \"error\" when it broke or drifted, and \"unknown\" when the record is not managed. These are not the statuses get_dns_records reports per record, which are \"pending\", \"verifying\", \"verified\" and \"drifted\"."},"is_parked":{"type":["boolean","null"],"description":"True when the domain is not meant to send mail."},"is_dmarc_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the DMARC record, so its policy can change without a DNS edit."},"is_spf_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the SPF record."},"is_dkim_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the DKIM records."},"is_bimi_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the BIMI record."},"is_mta_sts_managed":{"type":["boolean","null"],"description":"True when Palisade hosts MTA-STS for the domain."},"dmarc_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"DMARC policy settings: policy, sp, pct, strict_spf, strict_dkim, additional_emails."},"spf_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"SPF settings: enabled, force_flattening, mechanism_descriptions."},"bimi_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"BIMI settings: enabled, logo, certificate."},"dns_conflicts":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]}],"description":"A conflict, with the offending records, the clause they violate and how to fix it."}},{"type":"null"}],"description":"Contradictory records found in the live zone, such as two DMARC TXT records. Empty in the normal case; anything here breaks mail before Palisade can help."},"group_id":{"type":["string","null"],"description":"Id of the group this domain belongs to, or null when ungrouped."},"registrar":{"type":["string","null"],"description":"Registrar of record. This is not necessarily where DNS is hosted — get_dns_records reports that."},"created_at":{"type":["string","null"],"description":"When the domain was added, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the domain last changed, ISO 8601."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"delete_domain","title":"Delete domain","description":"Permanently remove a domain from your Palisade account. This stops monitoring and removes any Palisade-hosted DNS configuration for the domain. It removes the Palisade record only: the domain itself, and any records already published at your DNS provider, are left exactly as they are.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The domain id."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"deleted":{"type":["boolean","null"],"description":"True once the domain is gone."},"object":{"type":["string","null"],"description":"The type of record deleted, \"domain\"."},"id":{"type":["string","null"],"description":"Id of the deleted domain."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"get_dns_records","title":"Get DNS records","description":"Get the DNS records to publish for a domain: purpose (dmarc, spf, dkim, mta_sts, mta_sts_policy, tls_rpt, bimi), record type, host (absolute and relative to the apex), value, recommended TTL, required action (create, replace, or delete), whether the record is required, its live verification status, and already_published. Palisade does not create these records for you — apply each one yourself at the domain's DNS provider using your own DNS tool, connector, or that provider's API; the records are portable, so any DNS integration works. The response also includes dns_provider, resolved from the domain's live NS records: dns_provider.id names the DNS host (for example cloudflare, route53, godaddy, azure, google) and dns_provider.nameservers lists the nameservers it was derived from. If you have a DNS tool or connector for that provider, prefer it and publish the records yourself end to end; note this is the DNS host, which is often not the domain's registrar. When dns_provider.id is null (unknown, mixed, or unresolvable nameservers) do not guess a provider — check dns_provider.nameservers for a tool you do have, and otherwise hand the records to the user to publish. Skip every record where already_published is true — it is already live with exactly this value. For the rest, use the action field to reconcile (create a new record, replace an existing one of the same host/type, or delete the record) and preserve the given TTL. already_published is null when the live lookup could not be completed, in which case treat action as a best-effort hint and publishing is safe but may be redundant. Then call verify_domain; all_verified is true once every required record is verified. IMPORTANT: check the warnings array before applying anything — it is normally empty, but a warning means publishing the records as-is could break the domain's mail. Relay any warning to the user and get confirmation rather than applying silently. Note also that SPF and DKIM records only appear here when Palisade hosts them; their absence does not mean the domain needs none — get_spf reads the domain's live SPF state either way.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The domain id."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"records":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"purpose":{"type":["string","null"],"description":"What the record is for: dmarc, spf, dkim, mta_sts, mta_sts_policy, tls_rpt or bimi."},"type":{"type":["string","null"],"description":"DNS record type, for example TXT or CNAME."},"host":{"type":["string","null"],"description":"Fully qualified record name."},"relative_host":{"type":["string","null"],"description":"Record name relative to the apex; \"@\" designates the apex itself."},"value":{"type":["string","null"],"description":"Exact value to publish. Copy it verbatim."},"ttl":{"type":["number","null"],"description":"Recommended TTL in seconds. Preserve it when publishing."},"action":{"type":["string","null"],"description":"How to reconcile the record: \"create\", \"replace\" an existing record of the same host/type, or \"delete\"."},"required":{"type":["boolean","null"],"description":"False for records that improve an enabled feature but are not needed for it to work."},"status":{"type":["string","null"],"description":"Live status of this record: \"pending\", \"verifying\", \"verified\" or \"drifted\"."},"already_published":{"type":["boolean","null"],"description":"True when live DNS already serves exactly this value, so the record can be skipped. Null when the live lookup could not be completed, in which case \"action\" is a best-effort hint."}},"additionalProperties":true}},{"type":"null"}],"description":"The records to publish at the DNS provider. Skip every record where already_published is true."},"all_verified":{"type":["boolean","null"],"description":"True when every required record has status \"verified\" — the domain is fully set up."},"warnings":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"code":{"type":["string","null"],"description":"Stable identifier for the warning, safe to branch on."},"message":{"type":["string","null"],"description":"Human-readable explanation of the risk."}},"additionalProperties":true},{"type":"null"}]}],"description":"A risk to resolve before publishing."}},{"type":"null"}],"description":"Risks to resolve before publishing these records. Empty in the normal case. Read these first: publishing an enforcing DMARC policy on a domain with no working SPF or DKIM makes receivers reject or quarantine its mail. Relay any warning to the user and get confirmation rather than applying silently."},"dns_provider":{"anyOf":[{"type":"object","properties":{"id":{"type":["string","null"],"description":"Identifier of the DNS host, derived from the domain's live NS records — for example \"cloudflare\", \"route53\", \"godaddy\", \"azure\" or \"google\". Null when the nameservers do not match a known provider, disagree, or could not be resolved; never guess a provider in that case. This is the DNS host, which is often not the registrar."},"nameservers":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"A nameserver hostname."}},{"type":"null"}],"description":"The nameservers the domain is delegated to."}},"additionalProperties":true},{"type":"null"}],"description":"Where the domain's DNS is hosted, so the records can be published with a matching DNS tool, connector or provider API."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"enable_hosted_dmarc","title":"Enable hosted DMARC","description":"Switch a domain to Palisade-hosted DMARC. Prefer this over a self-managed DMARC record: Palisade owns the policy content, so it can tighten the policy (p=none to quarantine to reject), adjust pct, and add reporting addresses without you ever touching DNS again. The published record becomes a CNAME at _dmarc.<domain> pointing at Palisade instead of a DMARC TXT record; your current policy is carried over. Returns the CNAME to publish at your DNS provider — publish it, delete any existing _dmarc TXT record (a leftover TXT conflicts with the CNAME), then call verify_domain and poll get_domain. Hosted DMARC only becomes active once the CNAME resolves; until then \"hosted_dmarc_active\" is false and get_dns_records keeps reporting the self-managed TXT record.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The domain id."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"hosted_dmarc_active":{"type":["boolean","null"],"description":"False until the CNAME resolves. While it is false, get_dns_records keeps reporting the self-managed TXT record."},"record":{"anyOf":[{"type":"object","properties":{"purpose":{"type":["string","null"],"description":"What the record is for: dmarc, spf, dkim, mta_sts, mta_sts_policy, tls_rpt or bimi."},"type":{"type":["string","null"],"description":"DNS record type, for example TXT or CNAME."},"host":{"type":["string","null"],"description":"Fully qualified record name."},"relative_host":{"type":["string","null"],"description":"Record name relative to the apex; \"@\" designates the apex itself."},"value":{"type":["string","null"],"description":"Exact value to publish. Copy it verbatim."},"ttl":{"type":["number","null"],"description":"Recommended TTL in seconds. Preserve it when publishing."},"action":{"type":["string","null"],"description":"How to reconcile the record: \"create\", \"replace\" an existing record of the same host/type, or \"delete\"."},"required":{"type":["boolean","null"],"description":"False for records that improve an enabled feature but are not needed for it to work."},"status":{"type":["string","null"],"description":"Live status of this record: \"pending\", \"verifying\", \"verified\" or \"drifted\"."},"already_published":{"type":["boolean","null"],"description":"True when live DNS already serves exactly this value, so the record can be skipped. Null when the live lookup could not be completed, in which case \"action\" is a best-effort hint."}},"additionalProperties":true},{"type":"null"}],"description":"The CNAME to publish at _dmarc.<domain>. Delete any existing _dmarc TXT record, which conflicts with it."},"domain":{"anyOf":[{"type":"object","properties":{"id":{"type":["string","null"],"description":"Domain id. Pass it to get_domain, get_dns_records, verify_domain and enable_mta_sts."},"url":{"type":["string","null"],"description":"The domain name itself, for example \"example.com\"."},"dmarc_policy":{"type":["string","null"],"description":"Published DMARC policy: \"none\", \"quarantine\" or \"reject\"."},"monitoring_status":{"type":["string","null"],"description":"Onboarding state: \"none\" (nothing published), \"pending\" or \"setup\" (Palisade is receiving DMARC reports), \"drifted\" (a record changed under us) or \"failed\"."},"monitoring_failure_reason":{"type":["string","null"],"description":"Why monitoring failed, when monitoring_status is \"failed\". Null otherwise."},"score":{"type":["number","null"],"description":"Email security score, 0-100."},"score_band":{"type":["string","null"],"description":"Score bucket: \"critical\", \"bad\", \"good\" or \"great\"."},"open_tasks_count":{"type":["number","null"],"description":"Open remediation tasks for this domain. Use list_tasks to read them."},"highest_open_task_priority":{"anyOf":[{"anyOf":[{"not":{}},{"type":["number","null"]}],"description":"Priority of the most urgent open task, on a task's own inverted scale: 0 critical, 1 high, 2 medium, 3 low. Null when nothing is open. Sort domains on \"severity\" to order by it."},{"type":"null"}],"description":"Priority of the most urgent open task, on a task's own inverted scale: 0 critical, 1 high, 2 medium, 3 low. Null when nothing is open. Sort domains on \"severity\" to order by it."},"managed_dns_status":{"anyOf":[{"type":"object","properties":{"dmarc":{"type":["string","null"],"description":"Live status of the DMARC record."},"spf":{"type":["string","null"],"description":"Live status of the SPF record."},"dkim":{"type":["string","null"],"description":"Live status of the DKIM record."},"bimi":{"type":["string","null"],"description":"Live status of the BIMI record."},"mta_sts":{"type":["string","null"],"description":"Live status of the MTA-STS record."}},"additionalProperties":true},{"type":"null"}],"description":"Per-record status of the DNS Palisade manages: \"ready\" when the record is live and correct, \"syncing\" while it waits on DNS, \"error\" when it broke or drifted, and \"unknown\" when the record is not managed. These are not the statuses get_dns_records reports per record, which are \"pending\", \"verifying\", \"verified\" and \"drifted\"."},"is_parked":{"type":["boolean","null"],"description":"True when the domain is not meant to send mail."},"is_dmarc_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the DMARC record, so its policy can change without a DNS edit."},"is_spf_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the SPF record."},"is_dkim_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the DKIM records."},"is_bimi_managed":{"type":["boolean","null"],"description":"True when Palisade hosts the BIMI record."},"is_mta_sts_managed":{"type":["boolean","null"],"description":"True when Palisade hosts MTA-STS for the domain."},"dmarc_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"DMARC policy settings: policy, sp, pct, strict_spf, strict_dkim, additional_emails."},"spf_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"SPF settings: enabled, force_flattening, mechanism_descriptions."},"bimi_options":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"BIMI settings: enabled, logo, certificate."},"dns_conflicts":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]}],"description":"A conflict, with the offending records, the clause they violate and how to fix it."}},{"type":"null"}],"description":"Contradictory records found in the live zone, such as two DMARC TXT records. Empty in the normal case; anything here breaks mail before Palisade can help."},"group_id":{"type":["string","null"],"description":"Id of the group this domain belongs to, or null when ungrouped."},"registrar":{"type":["string","null"],"description":"Registrar of record. This is not necessarily where DNS is hosted — get_dns_records reports that."},"created_at":{"type":["string","null"],"description":"When the domain was added, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the domain last changed, ISO 8601."}},"additionalProperties":true},{"type":"null"}],"description":"The domain in its updated state."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"verify_domain","title":"Verify domain","description":"Trigger DNS verification for a domain after publishing records at your DNS provider. Returns which records are currently verified and queues a full re-check; poll get_domain to see updated statuses. When the SPF check keeps failing, get_spf reports what is wrong with the live record.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The domain id."},"check":{"type":"string","enum":["all","spf","dkim"],"default":"all","description":"Which verification to run: \"all\" checks every record, \"spf\" and \"dkim\" re-check a single record."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"queued":{"type":["boolean","null"],"description":"True when a full re-check was queued. Poll get_domain for the updated statuses."},"verified":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Per-record result of the synchronous pass, keyed by purpose, each a boolean."},"results":{"anyOf":[{"type":"object","properties":{"dmarc":{"type":["boolean","null"],"description":"True when DMARC was confirmed during this pass."},"spf":{"type":["boolean","null"],"description":"True when SPF was confirmed during this pass."},"dkim":{"type":["boolean","null"],"description":"True when DKIM was confirmed during this pass."},"mta_sts":{"type":["boolean","null"],"description":"True when MTA-STS was confirmed during this pass."},"bimi":{"type":["boolean","null"],"description":"True when BIMI was confirmed during this pass."}},"additionalProperties":true},{"type":"null"}],"description":"Outcome of the synchronous pass. A purpose is present only when it was confirmed now; absence means it was not confirmed in this pass — it may already be verified, or still propagating."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"get_domain_plan","title":"Get the remediation plan","description":"Answer \"what happens next on this domain\" rather than \"what is open right now\". Returns the four stages of the DMARC journey - setup, alignment, enforcement, monitoring - with where the domain stands in them and the steps each stage is made of, and upcoming_steps as the shortlist of what Palisade works next. A step already backed by tasks carries their ids in task_ids, so pair it with list_tasks: list_tasks is what can be actioned today, this is the road it is on and what completing it is worth against the audit score. Read-only and computed from stored state plus one cached DNS read, so it changes nothing and opens nothing.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The domain id."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"domain_id":{"type":["string","null"],"description":"The domain the plan is for."},"domain":{"type":["string","null"],"description":"The domain name."},"is_parked":{"type":["boolean","null"],"description":"True when the domain is parked, in which case it is monitored but has no journey to work through."},"agent_proposals_enabled":{"type":["boolean","null"],"description":"False when the organization is on a plan without the agent, in which case upcoming_steps is empty because Palisade will not open those tasks."},"current_stage":{"type":["number","null"],"description":"The stage being worked now, or null when none is current."},"stages":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"number":{"type":["number","null"],"description":"Position in the journey, 1 through 4."},"key":{"type":["string","null"],"description":"Stable identifier: \"setup\", \"alignment\", \"enforcement\" or \"monitoring\"."},"title":{"type":["string","null"],"description":"Stage name."},"description":{"type":["string","null"],"description":"What the stage covers."},"status":{"type":["string","null"],"description":"\"completed\", \"current\" or \"pending\"."},"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"key":{"type":["string","null"],"description":"Stable identifier for the step, for example \"dmarc-setup\", \"alignment-spf\" or \"dmarc-reject-full\"."},"task_type":{"type":["string","null"],"description":"The task type this step opens as, matching \"type\" on a task. Null for a step that never becomes one."},"title":{"type":["string","null"],"description":"What the step asks for."},"description":{"type":["string","null"],"description":"Why the step matters."},"priority":{"type":["number","null"],"description":"Severity on the task scale: 0 critical, 1 high, 2 medium, 3 low."},"score_gain":{"type":["number","null"],"description":"Points of the domain's audit score completing this step would win back."},"status":{"type":["string","null"],"description":"\"in_progress\" when tasks already exist for this step, \"upcoming\" when there is nothing to action yet."},"task_ids":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"A task id, readable with get_task."}},{"type":"null"}],"description":"The tasks carrying out this step. Empty when upcoming."}},"additionalProperties":true},{"type":"null"}]}],"description":"One step of the remediation plan."}},{"type":"null"}],"description":"The work this stage is made of. Empty for a parked domain."},"optional_steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]}],"description":"An optional improvement."}},{"type":"null"}],"description":"Improvements worth making that Palisade does not require."}},"additionalProperties":true},{"type":"null"}]}],"description":"One stage of the DMARC journey."}},{"type":"null"}],"description":"The four stages of the DMARC journey, in order."},"upcoming_steps":{"anyOf":[{"type":"array","items":{"$ref":"#/properties/stages/anyOf/0/items/anyOf/1/anyOf/0/properties/steps/anyOf/0/items"}},{"type":"null"}],"description":"What Palisade works next: the next stage's steps, when it has not started. A subset of stages[].steps with nothing to action yet."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"get_mta_sts","title":"Get MTA-STS status","description":"Get the MTA-STS state for a domain: whether Palisade-hosted MTA-STS is enabled, the policy mode, MX list, and the DNS targets to publish.","inputSchema":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid","description":"The domain id."}},"required":["domain_id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"is_managed":{"type":["boolean","null"],"description":"True when Palisade hosts MTA-STS for this domain."},"status":{"type":["string","null"],"description":"Live status of the MTA-STS records, in the same vocabulary as managed_dns_status: \"ready\", \"syncing\", \"error\" or \"unknown\"."},"mode":{"type":["string","null"],"description":"Policy mode: \"testing\", \"enforce\" or \"none\"."},"max_age":{"type":["number","null"],"description":"Policy max_age in seconds."},"mx":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"An MX hostname covered by the policy."}},{"type":"null"}],"description":"The MX hosts the published policy covers."},"cname_target":{"type":["string","null"],"description":"CNAME target to publish at _mta-sts.<domain>."},"txt_cname_target":{"type":["string","null"],"description":"CNAME target to publish for the policy host."},"tls_reporting_email":{"type":["string","null"],"description":"Address TLS reports are sent to, when TLS-RPT is enabled."},"tls_rpt_record":{"type":["string","null"],"description":"The TLS-RPT record value to publish, when enabled."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"enable_mta_sts","title":"Enable MTA-STS","description":"Enable Palisade-hosted MTA-STS for a domain. Provisions policy hosting and publishes the policy; the domain needs at least one MX record. Returns the CNAME targets to publish at your DNS provider.","inputSchema":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid","description":"The domain id."},"mode":{"type":"string","enum":["testing","enforce","none"],"default":"testing","description":"MTA-STS policy mode. Start with \"testing\", then move to \"enforce\" once TLS reports look clean."},"max_age":{"type":"number","minimum":86400,"maximum":31557600,"default":86400,"description":"Policy max_age in seconds (86400 to 31557600)."}},"required":["domain_id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"is_managed":{"type":["boolean","null"],"description":"True when Palisade hosts MTA-STS for this domain."},"status":{"type":["string","null"],"description":"Live status of the MTA-STS records, in the same vocabulary as managed_dns_status: \"ready\", \"syncing\", \"error\" or \"unknown\"."},"mode":{"type":["string","null"],"description":"Policy mode: \"testing\", \"enforce\" or \"none\"."},"max_age":{"type":["number","null"],"description":"Policy max_age in seconds."},"mx":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"An MX hostname covered by the policy."}},{"type":"null"}],"description":"The MX hosts the published policy covers."},"cname_target":{"type":["string","null"],"description":"CNAME target to publish at _mta-sts.<domain>."},"txt_cname_target":{"type":["string","null"],"description":"CNAME target to publish for the policy host."},"tls_reporting_email":{"type":["string","null"],"description":"Address TLS reports are sent to, when TLS-RPT is enabled."},"tls_rpt_record":{"type":["string","null"],"description":"The TLS-RPT record value to publish, when enabled."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"disable_mta_sts","title":"Disable MTA-STS","description":"Turn off Palisade-hosted MTA-STS for a domain. Palisade releases the policy hosting, stops serving the policy, and switches TLS reporting off for the domain. Sending mail servers that cached the policy keep enforcing it until its max_age elapses, so mail to this domain can still fail if its MX or TLS setup does not match the cached policy — prefer moving the policy to mode \"testing\" and waiting out max_age before disabling. Palisade does not clean up the domain's own DNS: the _mta-sts TXT record, the mta-sts CNAME, and the _smtp._tls TLS-RPT TXT record are all left published, and a record pointing at a policy or reporting address Palisade no longer serves is what makes delivery fail. Call get_dns_records afterwards and delete what it still lists at your DNS provider. Calling this on a domain that does not have hosted MTA-STS is a no-op, not an error.","inputSchema":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid","description":"The domain id."}},"required":["domain_id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"is_managed":{"type":["boolean","null"],"description":"True when Palisade hosts MTA-STS for this domain."},"status":{"type":["string","null"],"description":"Live status of the MTA-STS records, in the same vocabulary as managed_dns_status: \"ready\", \"syncing\", \"error\" or \"unknown\"."},"mode":{"type":["string","null"],"description":"Policy mode: \"testing\", \"enforce\" or \"none\"."},"max_age":{"type":["number","null"],"description":"Policy max_age in seconds."},"mx":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"An MX hostname covered by the policy."}},{"type":"null"}],"description":"The MX hosts the published policy covers."},"cname_target":{"type":["string","null"],"description":"CNAME target to publish at _mta-sts.<domain>."},"txt_cname_target":{"type":["string","null"],"description":"CNAME target to publish for the policy host."},"tls_reporting_email":{"type":["string","null"],"description":"Address TLS reports are sent to, when TLS-RPT is enabled."},"tls_rpt_record":{"type":["string","null"],"description":"The TLS-RPT record value to publish, when enabled."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"get_spf","title":"Get SPF diagnostics","description":"Read the live SPF state of a domain: the record DNS actually serves, how many DNS lookups it costs receivers against SPF's limit of 10, a breakdown of every include/a/mx/ptr/exists/redirect lookup with what each accounts for (terms receivers never reach are not counted: anything to the right of a matching all, and a redirect= in a record that also carries an all), and the problems found — a record over the lookup limit, more than one v=spf1 record (which receivers reject outright), includes that no longer resolve, loops, and unsafe all mechanisms. Purely diagnostic: it changes nothing, and it reads the domain's live DNS rather than Palisade's stored state, so it works the same whether or not Palisade hosts the record. When a problem shows up, check list_tasks before designing a fix — Palisade usually opens a task with worked-out instructions, and an SPF change made against that guidance is how a working domain stops delivering mail. A record over the lookup limit is what Palisade-hosted SPF flattening solves: set target_spf with update_domain and Palisade flattens the record and serves it from a single include, which get_dns_records then returns for publishing.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The domain id."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"domain_id":{"type":["string","null"],"description":"Id of the domain the diagnostics are for."},"domain":{"type":["string","null"],"description":"The domain name whose live DNS was read, for example \"example.com\"."},"record":{"type":["string","null"],"description":"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":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"One v=spf1 TXT record."}},{"type":"null"}],"description":"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":{"type":["number","null"],"description":"TTL of the SPF TXT record in seconds, when it could be read."},"status":{"type":["string","null"],"description":"Overall SPF health: \"pass\", \"warn\" or \"fail\"."},"lookup_count":{"type":["number","null"],"description":"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":{"type":["number","null"],"description":"The limit SPF imposes before receivers return a permanent error: 10."},"lookups_remaining":{"type":["number","null"],"description":"How many more lookups the record could add before breaking. 0 when at or over the limit."},"lookup_breakdown":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"mechanism":{"type":["string","null"],"description":"The SPF term that costs the lookup: \"include\", \"a\", \"mx\", \"ptr\", \"exists\" or \"redirect\". \"expanded-domain\" marks a macro expansion, which costs none."},"host":{"type":["string","null"],"description":"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":{"type":["number","null"],"description":"Nesting depth: 0 for mechanisms in the domain's own record, 1 for those inside an include it references, and so on."},"lookups":{"type":["number","null"],"description":"DNS lookups this entry accounts for: itself plus everything nested under it. The expensive includes are the ones with the highest number here."},"error":{"type":["boolean","null"],"description":"True when this lookup failed to resolve to a usable record."},"note":{"type":["string","null"],"description":"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."}},"additionalProperties":true},{"type":"null"}]}],"description":"One DNS lookup the record costs receivers."}},{"type":"null"}],"description":"Every lookup in the chain, nested entries following their parent."},"problems":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"code":{"type":["string","null"],"description":"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":{"type":["string","null"],"description":"\"error\" when receivers fail SPF (or get no protection from it) because of this; \"warning\" for a risk that does not break evaluation."},"host":{"type":["string","null"],"description":"The hostname the problem is about, when it concerns a specific lookup."},"message":{"type":["string","null"],"description":"What is wrong and what it means for mail, in plain language."}},"additionalProperties":true},{"type":"null"}]}],"description":"A problem found in the live SPF setup."}},{"type":"null"}],"description":"Problems found, errors before warnings. Empty when SPF is healthy."},"hosted_spf":{"anyOf":[{"type":"object","properties":{"is_managed":{"type":["boolean","null"],"description":"True when Palisade hosts and flattens the SPF record for this domain."},"status":{"type":["string","null"],"description":"Live status of the hosted SPF chain, when managed: \"ready\", \"syncing\", \"error\" or \"unknown\"."},"target_spf":{"type":["string","null"],"description":"The record Palisade flattens and publishes, when hosted SPF is configured."},"error_reason":{"type":["string","null"],"description":"Why the hosted SPF chain is in error, when it is."},"last_flattened_at":{"type":["string","null"],"description":"When Palisade last flattened and published the hosted chain, ISO 8601."}},"additionalProperties":true},{"type":"null"}],"description":"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."},"next_step":{"type":["string","null"],"description":"The single action Palisade suggests taking next."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"list_tasks","title":"List tasks","description":"List remediation tasks Palisade opened for your domains (failing SPF/DKIM sources, DNS issues, setup steps). By default returns open and pending tasks most urgent first, with equally urgent tasks in rollout order so DMARC setup comes before the SPF and DKIM work that depends on its reports.","inputSchema":{"type":"object","properties":{"statuses":{"type":"array","items":{"type":"string","enum":["open","snoozed","canceled","dismissed","pending","done"]},"description":"Filter by task status. Defaults to \"open\" and \"pending\", so a snoozed task is not returned unless you ask for \"snoozed\" explicitly. A task is closed as \"done\", \"dismissed\" or \"canceled\" — pass all three to see everything that has been resolved, since [\"done\"] alone leaves out the ones that were dismissed or cancelled."},"domain":{"type":"string","format":"uuid","description":"Filter to a single domain id."},"group":{"$ref":"#/properties/domain","description":"Filter to the domains in a single group id."},"sort":{"type":"string","enum":["created_at","priority","resolved_at","sequence","status"],"default":"priority","description":"Field to sort by. Defaults to \"priority\", most urgent first, with ties broken by rollout stage — so among equally urgent tasks, DMARC setup comes before the SPF and DKIM work that depends on its reports. Use \"sequence\" to walk the rollout outright, ignoring urgency."},"sort_direction":{"type":"string","enum":["desc","asc"],"default":"asc","description":"Sort direction: \"asc\" or \"desc\"."},"per_page":{"type":"number","minimum":1,"maximum":100,"default":50,"description":"Tasks per page, 1 to 100."},"page":{"type":"number","minimum":1,"default":1,"description":"Page to return, 1-based. Defaults to 1."},"expand":{"type":"array","items":{"type":"string","enum":["domain","source"]},"description":"Optional expansions: \"domain\" and \"source\"."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Task id. Pass it to get_task."},"domain_id":{"type":["string","null"],"description":"Domain the task belongs to."},"sending_domain":{"type":["string","null"],"description":"The sending domain the task is about, when the task came from a DMARC report."},"source_id":{"type":["string","null"],"description":"Id of the sending source (an ESP or service) the task is about, when known."},"source_alias":{"type":["string","null"],"description":"Human-readable name of that sending source, for example \"Mailchimp\"."},"priority":{"type":["number","null"],"description":"Urgency, 0 to 3. Lower is more urgent: 0 is critical, 3 is low. Sort ascending to work the list."},"sequence":{"type":["number","null"],"description":"Rollout stage, lower first: 10 DMARC setup, 20 foundation records, 30 SPF/DKIM alignment, 40 cleanup, 50 enforcement, 60 hardening. Orders equally urgent tasks, since an alignment task cannot be actioned before DMARC reporting exists to show the failure."},"status":{"type":["string","null"],"description":"Task status: \"open\", \"snoozed\" or \"pending\" while the work is outstanding; \"done\", \"dismissed\" or \"canceled\" once it is closed."},"type":{"type":["string","null"],"description":"What kind of problem the task describes, for example a failing SPF or DKIM source."},"description":{"type":["string","null"],"description":"What is wrong, in plain language."},"snoozed_until":{"type":["string","null"],"description":"When a snoozed task becomes visible again, ISO 8601. Null when not snoozed."},"created_at":{"type":["string","null"],"description":"When the task was opened, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the task last changed, ISO 8601."},"resolved_at":{"type":["string","null"],"description":"When the task was resolved, ISO 8601. Null while it is open."},"resolution_reason":{"type":["string","null"],"description":"Why a done task closed: \"verified\" when Palisade's own check found the problem gone, \"manual\" when a person or an API caller marked it done and nothing was checked. Null when the task is not done or the reason was never recorded."},"instructions":{"anyOf":[{"type":"object","properties":{"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"One step, in the order it should be carried out."}},{"type":"null"}],"description":"What to do, in order."},"dns_records":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]}],"description":"A record to publish: type, name, value and whether it is required."}},{"type":"null"}],"description":"DNS records the fix needs, when it needs any."}},"additionalProperties":true},{"type":"null"}],"description":"Provider-specific fix instructions Palisade has already worked out. Present only with expand: [\"instructions\"], and only on tasks that have them. Follow these rather than improvising a fix."},"source":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"The sending source record. Present only with expand: [\"source\"]."},"domain":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"The domain record. Present only with expand: [\"domain\"]."}},"additionalProperties":true}},{"type":"null"}],"description":"The tasks on this page, most urgent first."},"page_info":{"anyOf":[{"type":"object","properties":{"count":{"type":["number","null"],"description":"Total number of records matching the filter, across all pages — not the size of this page."},"page":{"type":["number","null"],"description":"The page returned, 1-based."},"per_page":{"type":["number","null"],"description":"Page size used for this response."}},"additionalProperties":true},{"type":"null"}],"description":"Pagination for the list. Request the next page when count exceeds page * per_page."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"get_task","title":"Get task","description":"Get a single task by id. Use expand: [\"instructions\"] to include provider-specific fix instructions when the task supports them.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The task id."},"expand":{"type":"array","items":{"type":"string","enum":["domain","instructions","source"]},"description":"Optional expansions: \"domain\", \"source\", and \"instructions\" (provider-specific fix instructions, when available)."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Task id. Pass it to get_task."},"domain_id":{"type":["string","null"],"description":"Domain the task belongs to."},"sending_domain":{"type":["string","null"],"description":"The sending domain the task is about, when the task came from a DMARC report."},"source_id":{"type":["string","null"],"description":"Id of the sending source (an ESP or service) the task is about, when known."},"source_alias":{"type":["string","null"],"description":"Human-readable name of that sending source, for example \"Mailchimp\"."},"priority":{"type":["number","null"],"description":"Urgency, 0 to 3. Lower is more urgent: 0 is critical, 3 is low. Sort ascending to work the list."},"sequence":{"type":["number","null"],"description":"Rollout stage, lower first: 10 DMARC setup, 20 foundation records, 30 SPF/DKIM alignment, 40 cleanup, 50 enforcement, 60 hardening. Orders equally urgent tasks, since an alignment task cannot be actioned before DMARC reporting exists to show the failure."},"status":{"type":["string","null"],"description":"Task status: \"open\", \"snoozed\" or \"pending\" while the work is outstanding; \"done\", \"dismissed\" or \"canceled\" once it is closed."},"type":{"type":["string","null"],"description":"What kind of problem the task describes, for example a failing SPF or DKIM source."},"description":{"type":["string","null"],"description":"What is wrong, in plain language."},"snoozed_until":{"type":["string","null"],"description":"When a snoozed task becomes visible again, ISO 8601. Null when not snoozed."},"created_at":{"type":["string","null"],"description":"When the task was opened, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the task last changed, ISO 8601."},"resolved_at":{"type":["string","null"],"description":"When the task was resolved, ISO 8601. Null while it is open."},"resolution_reason":{"type":["string","null"],"description":"Why a done task closed: \"verified\" when Palisade's own check found the problem gone, \"manual\" when a person or an API caller marked it done and nothing was checked. Null when the task is not done or the reason was never recorded."},"instructions":{"anyOf":[{"type":"object","properties":{"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"One step, in the order it should be carried out."}},{"type":"null"}],"description":"What to do, in order."},"dns_records":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]}],"description":"A record to publish: type, name, value and whether it is required."}},{"type":"null"}],"description":"DNS records the fix needs, when it needs any."}},"additionalProperties":true},{"type":"null"}],"description":"Provider-specific fix instructions Palisade has already worked out. Present only with expand: [\"instructions\"], and only on tasks that have them. Follow these rather than improvising a fix."},"source":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"The sending source record. Present only with expand: [\"source\"]."},"domain":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"The domain record. Present only with expand: [\"domain\"]."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"complete_task","title":"Complete task","description":"Close a remediation task as done, once the change it asked for is actually live. Do the fix first and confirm it: publish the records at your DNS provider, run verify_domain, and check the result before calling this. Completing a task does not re-check DNS and does not fix anything by itself - it only records that the work is finished, so a task closed over an unfixed domain will be reopened by the next monitoring sweep. Closing a task discards nothing - the task and its history remain - and reopening it clears resolved_at, so an open task never reports a resolution time.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The task id."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Task id. Pass it to get_task."},"domain_id":{"type":["string","null"],"description":"Domain the task belongs to."},"sending_domain":{"type":["string","null"],"description":"The sending domain the task is about, when the task came from a DMARC report."},"source_id":{"type":["string","null"],"description":"Id of the sending source (an ESP or service) the task is about, when known."},"source_alias":{"type":["string","null"],"description":"Human-readable name of that sending source, for example \"Mailchimp\"."},"priority":{"type":["number","null"],"description":"Urgency, 0 to 3. Lower is more urgent: 0 is critical, 3 is low. Sort ascending to work the list."},"sequence":{"type":["number","null"],"description":"Rollout stage, lower first: 10 DMARC setup, 20 foundation records, 30 SPF/DKIM alignment, 40 cleanup, 50 enforcement, 60 hardening. Orders equally urgent tasks, since an alignment task cannot be actioned before DMARC reporting exists to show the failure."},"status":{"type":["string","null"],"description":"Task status: \"open\", \"snoozed\" or \"pending\" while the work is outstanding; \"done\", \"dismissed\" or \"canceled\" once it is closed."},"type":{"type":["string","null"],"description":"What kind of problem the task describes, for example a failing SPF or DKIM source."},"description":{"type":["string","null"],"description":"What is wrong, in plain language."},"snoozed_until":{"type":["string","null"],"description":"When a snoozed task becomes visible again, ISO 8601. Null when not snoozed."},"created_at":{"type":["string","null"],"description":"When the task was opened, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the task last changed, ISO 8601."},"resolved_at":{"type":["string","null"],"description":"When the task was resolved, ISO 8601. Null while it is open."},"resolution_reason":{"type":["string","null"],"description":"Why a done task closed: \"verified\" when Palisade's own check found the problem gone, \"manual\" when a person or an API caller marked it done and nothing was checked. Null when the task is not done or the reason was never recorded."},"instructions":{"anyOf":[{"type":"object","properties":{"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"One step, in the order it should be carried out."}},{"type":"null"}],"description":"What to do, in order."},"dns_records":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]}],"description":"A record to publish: type, name, value and whether it is required."}},{"type":"null"}],"description":"DNS records the fix needs, when it needs any."}},"additionalProperties":true},{"type":"null"}],"description":"Provider-specific fix instructions Palisade has already worked out. Present only with expand: [\"instructions\"], and only on tasks that have them. Follow these rather than improvising a fix."},"source":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"The sending source record. Present only with expand: [\"source\"]."},"domain":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"The domain record. Present only with expand: [\"domain\"]."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"dismiss_task","title":"Dismiss task","description":"Close a remediation task that does not apply, without doing the work it asks for - for example a failing sender the organization does not own and will not authenticate. Use complete_task instead when the fix was actually made. For a task about a sending source, set ignore_spf and/or ignore_dkim to stop Palisade counting that source against the domain, otherwise the next sweep sees the same failing source and opens the task again. Closing a task discards nothing - the task and its history remain - and reopening it clears resolved_at, so an open task never reports a resolution time.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The task id."},"ignore_spf":{"type":"boolean","description":"Stop counting this task's source against SPF, so Palisade does not reopen the same task for it."},"ignore_dkim":{"type":"boolean","description":"Stop counting this task's source against DKIM, so Palisade does not reopen the same task for it."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Task id. Pass it to get_task."},"domain_id":{"type":["string","null"],"description":"Domain the task belongs to."},"sending_domain":{"type":["string","null"],"description":"The sending domain the task is about, when the task came from a DMARC report."},"source_id":{"type":["string","null"],"description":"Id of the sending source (an ESP or service) the task is about, when known."},"source_alias":{"type":["string","null"],"description":"Human-readable name of that sending source, for example \"Mailchimp\"."},"priority":{"type":["number","null"],"description":"Urgency, 0 to 3. Lower is more urgent: 0 is critical, 3 is low. Sort ascending to work the list."},"sequence":{"type":["number","null"],"description":"Rollout stage, lower first: 10 DMARC setup, 20 foundation records, 30 SPF/DKIM alignment, 40 cleanup, 50 enforcement, 60 hardening. Orders equally urgent tasks, since an alignment task cannot be actioned before DMARC reporting exists to show the failure."},"status":{"type":["string","null"],"description":"Task status: \"open\", \"snoozed\" or \"pending\" while the work is outstanding; \"done\", \"dismissed\" or \"canceled\" once it is closed."},"type":{"type":["string","null"],"description":"What kind of problem the task describes, for example a failing SPF or DKIM source."},"description":{"type":["string","null"],"description":"What is wrong, in plain language."},"snoozed_until":{"type":["string","null"],"description":"When a snoozed task becomes visible again, ISO 8601. Null when not snoozed."},"created_at":{"type":["string","null"],"description":"When the task was opened, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the task last changed, ISO 8601."},"resolved_at":{"type":["string","null"],"description":"When the task was resolved, ISO 8601. Null while it is open."},"resolution_reason":{"type":["string","null"],"description":"Why a done task closed: \"verified\" when Palisade's own check found the problem gone, \"manual\" when a person or an API caller marked it done and nothing was checked. Null when the task is not done or the reason was never recorded."},"instructions":{"anyOf":[{"type":"object","properties":{"steps":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"One step, in the order it should be carried out."}},{"type":"null"}],"description":"What to do, in order."},"dns_records":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}]}],"description":"A record to publish: type, name, value and whether it is required."}},{"type":"null"}],"description":"DNS records the fix needs, when it needs any."}},"additionalProperties":true},{"type":"null"}],"description":"Provider-specific fix instructions Palisade has already worked out. Present only with expand: [\"instructions\"], and only on tasks that have them. Follow these rather than improvising a fix."},"source":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"The sending source record. Present only with expand: [\"source\"]."},"domain":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"The domain record. Present only with expand: [\"domain\"]."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"get_dmarc_summary","title":"Get DMARC summary","description":"Summarize what DMARC aggregate reports say about a domain over a recent window: message volume, DMARC/SPF/DKIM pass rates, the compliance breakdown, and the change against the immediately preceding window of the same length (so the default 7-day period is a week-over-week comparison). Also returns the domain's DMARC policy, whether Palisade currently recommends tightening it, and the open remediation tasks for the domain. Pass rates here are computed over all reported volume including forwarded and uncategorised traffic, so they read lower than the last_14_days figures get_domain returns, which count only compliant and non-compliant mail; the payload carries both so the difference is visible. When this surfaces a problem, do not design a fix: policy_readiness and every entry in open_tasks carry a task id, and get_task with expand: [\"instructions\"] returns the steps Palisade has already worked out for it.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The domain id."},"period":{"type":"string","enum":["last_7_days","last_14_days","last_30_days"],"default":"last_7_days","description":"Reporting window, ending yesterday. Defaults to last_7_days."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"domain_id":{"type":["string","null"],"description":"Id of the domain the summary is for."},"domain":{"type":["string","null"],"description":"The domain name the summary is for."},"date_from":{"type":["string","null"],"description":"First day of the window, YYYY-MM-DD. Clamped to the earliest day the organization's plan retains report history for, so it can be later than the requested period alone implies."},"date_to":{"type":["string","null"],"description":"Last day of the window, YYYY-MM-DD. Always yesterday: aggregate reports arrive a day in arrears, so a window ending today would read as a volume collapse."},"volume":{"type":["number","null"],"description":"Total messages reported in the window, across every compliance bucket."},"dmarc_pass_pct":{"type":["number","null"],"description":"Share of volume that passed DMARC, 0-100, rounded to two decimals, and 0 when the window has no volume. The denominator is volume, so forwarded and uncategorised traffic counts against it - which is why this reads lower than the last_14_days figure."},"spf_pass_pct":{"type":["number","null"],"description":"Share of volume that passed SPF, 0-100, rounded to two decimals, and 0 when the window has no volume."},"dkim_pass_pct":{"type":["number","null"],"description":"Share of volume that passed DKIM, 0-100, rounded to two decimals, and 0 when the window has no volume."},"totals":{"anyOf":[{"type":"object","properties":{"compliant":{"type":["number","null"],"description":"Messages that passed DMARC alignment."},"non_compliant":{"type":["number","null"],"description":"Messages that failed DMARC. This is the number to drive to zero."},"forwarded":{"type":["number","null"],"description":"Messages the compliance categorizer attributed to forwarding, which fails SPF for reasons the sender cannot fix."},"unknown":{"type":["number","null"],"description":"Messages from a sending source Palisade could not identify."},"undetermined":{"type":["number","null"],"description":"Messages whose compliance is undetermined, including reports the categorizer has not processed yet."}},"additionalProperties":true},{"type":"null"}],"description":"Message counts by compliance bucket. They sum to volume."},"previous_period":{"anyOf":[{"type":"object","properties":{"date_from":{"type":["string","null"],"description":"First day of the window, YYYY-MM-DD. Clamped to the earliest day the organization's plan retains report history for, so it can be later than the requested period alone implies."},"date_to":{"type":["string","null"],"description":"Last day of the window, YYYY-MM-DD. No window ever includes today: aggregate reports arrive a day in arrears, so a window ending today would read as a volume collapse."},"volume":{"type":["number","null"],"description":"Total messages reported in the window, across every compliance bucket."},"dmarc_pass_pct":{"type":["number","null"],"description":"Share of volume that passed DMARC, 0-100, rounded to two decimals, and 0 when the window has no volume. The denominator is volume, so forwarded and uncategorised traffic counts against it - which is why this reads lower than the last_14_days figure."},"spf_pass_pct":{"type":["number","null"],"description":"Share of volume that passed SPF, 0-100, rounded to two decimals, and 0 when the window has no volume."},"dkim_pass_pct":{"type":["number","null"],"description":"Share of volume that passed DKIM, 0-100, rounded to two decimals, and 0 when the window has no volume."}},"additionalProperties":true},{"type":"null"}],"description":"The same number of days immediately before this window. It ends the day before this window starts, so its date_to is not yesterday."},"change":{"anyOf":[{"type":"object","properties":{"volume":{"type":["number","null"],"description":"Change in message volume against the previous window."},"dmarc_pass_pct":{"type":["number","null"],"description":"Change in DMARC pass rate, in percentage points, rounded to two decimals."},"spf_pass_pct":{"type":["number","null"],"description":"Change in SPF pass rate, in percentage points, rounded to two decimals."},"dkim_pass_pct":{"type":["number","null"],"description":"Change in DKIM pass rate, in percentage points, rounded to two decimals."}},"additionalProperties":true},{"type":"null"}],"description":"This window minus the previous one. Positive means the metric improved (or grew, for volume)."},"last_14_days":{"anyOf":[{"type":"object","properties":{"volume":{"type":["number","null"],"description":"Messages counted over the last 14 days, as stored on the domain."},"dmarc_pass_pct":{"type":["number","null"],"description":"Share of that volume that passed DMARC, 0-100."},"spf_pass_pct":{"type":["number","null"],"description":"Share of that volume that passed SPF, 0-100."},"dkim_pass_pct":{"type":["number","null"],"description":"Share of that volume that passed DKIM, 0-100."}},"additionalProperties":true},{"type":"null"}],"description":"The stored 14-day figures get_domain reports. They count only compliant and non-compliant traffic, so they read higher than the window figures above on a domain with forwarding. A mismatch is the two definitions, not a fault."},"dmarc_policy":{"type":["string","null"],"description":"The domain's DMARC policy state, in the same form get_domain reports."},"monitoring_status":{"type":["string","null"],"description":"Monitoring state of the domain, in the same form get_domain reports."},"policy_readiness":{"anyOf":[{"type":"object","properties":{"status":{"type":["string","null"],"description":"\"advance_recommended\" when Palisade has an open policy-advancement task for the domain, \"hold\" when it has not opened one, and \"at_maximum\" when the domain is already at p=reject with pct=100."},"reason":{"type":["string","null"],"description":"Why Palisade does or does not recommend tightening the policy right now. When status is \"advance_recommended\", this is the advancement task's own description."},"open_task_id":{"type":["string","null"],"description":"The UPDATE_DMARC_RECORD task carrying the recommendation, when Palisade has opened one. Read it with get_task."}},"additionalProperties":true},{"type":"null"}],"description":"Whether Palisade currently recommends tightening the DMARC policy. This reports the recommendation Palisade has already computed - it is not a second, independent gate."},"open_tasks":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"id":{"type":["string","null"],"description":"Task id. Pass it to get_task with expand: [\"instructions\"] for the fix."},"type":{"type":["string","null"],"description":"What kind of problem the task describes."},"priority":{"type":["number","null"],"description":"Lower is more urgent. 0 is the most urgent."},"description":{"type":["string","null"],"description":"What is wrong, in plain language."},"source_alias":{"type":["string","null"],"description":"The sending source this task covers. Matches senders[].source_alias from list_dmarc_senders."},"sending_domain":{"type":["string","null"],"description":"The sending domain the task is about, when it has one."}},"additionalProperties":true},{"type":"null"}]}],"description":"An open remediation task for this domain."}},{"type":"null"}],"description":"Open and pending tasks, most urgent first, capped at 10. Do not design a fix yourself - each entry carries a task id whose instructions Palisade has already worked out."},"open_tasks_count":{"type":["number","null"],"description":"Total open and pending tasks, which may exceed the array length."},"next_step":{"type":["string","null"],"description":"The single action Palisade suggests taking next."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"list_dmarc_senders","title":"List DMARC senders","description":"List the sources sending mail as a domain, built from DMARC aggregate reports and ordered by volume. This is what receivers actually saw, so it includes senders nobody has confirmed and senders Palisade cannot identify (is_known false) - those are usually the interesting ones, and they are the reason to use this rather than the confirmed-sources list. Each sender carries its volume and share, DMARC/SPF/DKIM pass rates, the compliance breakdown, the header-from domains it sent as, and is_failing when it has non-compliant volume. A failing sender that Palisade has opened remediation for also carries open_task_id: call get_task with expand: [\"instructions\"] and follow those steps rather than working out an SPF or DKIM change yourself. Returns at most 20 senders; truncated tells you whether more exist. Aggregates only - raw reports are not available through this tool.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The domain id."},"period":{"type":"string","enum":["last_7_days","last_14_days","last_30_days"],"default":"last_7_days","description":"Reporting window, ending yesterday. Defaults to last_7_days."},"limit":{"type":"integer","minimum":1,"maximum":20,"default":10,"description":"How many senders to return, highest volume first. Maximum 20."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"domain_id":{"type":["string","null"],"description":"The domain the senders were seen for."},"domain":{"type":["string","null"],"description":"The domain name."},"date_from":{"type":["string","null"],"description":"First day of the window, YYYY-MM-DD."},"date_to":{"type":["string","null"],"description":"Last day of the window, YYYY-MM-DD."},"volume":{"type":["number","null"],"description":"Total windowed volume across every sender, not just the ones returned."},"total_senders":{"type":["number","null"],"description":"Distinct senders seen in the window."},"truncated":{"type":["boolean","null"],"description":"True when total_senders exceeds the returned list."},"senders":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"source_alias":{"type":["string","null"],"description":"Stable identifier for the sending source: a Palisade source id when recognised, otherwise the sending base domain, or \"unknown\". This is the key that links a sender to a task."},"name":{"type":["string","null"],"description":"Display name when the source is recognised, otherwise the same as source_alias."},"source_id":{"type":["string","null"],"description":"Palisade source id, when the sender is recognised."},"is_known":{"type":["boolean","null"],"description":"False when DMARC reports show this sender but Palisade cannot identify it. These are usually the interesting ones."},"sending_domains":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"A header-from domain."}},{"type":"null"}],"description":"The header-from domains this source sent as, highest volume first."},"volume":{"type":["number","null"],"description":"Messages this sender accounts for in the window."},"volume_share_pct":{"type":["number","null"],"description":"Share of the domain's total windowed volume this sender accounts for, 0-100."},"dmarc_pass_pct":{"type":["number","null"],"description":"Share of this sender's volume that passed DMARC, 0-100."},"spf_pass_pct":{"type":["number","null"],"description":"Share of this sender's volume that passed SPF, 0-100."},"dkim_pass_pct":{"type":["number","null"],"description":"Share of this sender's volume that passed DKIM, 0-100."},"totals":{"anyOf":[{"type":"object","properties":{"compliant":{"type":["number","null"],"description":"Messages that passed DMARC alignment."},"non_compliant":{"type":["number","null"],"description":"Messages that failed DMARC. This is the number to drive to zero."},"forwarded":{"type":["number","null"],"description":"Messages the categorizer attributed to forwarding, which fails SPF for reasons the sender cannot fix."},"unknown":{"type":["number","null"],"description":"Messages from a source Palisade could not identify."},"undetermined":{"type":["number","null"],"description":"Messages whose compliance is undetermined, including reports not processed yet."}},"additionalProperties":true},{"type":"null"}],"description":"Message counts by compliance bucket."},"is_failing":{"type":["boolean","null"],"description":"True when the sender has non-compliant volume in the window."},"open_task_id":{"type":["string","null"],"description":"An open remediation task covering this sender, when one exists. Call get_task with expand: [\"instructions\"] for the fix rather than improvising one."},"open_task_type":{"type":["string","null"],"description":"What kind of problem that task describes."}},"additionalProperties":true},{"type":"null"}]}],"description":"A source sending mail as this domain, as receivers reported it."}},{"type":"null"}],"description":"The senders, highest volume first."},"next_step":{"type":["string","null"],"description":"The single action Palisade suggests taking next."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"list_groups","title":"List groups","description":"List the groups in your Palisade account. Groups organize domains (for example per customer, for MSPs). Use expand: [\"domains\"] for per-group domain stats.","inputSchema":{"type":"object","properties":{"query":{"type":"string","description":"Filter by group name (substring match)."},"sort":{"type":"string","enum":["created_at","name"],"description":"Field to sort by: \"created_at\" or \"name\"."},"sort_direction":{"type":"string","enum":["desc","asc"],"description":"Sort direction: \"asc\" or \"desc\"."},"per_page":{"type":"number","minimum":1,"maximum":100,"default":100,"description":"Groups per page, 1 to 100. Defaults to 100."},"page":{"type":"number","minimum":1,"default":1,"description":"Page to return, 1-based. Defaults to 1."},"expand":{"type":"array","items":{"type":"string","enum":["domains"]},"description":"Optional expansions. Use [\"domains\"] to include per-group domain counts, enforcement, volume and average score."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Group id. Pass it as group_id when adding a domain."},"name":{"type":["string","null"],"description":"Group name."},"external_customer_id":{"type":["string","null"],"description":"Your own id for this customer, when one was set."},"external_billing_entity_id":{"type":["string","null"],"description":"Your own billing entity id for this customer, when one was set."},"domains":{"anyOf":[{"type":"object","properties":{"count":{"type":["number","null"],"description":"Domains in the group."},"enforcement_pct":{"type":["number","null"],"description":"Share of those domains at DMARC p=quarantine or p=reject, 0-100."},"last_14_days_volume":{"type":["number","null"],"description":"Messages seen in DMARC reports over the last 14 days."},"last_14_days_dmarc_pass_pct":{"type":["number","null"],"description":"Share of that volume passing DMARC, 0-100."},"average_score":{"type":["number","null"],"description":"Mean email security score across the group, 0-100."}},"additionalProperties":true},{"type":"null"}],"description":"Aggregate stats for the group. Present only with expand: [\"domains\"]."},"created_at":{"type":["string","null"],"description":"When the group was created, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the group last changed, ISO 8601."}},"additionalProperties":true}},{"type":"null"}],"description":"The groups on this page."},"page_info":{"anyOf":[{"type":"object","properties":{"count":{"type":["number","null"],"description":"Total number of records matching the filter, across all pages — not the size of this page."},"page":{"type":["number","null"],"description":"The page returned, 1-based."},"per_page":{"type":["number","null"],"description":"Page size used for this response."}},"additionalProperties":true},{"type":"null"}],"description":"Pagination for the list. Request the next page when count exceeds page * per_page."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"create_group","title":"Create group","description":"Create a group to organize domains — for example one group per customer if you are an MSP. Returns the group id, which you pass as the group_id argument of create_domain. Use list_groups first to avoid creating a duplicate.","inputSchema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Name of the group, for example the customer it represents. Must not be empty."}},"required":["name"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Group id. Pass it as group_id when adding a domain."},"name":{"type":["string","null"],"description":"Group name."},"external_customer_id":{"type":["string","null"],"description":"Your own id for this customer, when one was set."},"external_billing_entity_id":{"type":["string","null"],"description":"Your own billing entity id for this customer, when one was set."},"domains":{"anyOf":[{"type":"object","properties":{"count":{"type":["number","null"],"description":"Domains in the group."},"enforcement_pct":{"type":["number","null"],"description":"Share of those domains at DMARC p=quarantine or p=reject, 0-100."},"last_14_days_volume":{"type":["number","null"],"description":"Messages seen in DMARC reports over the last 14 days."},"last_14_days_dmarc_pass_pct":{"type":["number","null"],"description":"Share of that volume passing DMARC, 0-100."},"average_score":{"type":["number","null"],"description":"Mean email security score across the group, 0-100."}},"additionalProperties":true},{"type":"null"}],"description":"Aggregate stats for the group. Present only with expand: [\"domains\"]."},"created_at":{"type":["string","null"],"description":"When the group was created, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the group last changed, ISO 8601."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"update_group","title":"Update group","description":"Rename a group or change the external identifiers it is reconciled by. Pass the id plus only the fields you want to change. This does not move domains — use update_domain with group_id to change which group a domain belongs to.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The group id, from list_groups."},"name":{"type":"string","minLength":1,"description":"A new name for the group. Renaming also updates the group name shown on every domain in it."},"external_customer_id":{"type":["string","null"],"description":"Your own identifier for the customer this group represents, used to reconcile Palisade groups against an external system. Must be unique across your groups; null clears it. Setting this clears external_billing_entity_id."},"external_billing_entity_id":{"type":["string","null"],"description":"Your own identifier for the billing entity this group bills to; null clears it."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Group id. Pass it as group_id when adding a domain."},"name":{"type":["string","null"],"description":"Group name."},"external_customer_id":{"type":["string","null"],"description":"Your own id for this customer, when one was set."},"external_billing_entity_id":{"type":["string","null"],"description":"Your own billing entity id for this customer, when one was set."},"domains":{"anyOf":[{"type":"object","properties":{"count":{"type":["number","null"],"description":"Domains in the group."},"enforcement_pct":{"type":["number","null"],"description":"Share of those domains at DMARC p=quarantine or p=reject, 0-100."},"last_14_days_volume":{"type":["number","null"],"description":"Messages seen in DMARC reports over the last 14 days."},"last_14_days_dmarc_pass_pct":{"type":["number","null"],"description":"Share of that volume passing DMARC, 0-100."},"average_score":{"type":["number","null"],"description":"Mean email security score across the group, 0-100."}},"additionalProperties":true},{"type":"null"}],"description":"Aggregate stats for the group. Present only with expand: [\"domains\"]."},"created_at":{"type":["string","null"],"description":"When the group was created, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the group last changed, ISO 8601."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"delete_group","title":"Delete group","description":"Permanently delete a group. The domains in it are not deleted and keep monitoring, but they stop belonging to the group and have to be reassigned one at a time with update_domain. Check how many domains it holds first with list_groups using expand: [\"domains\"], which reports a count per group — the group and its external identifiers cannot be recovered, and recreating it produces a new id.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The group id, from list_groups."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"deleted":{"type":["boolean","null"],"description":"True once the group is gone."},"object":{"type":["string","null"],"description":"The type of record deleted, \"group\"."},"id":{"type":["string","null"],"description":"Id of the deleted group."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"get_subscription","title":"Get subscription","description":"Get the billing state of your Palisade organization: pricing type, subscription status, the current billing period (start and end), resource limits, Free-plan email usage when applicable, and current-calendar-month domain usage.","inputSchema":{"type":"object","properties":{},"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"pricing_type":{"type":["string","null"],"description":"Which price list the organization is on."},"is_custom":{"type":["boolean","null"],"description":"True on a negotiated custom agreement, matching pricing_type \"custom\". Distributor children are billed on a contract too but are not custom; is_contract_billed covers both."},"is_contract_billed":{"type":["boolean","null"],"description":"True when billed on a contract rather than self-serve checkout: a negotiated custom agreement, or a distributor child paid for by its parent. pricing_type tells them apart."},"subscription_status":{"type":["string","null"],"description":"Stripe subscription status, for example \"active\", \"trialing\" or \"canceled\"."},"subscription_current_period_end":{"type":["string","null"],"description":"When the current billing period ends, ISO 8601."},"subscription_current_period_start":{"type":["string","null"],"description":"When the current billing period began, ISO 8601. Read the period from this rather than assuming it is a month long and subtracting one from the end: an annual subscription bills a year at a time. Null for contract-billed and MSP organizations, and while a renewal is being processed."},"subscription_cancel_at_period_end":{"type":["boolean","null"],"description":"True when the subscription is set to stop at period end instead of renewing."},"trial_eligible":{"type":["boolean","null"],"description":"Whether checkout would include a free trial. False once the organization has ever subscribed."},"card_required_for_trial":{"type":["boolean","null"],"description":"True when the organization starts its free trial only by adding a card at checkout rather than automatically at signup."},"msp_intent":{"type":["boolean","null"],"description":"True when the organization signed up card-first asking for MSP pricing. It is on regular pricing until verified as an MSP."},"has_billing_portal":{"type":["boolean","null"],"description":"Whether start_billing_portal can open the Stripe billing portal for this organization: invoices, billing email and address, payment methods. On a contract-billed plan it is true only when a portal that cannot cancel the contract is configured."},"it_teams_plan":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"The IT Teams email-volume tier and interval, when subscribed to one. Null on other plans."},"resources_limits":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Plan limits, such as how many domains the organization may monitor."},"parent_organization_name":{"type":["string","null"],"description":"Name of the reseller or parent that owns billing, when there is one."},"free_plan_email_usage":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Current calendar-month email volume for a Free-plan organization against the 1,000/month cap. Null on paid plans."},"current_month_usage":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Domain counts for the current calendar month: active, inactive, parked and deleted."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"start_checkout","title":"Start checkout","description":"Create a Stripe Checkout session to start a paid Palisade subscription for your organization. Returns a checkout URL. Open this URL in a browser to complete payment - hand it to a human unless you can complete Stripe Checkout. On the IT Teams plan, pick a monthly email-volume tier that covers both the email volume and the number of set-up domains (100k: $19/mo, up to 100K emails/month and 2 set-up domains; 250k: $29/mo, up to 250K emails/month and 3 set-up domains; 500k: $59/mo, up to 500K emails/month and 6 set-up domains; 1m: $99/mo, up to 1M emails/month and 10 set-up domains; 2500k: $249/mo, up to 2.5M emails/month and 20 set-up domains; above the top tier on either, contact sales) and a billing interval (annual saves 20%).","inputSchema":{"type":"object","properties":{"volume_tier":{"type":"string","enum":["100k","250k","500k","1m","2500k"],"description":"IT Teams tier, covering both email volume and set-up domains. Defaults to 100k."},"billing_interval":{"type":"string","enum":["monthly","annual"],"description":"Billing interval. Annual billing is discounted 20%. Defaults to monthly."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"url":{"type":["string","null"],"description":"The Stripe Checkout URL. Open it in a browser to pay."},"session_id":{"type":["string","null"],"description":"The Stripe Checkout session id."},"guidance":{"type":["string","null"],"description":"What to do with the URL: hand it to a human unless you can complete Stripe Checkout yourself."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"start_billing_portal","title":"Start billing portal","description":"Start a Stripe billing-portal session for your organization and return its URL. This creates a session rather than reading one: payment-method changes, invoice history, and subscription cancellation all happen in the portal - open the URL in a browser (hand it to a human if needed).","inputSchema":{"type":"object","properties":{},"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"url":{"type":["string","null"],"description":"The Stripe billing-portal URL, for payment methods, invoices and cancellation."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"list_webhook_events","title":"List webhook event types","description":"List the event types a webhook endpoint can subscribe to, with what each one means. Call this before create_webhook_endpoint so you can pass a precise enabled_events list rather than subscribing to everything.","inputSchema":{"type":"object","properties":{},"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"data":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"anyOf":[{"type":"object","properties":{"type":{"type":["string","null"],"description":"The event type to pass in enabled_events, for example \"domain.updated\"."},"description":{"type":["string","null"],"description":"What triggers this event."},"payload_type":{"type":["string","null"],"description":"The object the event payload carries."}},"additionalProperties":true},{"type":"null"}]}],"description":"An event type a webhook endpoint can subscribe to."}},{"type":"null"}],"description":"Every event type available. Pass the ones you need as enabled_events on create_webhook_endpoint."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"list_webhook_endpoints","title":"List webhook endpoints","description":"List the webhook endpoints registered for your organization, including their status and recent delivery health. An endpoint showing AUTO_DISABLED stopped receiving events after repeated delivery failures and has to be re-enabled.","inputSchema":{"type":"object","properties":{"page":{"type":"number","minimum":1,"default":1,"description":"Page to return, 1-based. Defaults to 1."},"per_page":{"type":"number","minimum":1,"maximum":100,"default":50,"description":"Endpoints per page, 1 to 100."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Endpoint id. Pass it to delete_webhook_endpoint."},"url":{"type":["string","null"],"description":"The https URL Palisade POSTs events to."},"description":{"type":["string","null"],"description":"Your note about what the endpoint is for."},"enabled_events":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"An event type, for example \"domain.updated\"."}},{"type":"null"}],"description":"Event types this endpoint receives. An empty array means every event, including ones added later — not none."},"status":{"type":["string","null"],"description":"ENABLED receives events. DISABLED was turned off by you. AUTO_DISABLED was turned off by Palisade after repeated delivery failures and stays off until re-enabled."},"secret_reminder":{"type":["string","null"],"description":"Last 4 characters of the signing secret. The full secret is returned only at creation."},"api_version":{"type":["string","null"],"description":"API version the payload shape is pinned to, captured when the endpoint was created."},"consecutive_failures":{"type":["number","null"],"description":"Failed deliveries in a row. Palisade auto-disables the endpoint once this runs high."},"disabled_reason":{"type":["string","null"],"description":"Why the endpoint was disabled, when it is not ENABLED."},"last_success_at":{"type":["string","null"],"description":"Last successful delivery, ISO 8601."},"last_failure_at":{"type":["string","null"],"description":"Last failed delivery, ISO 8601."},"created_at":{"type":["string","null"],"description":"When the endpoint was created, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the endpoint last changed, ISO 8601."}},"additionalProperties":true}},{"type":"null"}],"description":"The webhook endpoints on this page."},"page_info":{"anyOf":[{"type":"object","properties":{"count":{"type":["number","null"],"description":"Total number of records matching the filter, across all pages — not the size of this page."},"page":{"type":["number","null"],"description":"The page returned, 1-based."},"per_page":{"type":["number","null"],"description":"Page size used for this response."}},"additionalProperties":true},{"type":"null"}],"description":"Pagination for the list. Request the next page when count exceeds page * per_page."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"create_webhook_endpoint","title":"Create webhook endpoint","description":"Register an https URL that Palisade POSTs events to, so you can react to domain changes instead of polling get_domain. The response contains the signing secret, and it is the only time it is ever returned — surface it to the user immediately and tell them to store it. Leave enabled_events empty to receive every event.","inputSchema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"The https URL Palisade POSTs events to. It must be publicly reachable; private and loopback addresses are rejected."},"description":{"type":"string","maxLength":500,"description":"Your own note about what this endpoint is for. Up to 500 characters."},"enabled_events":{"type":"array","items":{"type":"string","enum":["domain.created","domain.updated","domain.deleted","domain.monitoring.activated","domain.monitoring.failed","domain.monitoring.drifted","domain.monitoring.recovered","domain.hosted_record.ready","domain.hosted_record.error","domain.score_changed","domain.policy_changed","mta_sts.enabled","mta_sts.updated","mta_sts.disabled","task.opened","task.completed","task.canceled","task.dismissed","task.reopened","organization.updated","member.added","member.updated","member.removed","invitation.created","invitation.revoked","group.created","group.updated","group.deleted"]},"description":"Event types to receive. Call list_webhook_events first and pass a precise list. Omit it, or send an empty array, to receive every event including ones added later."}},"required":["url"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Endpoint id. Pass it to delete_webhook_endpoint."},"url":{"type":["string","null"],"description":"The https URL Palisade POSTs events to."},"description":{"type":["string","null"],"description":"Your note about what the endpoint is for."},"enabled_events":{"anyOf":[{"type":"array","items":{"anyOf":[{"not":{}},{"type":["string","null"]}],"description":"An event type, for example \"domain.updated\"."}},{"type":"null"}],"description":"Event types this endpoint receives. An empty array means every event, including ones added later — not none."},"status":{"type":["string","null"],"description":"ENABLED receives events. DISABLED was turned off by you. AUTO_DISABLED was turned off by Palisade after repeated delivery failures and stays off until re-enabled."},"secret_reminder":{"type":["string","null"],"description":"Last 4 characters of the signing secret. The full secret is returned only at creation."},"api_version":{"type":["string","null"],"description":"API version the payload shape is pinned to, captured when the endpoint was created."},"consecutive_failures":{"type":["number","null"],"description":"Failed deliveries in a row. Palisade auto-disables the endpoint once this runs high."},"disabled_reason":{"type":["string","null"],"description":"Why the endpoint was disabled, when it is not ENABLED."},"last_success_at":{"type":["string","null"],"description":"Last successful delivery, ISO 8601."},"last_failure_at":{"type":["string","null"],"description":"Last failed delivery, ISO 8601."},"created_at":{"type":["string","null"],"description":"When the endpoint was created, ISO 8601."},"updated_at":{"type":["string","null"],"description":"When the endpoint last changed, ISO 8601."},"secret":{"type":["string","null"],"description":"The signing secret, returned only here and never again. Surface it to the user immediately and tell them to store it."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"delete_webhook_endpoint","title":"Delete webhook endpoint","description":"Stop delivering events to a webhook endpoint and remove it. Past delivery history is kept for debugging. Use list_webhook_endpoints first to get the id.","inputSchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Id of the endpoint to delete. Use list_webhook_endpoints to find it."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Id of the endpoint that was removed. Delivery history is kept for debugging."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"create_prospecting_report","title":"Generate a prospecting report","description":"Audit up to ten domains and produce a branded PDF report on their email-authentication posture, hosted behind a Palisade link you can send to a prospect. Returns the link and the date it stops working. This is the tool for the outreach step after audit_domain has told you a domain is worth writing to: audit_domain answers \"is this domain exposed\", this turns the answer into something a prospect can read. The report carries your organization name and logo, and its contact details when include_organization_info is set. Domains do not need to be in your Palisade account. Reads live DNS, so it pays into the same budget as audit_domain, once per domain rather than once per report. A domain whose nameservers do not answer is left out; if none of them answer the call fails rather than returning an empty report.","inputSchema":{"type":"object","properties":{"domains":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":10,"description":"The domains to audit and write up. At most 10 per report."},"generated_for":{"type":"string","minLength":1,"description":"The prospect the report is for. Printed on the cover, so use the name they would recognise."},"language":{"type":"string","enum":["en","fr"],"description":"Report language. Defaults to English."},"include_organization_info":{"type":"boolean","description":"Print your organization's email, phone and website on the contact page."},"include_bimi":{"type":"boolean","description":"Include the BIMI section."},"include_dns_records":{"type":"boolean","description":"Include the published DNS records behind each verdict."},"include_potential_loss":{"type":"boolean","description":"Include the estimated financial exposure section."},"theme":{"type":"string","enum":["light","dark"],"description":"The skin the PDF is drawn in: \"light\" on paper or \"dark\" on a near-black ground. Defaults to light."},"currency":{"type":"string","enum":["USD","CAD","EUR","GBP","AUD"],"description":"The currency the modelled cost is labelled in: USD, CAD, EUR, GBP or AUD. Defaults to USD. The figure is the same in every currency; only its symbol changes."},"deadline_weeks":{"type":"integer","minimum":1,"maximum":52,"description":"How many weeks the plan and closing pages give the prospect to fix what the report found, from 1 to 52. Defaults to 6."}},"required":["domains","generated_for"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Report id. Pass it to get_prospecting_report or delete_prospecting_report."},"url":{"type":["string","null"],"description":"The hosted PDF link to send to the prospect. It is a Palisade URL rather than a storage one, which is what makes expires_at enforceable: the object itself is never disclosed."},"generated_for":{"type":["string","null"],"description":"The prospect the report was written for, as it appears on the cover."},"domains":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The domains the report covers. Only domains that could be audited are included."},"language":{"type":["string","null"],"description":"\"en\" or \"fr\". Fixed at creation; regenerate to change it."},"theme":{"type":["string","null"],"description":"\"light\" or \"dark\": the skin the PDF was drawn in."},"currency":{"type":["string","null"],"description":"The currency the modelled cost is labelled in, e.g. \"USD\"."},"deadline_weeks":{"type":["number","null"],"description":"How many weeks the plan and closing pages give the prospect to fix what was found."},"expires_at":{"type":["string","null"],"description":"When the link stops working. After this the report is unreachable and cannot be revived."},"created_at":{"type":["string","null"],"description":"When the report was generated."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"}},{"name":"list_prospecting_reports","title":"List prospecting reports","description":"List the prospecting reports your organization has generated, newest first, with the link and expiry for each. Use it to find a report you generated earlier rather than regenerating it, which would cost another round of DNS lookups. Reports disappear from this list once they expire.","inputSchema":{"type":"object","properties":{"page":{"type":"integer","minimum":1,"description":"1-based page number. Defaults to 1."},"per_page":{"type":"integer","minimum":1,"maximum":100,"description":"Page size. Defaults to 25."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Report id. Pass it to get_prospecting_report or delete_prospecting_report."},"url":{"type":["string","null"],"description":"The hosted PDF link to send to the prospect. It is a Palisade URL rather than a storage one, which is what makes expires_at enforceable: the object itself is never disclosed."},"generated_for":{"type":["string","null"],"description":"The prospect the report was written for, as it appears on the cover."},"domains":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The domains the report covers. Only domains that could be audited are included."},"language":{"type":["string","null"],"description":"\"en\" or \"fr\". Fixed at creation; regenerate to change it."},"theme":{"type":["string","null"],"description":"\"light\" or \"dark\": the skin the PDF was drawn in."},"currency":{"type":["string","null"],"description":"The currency the modelled cost is labelled in, e.g. \"USD\"."},"deadline_weeks":{"type":["number","null"],"description":"How many weeks the plan and closing pages give the prospect to fix what was found."},"expires_at":{"type":["string","null"],"description":"When the link stops working. After this the report is unreachable and cannot be revived."},"created_at":{"type":["string","null"],"description":"When the report was generated."}},"additionalProperties":true}},{"type":"null"}],"description":"The reports on this page, newest first."},"page_info":{"anyOf":[{"type":"object","properties":{"count":{"type":["number","null"],"description":"Total number of records matching the filter, across all pages — not the size of this page."},"page":{"type":["number","null"],"description":"The page returned, 1-based."},"per_page":{"type":["number","null"],"description":"Page size used for this response."}},"additionalProperties":true},{"type":"null"}],"description":"Pagination for the list. Request the next page when count exceeds page * per_page."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"get_prospecting_report","title":"Get a prospecting report","description":"Read one prospecting report by id: the domains it covers, who it was written for, its link and when that link expires. Returns the metadata and the link, not the PDF itself.","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"The report id, from create_prospecting_report or list_prospecting_reports."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Report id. Pass it to get_prospecting_report or delete_prospecting_report."},"url":{"type":["string","null"],"description":"The hosted PDF link to send to the prospect. It is a Palisade URL rather than a storage one, which is what makes expires_at enforceable: the object itself is never disclosed."},"generated_for":{"type":["string","null"],"description":"The prospect the report was written for, as it appears on the cover."},"domains":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The domains the report covers. Only domains that could be audited are included."},"language":{"type":["string","null"],"description":"\"en\" or \"fr\". Fixed at creation; regenerate to change it."},"theme":{"type":["string","null"],"description":"\"light\" or \"dark\": the skin the PDF was drawn in."},"currency":{"type":["string","null"],"description":"The currency the modelled cost is labelled in, e.g. \"USD\"."},"deadline_weeks":{"type":["number","null"],"description":"How many weeks the plan and closing pages give the prospect to fix what was found."},"expires_at":{"type":["string","null"],"description":"When the link stops working. After this the report is unreachable and cannot be revived."},"created_at":{"type":["string","null"],"description":"When the report was generated."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"delete_prospecting_report","title":"Delete a prospecting report","description":"Delete a prospecting report and the stored PDF behind it. The link stops working immediately, so do this when a report was sent to the wrong prospect or should no longer be readable. It cannot be undone; generate a new report instead of trying to restore one.","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"The report id to delete."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"success":{"type":["boolean","null"],"description":"True when the report and its stored PDF were both removed."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"list_dns_connections","title":"List DNS provider connections","description":"List the DNS providers your organization has connected to Palisade. A connection is what lets Palisade publish email-authentication records into a domain's zone directly, instead of returning records for someone to copy by hand. Read this before telling anyone to publish a record manually: if a connection covers the domain, the records can be applied through it. Authorising a new connection happens in the Palisade app, not here, because it goes through the provider's own consent screen.","inputSchema":{"type":"object","properties":{"provider":{"type":"string","description":"Filter to one provider, e.g. \"cloudflare\"."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Connection id."},"provider":{"type":["string","null"],"description":"The DNS provider this connection authenticates against, e.g. \"cloudflare\"."},"display_name":{"type":["string","null"],"description":"Display name for the connection."},"status":{"type":["string","null"],"description":"Whether the stored credentials still work. A revoked connection still appears in the list."},"last_error":{"type":["string","null"],"description":"Why the connection last failed, when it did."},"last_verified_at":{"type":["string","null"],"description":"When the credentials were last confirmed to work."},"expires_at":{"type":["string","null"],"description":"When the stored authorisation expires, when it does."},"created_at":{"type":["string","null"],"description":"When the connection was authorised."}},"additionalProperties":true}},{"type":"null"}],"description":"The DNS provider connections on this page."},"page_info":{"anyOf":[{"type":"object","properties":{"count":{"type":["number","null"],"description":"Total number of records matching the filter, across all pages — not the size of this page."},"page":{"type":["number","null"],"description":"The page returned, 1-based."},"per_page":{"type":["number","null"],"description":"Page size used for this response."}},"additionalProperties":true},{"type":"null"}],"description":"Pagination for the list. Request the next page when count exceeds page * per_page."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"get_dns_connection","title":"Get a DNS provider connection","description":"Read one DNS provider connection by id, including whether its stored credentials still work. A connection whose credentials have been revoked at the provider still appears in the list, so check here before assuming records can be published through it.","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"The connection id, from list_dns_connections."}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Connection id."},"provider":{"type":["string","null"],"description":"The DNS provider this connection authenticates against, e.g. \"cloudflare\"."},"display_name":{"type":["string","null"],"description":"Display name for the connection."},"status":{"type":["string","null"],"description":"Whether the stored credentials still work. A revoked connection still appears in the list."},"last_error":{"type":["string","null"],"description":"Why the connection last failed, when it did."},"last_verified_at":{"type":["string","null"],"description":"When the credentials were last confirmed to work."},"expires_at":{"type":["string","null"],"description":"When the stored authorisation expires, when it does."},"created_at":{"type":["string","null"],"description":"When the connection was authorised."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"get_dns_connection_coverage","title":"Check whether a connection covers a domain","description":"Answer whether one of your DNS connections can publish records for a given domain, and which one. This is the question to ask before returning DNS records for a human to paste: a covered domain can be configured through the connection instead. Takes a Palisade domain id, so the domain has to be in your account; use list_domains to find it.","inputSchema":{"type":"object","properties":{"domain_id":{"type":"string","description":"The Palisade domain id, from list_domains or get_domain."}},"required":["domain_id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"covered":{"type":["boolean","null"],"description":"True when a connection can publish this domain's records without anyone copying them by hand. False is a real answer, not an error: it means no connected provider holds a zone for this domain."},"connection_id":{"type":["string","null"],"description":"The connection that covers the domain. Null when covered is false."},"provider":{"type":["string","null"],"description":"The provider holding the zone, e.g. \"cloudflare\". Null when covered is false."},"display_name":{"type":["string","null"],"description":"Display name of the covering connection. Null when covered is false."},"zone":{"anyOf":[{"type":"object","properties":{"external_id":{"type":["string","null"],"description":"The provider's own id for the zone."},"name":{"type":["string","null"],"description":"The zone name, e.g. example.com."}},"additionalProperties":true},{"type":"null"}],"description":"The provider zone matched to the domain. Null when covered is false."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"list_activity_log","title":"List account activity","description":"Read what has happened in your Palisade account: who changed a domain, when a record was published, which API key did it. Use it to answer \"what changed and who changed it\" before assuming something broke on its own, and to check whether a change you are about to make has already been made by someone else. Filter by action, actor, resource or date range. Paginated by cursor rather than page number, so pass the cursor from the previous response to continue. Reads only, and shows your own organization.","inputSchema":{"type":"object","properties":{"action":{"type":"string","description":"Filter to one action, e.g. \"domain.created\". list_activity_log_actions returns the vocabulary."},"actor_email":{"type":"string","description":"Filter to a person by email address."},"resource_type":{"type":"string","description":"Filter to a kind of record, e.g. \"Domain\"."},"resource_id":{"type":"string","description":"Filter to one record by id."},"date_from":{"type":"string","description":"ISO 8601 timestamp. Only activity at or after this."},"date_to":{"type":"string","description":"ISO 8601 timestamp. Only activity at or before this."},"cursor":{"type":"string","description":"Cursor from a previous response, to fetch the next page."},"per_page":{"type":"integer","minimum":1,"maximum":200,"description":"Page size. Defaults to 50."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"data":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Entry id."},"action":{"type":["string","null"],"description":"What happened, e.g. \"domain.created\". list_activity_log_actions returns the full vocabulary."},"actor":{"anyOf":[{"type":"object","additionalProperties":{}},{"type":"null"}],"description":"Who did it: a user, an API key, or Palisade itself for system activity."},"resource_type":{"type":["string","null"],"description":"The kind of record acted on."},"resource_id":{"type":["string","null"],"description":"The record acted on."},"created_at":{"type":["string","null"],"description":"When it happened."}},"additionalProperties":true}},{"type":"null"}],"description":"The activity on this page, newest first."},"page_info":{"anyOf":[{"type":"object","properties":{"has_more":{"type":["boolean","null"],"description":"True when more activity matches the filter than this page returned."},"next_cursor":{"type":["string","null"],"description":"Pass this back as `cursor` to fetch the next page. Null when has_more is false."},"per_page":{"type":["number","null"],"description":"Page size used for this response."}},"additionalProperties":true},{"type":"null"}],"description":"Cursor pagination. This list is not page-numbered: continue with next_cursor rather than an offset."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"list_activity_log_actions","title":"List activity log actions","description":"Return every action value that can appear in the activity log. Read this before filtering list_activity_log by action, so the filter uses a value that exists rather than a guess that silently matches nothing.","inputSchema":{"type":"object","properties":{},"$schema":"http://json-schema.org/draft-07/schema#"},"outputSchema":{"type":"object","properties":{"actions":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Every action value that can appear in the log, for filtering list_activity_log."}},"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"readOnlyHint":true,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"}}],"prompts":[{"name":"onboard-domain","title":"Onboard a domain","description":"Take a domain from nothing to verified: add it, publish the DNS records Palisade returns at the domain's own DNS provider, verify, and review the remediation tasks onboarding opens.","arguments":[{"name":"domain","description":"The domain name, for example example.com.","required":true}]},{"name":"diagnose-deliverability","title":"Diagnose deliverability","description":"Find out why mail sent as a domain is failing authentication, then fix it by following the remediation Palisade already opened rather than improvising an SPF or DKIM change, verify the fix, and close the tasks it covered.","arguments":[{"name":"domain","description":"The domain name, for example example.com.","required":true}]},{"name":"weekly-report","title":"Weekly report","description":"Summarise the week for one domain or the whole organization: DMARC volume and pass-rate deltas, open remediation tasks, and record statuses, written as a status update for a client or a manager.","arguments":[{"name":"domain","description":"A domain name to report on. Omit it, or pass \"all\", to cover every domain.","required":false}]}]}