{"openapi":"3.0.0","info":{"version":"2025-04-30","title":"Palisade API","description":"The Palisade API uses independent rate-limit buckets. Honor the response headers — do not hard-code limits.\n\n### Buckets\n- **Global** — applies to every request reaching the API (public and authenticated alike).\n- **DNS** — applies in addition to the global bucket on endpoints that trigger outbound DNS resolution during the request (domain verify, find-dkim-keys, the DNS-reading tools — audit, mailbox provider, SPF include validation, DNS lookup and header analysis — and DNS provider detect). A request can hit either bucket independently.\n- **API key** — applies in addition to the global bucket on every request authenticated with an API key. Each key gets its own bucket, shared across all API instances.\n- **API key DNS** — a stricter per-key bucket that applies in addition to the DNS bucket on the same DNS-triggering endpoints when the request is authenticated with an API key.\n- **Signup** — a per-IP bucket on the headless signup endpoint.\n- **MCP OAuth** — a per-subject bucket on the MCP server for requests authenticated with an OAuth (JWT) token, mirroring the API-key bucket for token callers.\n\n### Headers on every response\n- `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` — current bucket state.\n\n### Headers on 429 responses\n- `Retry-After` — seconds to wait before retrying.\n\n### 429 response body\n`{ \"error\": \"rate_limit_exceeded\", \"scope\": \"global\" | \"dns\" | \"api_key\" | \"api_key_dns\" | \"signup\" | \"mcp_oauth\", \"limit\": N, \"window_seconds\": N, \"retry_after_seconds\": N, \"message\": \"...\" }`. Clients must respect `Retry-After` and back off exponentially on repeat failures.\n\n### Keying\nLimits are applied **per API key** when the request carries Basic credentials, **per JWT subject** for dashboard-session callers, and fall back to **per source IP** otherwise. Multiple keys from the same host each get their own bucket."},"externalDocs":{"description":"View the raw OpenAPI Specification in JSON format","url":"/swagger.json"},"servers":[{"url":"https://api.palisade.email","description":"API server"}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"BasicAuth":{"type":"http","scheme":"basic","description":"API key sent as HTTP Basic credentials. Put the API key in the username and leave the password empty: `Authorization: Basic base64(<api_key>:)`."}},"responses":{"RateLimitExceeded":{"description":"Rate limit exceeded. See `Retry-After` and `RateLimit-*` response headers.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying."},"RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests permitted in the window."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in the window."},"RateLimit-Reset":{"schema":{"type":"integer"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitExceeded"}}}}},"schemas":{"Source":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"external_id":{"type":"string"},"name":{"type":"string"},"homepage":{"type":"string","nullable":true,"format":"uri"},"base_domains":{"type":"array","items":{"type":"string"}},"category":{"type":"string","enum":["SENDING_TOOL","MAILBOX_PROVIDER","VPS_PROVIDER","SECURITY_PROVIDER","EMAIL_RELAY"]},"dmarc_support":{"type":"boolean"},"spf_support":{"type":"string","enum":["NONE","RARE","FULL"]},"spf_support_options":{"type":"object","properties":{"direct":{"type":"object","nullable":true,"properties":{"values":{"type":"array","items":{"type":"string"}},"requires_advanced_config":{"type":"boolean","nullable":true}},"required":["values","requires_advanced_config"]},"subdomain":{"type":"object","nullable":true,"properties":{"is_managed":{"type":"boolean"},"requires_user_selection":{"type":"boolean"},"path":{"type":"string","nullable":true},"value":{"type":"string","nullable":true},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"value":{"type":"string"}},"required":["type"]}}},"required":["is_managed","requires_user_selection","path","value","dns_records"]}},"required":["direct","subdomain"]},"dkim_support":{"type":"string","enum":["NONE","RARE","FULL"]},"parent_source_id":{"type":"string","nullable":true,"format":"uuid"},"display_name":{"type":"string","nullable":true},"envelope_froms":{"type":"array","items":{"type":"string"}},"is_custom":{"type":"boolean"},"documentation":{"type":"object","properties":{"spf":{"type":"string","nullable":true},"dkim":{"type":"string","nullable":true},"combined":{"type":"string","nullable":true}}},"spf_reply_to_target":{"type":"string","nullable":true},"spf_supported_methods":{"type":"array","items":{"type":"string","enum":["DIRECT","SUBDOMAIN","MANAGED_SUBDOMAIN"]}},"forwarding_type":{"type":"string","enum":["NOT_FORWARDER","FORWARDER","FORWARDER_ONLY"]},"dkim_discovery_selectors":{"type":"array","items":{"type":"string"}},"spf_discovery_values":{"type":"array","items":{"type":"string"}},"spf_reply_to_discovery_values":{"type":"array","items":{"type":"string"}},"mx_domains":{"type":"array","items":{"type":"string"}},"dkim_instructions":{"type":"object","nullable":true,"properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"spf_instructions":{"type":"object","nullable":true,"properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"instructions_curation":{"type":"object","nullable":true,"properties":{"dkim":{"type":"object","nullable":true,"properties":{"status":{"type":"string","enum":["PENDING","APPROVED","REJECTED"]},"proposal":{"type":"object","properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"provenance":{"type":"object","properties":{"doc_url":{"type":"string"},"doc_title":{"type":"string","nullable":true},"doc_discovered":{"type":"boolean"},"model":{"type":"string"},"confidence":{"type":"number"},"critic_notes":{"type":"array","items":{"type":"string"}},"generated_at":{"type":"string"}},"required":["doc_url","doc_title","doc_discovered","model","confidence","critic_notes","generated_at"]},"reviewed_at":{"type":"string","nullable":true},"reviewed_by":{"type":"string","nullable":true}},"required":["status","proposal","provenance"]},"spf":{"type":"object","nullable":true,"properties":{"status":{"type":"string","enum":["PENDING","APPROVED","REJECTED"]},"proposal":{"type":"object","properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"provenance":{"type":"object","properties":{"doc_url":{"type":"string"},"doc_title":{"type":"string","nullable":true},"doc_discovered":{"type":"boolean"},"model":{"type":"string"},"confidence":{"type":"number"},"critic_notes":{"type":"array","items":{"type":"string"}},"generated_at":{"type":"string"}},"required":["doc_url","doc_title","doc_discovered","model","confidence","critic_notes","generated_at"]},"reviewed_at":{"type":"string","nullable":true},"reviewed_by":{"type":"string","nullable":true}},"required":["status","proposal","provenance"]}}},"status":{"type":"string","enum":["ACTIVE","PENDING_REVIEW","REJECTED"]},"origin":{"type":"string","enum":["DMARC_IO","AI_DISCOVERED","MANUAL"]},"ai_confidence":{"type":"number","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","external_id","name","homepage","base_domains","category","dmarc_support","spf_support","spf_support_options","dkim_support","envelope_froms","is_custom","documentation","spf_reply_to_target","spf_supported_methods","forwarding_type","dkim_discovery_selectors","spf_discovery_values","spf_reply_to_discovery_values","mx_domains","dkim_instructions","status","origin","ai_confidence","created_at","updated_at"]},"SourceSpfIndexItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"logo_url":{"type":"string","nullable":true},"homepage":{"type":"string","nullable":true,"format":"uri"},"values":{"type":"array","items":{"type":"string"},"description":"SPF mechanisms Palisade prescribes adding for this source. Safe to publish on a user's behalf."},"recognition_values":{"type":"array","items":{"type":"string"},"description":"Every mechanism that identifies this source, including values only observed in the wild. Use these to name a mechanism already present in a record; they are not safe to add to one."},"requires_advanced_config":{"type":"boolean","description":"When true, the mechanism depends on per-customer setup and must not be offered as a one-click add."}},"required":["id","name","logo_url","homepage","values","recognition_values","requires_advanced_config"]},"SourceDkimIndexItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"logo_url":{"type":"string","nullable":true},"homepage":{"type":"string","nullable":true,"description":"The source's home page. Free text on the record rather than a validated URL - a handful of sources carry a bare hostname such as `samanage.com` - so treat it as a hostname that may or may not carry a scheme."},"selectors":{"type":"array","items":{"type":"string"},"description":"DKIM selectors this source is known to publish under. Matched against the selector of a key already in DNS; they are per-source conventions, not values to publish on a customer's behalf."},"base_domains":{"type":"array","items":{"type":"string"},"description":"Domains this source signs from. A DKIM CNAME whose target sits under one of these belongs to this source, which is the only signal that identifies a per-customer selector such as HubSpot's `hs1-<portal>`."}},"required":["id","name","logo_url","homepage","selectors","base_domains"]},"RateLimitExceeded":{"type":"object","properties":{"error":{"type":"string","enum":["rate_limit_exceeded"]},"scope":{"type":"string","enum":["global","dns","api_key","api_key_dns","signup","mcp_oauth"],"description":"Which rate-limit bucket was exceeded."},"message":{"type":"string"},"limit":{"type":"integer","minimum":0,"exclusiveMinimum":true,"description":"Requests allowed in the window for this bucket."},"window_seconds":{"type":"integer","minimum":0,"exclusiveMinimum":true},"retry_after_seconds":{"type":"integer","minimum":0,"exclusiveMinimum":true}},"required":["error","scope","message","limit","window_seconds","retry_after_seconds"]},"DmarcPolicy":{"type":"string","enum":["none","quarantine","reject"]},"DnsStatus":{"type":"string","enum":["unknown","syncing","ready","error"]},"DmarcSetupState":{"type":"string","enum":["drifted","none","pending","setup","failed"]},"DmarcSetupFailureReason":{"type":"string","nullable":true,"enum":["nameservers_unresolvable","dmarc_record_missing","dmarc_record_added_as_txt_not_cname","dmarc_record_on_parent_only","dmarc_record_missing_palisade_rua","dmarc_record_invalid","dmarc_multiple_records"]},"DmarcSetupFailureDetail":{"type":"object","nullable":true,"properties":{"observed":{"type":"string"},"expected_rua":{"type":"string"},"from_hostname":{"type":"string"},"record_count":{"type":"number"}}},"ManagedDnsErrorReason":{"type":"string","nullable":true,"enum":["setup_timeout","drift","certificate_invalid","record_conflict","delegation_missing","publish_failed","dkim_key_error","check_failed"],"description":"Why managed_dns_status.bimi is \"error\". \"setup_timeout\" means the customer CNAME was never detected within the verification window (setup was never completed); \"drift\" means a previously verified CNAME stopped pointing at Palisade; \"record_conflict\" means default._bimi holds conflicting records — see dns_conflicts. Null when there is no error or the error predates reason tracking."},"DnsConflictProtocol":{"type":"string","enum":["dmarc","spf","bimi","mta_sts","dkim"]},"DnsConflictKind":{"type":"string","enum":["cname_with_other_data","multiple_dmarc_records","multiple_spf_records","multiple_bimi_records","cname_target_unresolvable","cname_chain_too_deep","authoritative_disagreement","resolver_divergence","managed_delegation_missing"]},"DnsConflict":{"type":"object","properties":{"label":{"type":"string","description":"The exact DNS name the conflicting records sit at."},"protocol":{"$ref":"#/components/schemas/DnsConflictProtocol"},"kind":{"$ref":"#/components/schemas/DnsConflictKind"},"summary":{"type":"string"},"rfc":{"type":"string","description":"The specification clause the zone violates, e.g. \"RFC 1034 §3.6.2\"."},"consequence":{"type":"string","description":"What this actually does to mail for this domain, in plain language."},"remediation":{"type":"string","description":"Which record to delete and which to keep, stated concretely enough to act on."},"conflicting_records":{"type":"array","items":{"type":"string"},"description":"The offending record values, verbatim as published."},"authoritative_nameservers":{"type":"array","items":{"type":"object","properties":{"nameserver":{"type":"string"},"ip":{"type":"string"},"cname":{"type":"array","items":{"type":"string"}},"txt":{"type":"array","items":{"type":"string"}},"answered":{"type":"boolean"},"timed_out":{"type":"boolean"}},"required":["nameserver","ip","cname","txt","answered","timed_out"]},"description":"Every authoritative nameserver queried and exactly what it returned."},"public_resolvers":{"type":"array","items":{"type":"object","properties":{"resolver":{"type":"string"},"ip":{"type":"string"},"cname":{"type":"array","items":{"type":"string"}},"txt":{"type":"array","items":{"type":"string"}},"answered":{"type":"boolean"},"timed_out":{"type":"boolean"}},"required":["resolver","ip","cname","txt","answered","timed_out"]},"description":"Independent public resolvers queried and exactly what each returned."},"occurrence_count":{"type":"number","description":"How many detection runs have observed this conflict."},"first_detected_at":{"type":"string","format":"date-time"},"last_detected_at":{"type":"string","format":"date-time"}},"required":["label","protocol","kind","summary","rfc","consequence","remediation","conflicting_records","authoritative_nameservers","public_resolvers","occurrence_count","first_detected_at","last_detected_at"]},"Group":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"external_customer_id":{"type":"string","nullable":true},"external_billing_entity_id":{"type":"string","nullable":true},"domains":{"type":"object","properties":{"count":{"type":"number"},"enforcement_pct":{"type":"number"},"last_14_days_volume":{"type":"number"},"last_14_days_dmarc_pass_pct":{"type":"number"},"average_score":{"type":"number"}},"required":["count","enforcement_pct","last_14_days_volume","last_14_days_dmarc_pass_pct","average_score"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","name","external_customer_id","external_billing_entity_id","created_at","updated_at"]},"TaskStatus":{"type":"string","enum":["open","snoozed","canceled","dismissed","pending","done"]},"TaskType":{"type":"string","enum":["cleanup_spf_record","confirm_source","lockdown_domain","reduce_spf_lookups","set_up_dmarc_record","source_failing_dkim","source_failing_spf","set_up_mx_record","set_up_spf_record","update_dmarc_record","update_ttl","set_up_mta_sts","set_up_tls_rpt","resolve_dns_conflict","review_unused_sender","cleanup_sender_records","fix_mx_certificate","renew_vmc_certificate","dkim_selector_not_imported","set_up_bimi"]},"TaskResolutionReason":{"type":"string","nullable":true,"enum":["verified","manual"],"description":"Why a done ticket closed. 'verified': Palisade's own check found the problem gone. 'manual': a person or an API caller marked it done and nothing was checked. Null on a ticket that is not done, and on one closed before the reason was first recorded. More values may be added, so treat an unknown one as unspecified."},"Task":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"domain_id":{"type":"string","format":"uuid"},"domain":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"dmarc_policy":{"type":"string"},"score":{"type":"number","minimum":0,"maximum":100,"description":"The domain audit score as it stands now."}},"required":["id","url","dmarc_policy","score"]},"score_gain":{"type":"integer","minimum":0,"description":"How much of the domain score completing this ticket wins back, by the same scoring the domain overview shows. 0 for ticket types the score does not measure. Present only with expand[]=domain, since it depends on the domain as it stands now."},"sending_domain":{"type":"string","nullable":true},"source_id":{"type":"string","nullable":true,"format":"uuid"},"source":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"homepage":{"type":"string","nullable":true,"format":"uri"},"documentation":{"type":"object","properties":{"spf":{"type":"string","nullable":true},"dkim":{"type":"string","nullable":true},"combined":{"type":"string","nullable":true}}},"category":{"type":"string","enum":["SENDING_TOOL","MAILBOX_PROVIDER","VPS_PROVIDER","SECURITY_PROVIDER","EMAIL_RELAY"]},"forwarding_type":{"type":"string","enum":["NOT_FORWARDER","FORWARDER","FORWARDER_ONLY"]},"spf_support":{"type":"string","enum":["NONE","RARE","FULL"]},"spf_supported_methods":{"type":"array","items":{"type":"string","enum":["DIRECT","SUBDOMAIN","MANAGED_SUBDOMAIN"]}},"spf_support_options":{"type":"object","properties":{"direct":{"type":"object","nullable":true,"properties":{"values":{"type":"array","items":{"type":"string"}},"requires_advanced_config":{"type":"boolean","nullable":true}},"required":["values","requires_advanced_config"]},"subdomain":{"type":"object","nullable":true,"properties":{"is_managed":{"type":"boolean"},"requires_user_selection":{"type":"boolean"},"path":{"type":"string","nullable":true},"value":{"type":"string","nullable":true},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"value":{"type":"string"}},"required":["type"]}}},"required":["is_managed","requires_user_selection","path","value","dns_records"]}},"required":["direct","subdomain"]},"dkim_support":{"type":"string","enum":["NONE","RARE","FULL"]},"dkim_discovery_selectors":{"type":"array","items":{"type":"string"}}},"required":["id","name","homepage","documentation","category","forwarding_type","spf_support","spf_supported_methods","spf_support_options","dkim_support","dkim_discovery_selectors"]},"instructions":{"type":"object","nullable":true,"properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"],"description":"BETA: Only expandable for GET /tasks/:id. Can contain instructions sometimes."},"source_alias":{"type":"string","nullable":true},"priority":{"type":"number"},"sequence":{"type":"number","description":"Where the task sits in the email-authentication rollout, lower first: 10 DMARC setup, 20 foundation records, 30 SPF/DKIM alignment, 40 cleanup, 50 enforcement, 60 hardening. Separate from priority, which is urgency; sequence orders tasks of equal priority, since an alignment task cannot be worked before DMARC reporting exists to show the failure."},"status":{"$ref":"#/components/schemas/TaskStatus"},"type":{"$ref":"#/components/schemas/TaskType"},"description":{"type":"string"},"snoozed_until":{"type":"string","nullable":true,"format":"date-time"},"marked_pending_at":{"type":"string","nullable":true,"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"resolved_at":{"type":"string","nullable":true,"format":"date-time"},"resolution_reason":{"$ref":"#/components/schemas/TaskResolutionReason"}},"required":["id","domain_id","sending_domain","source_id","source_alias","priority","sequence","status","type","description","snoozed_until","marked_pending_at","created_at","updated_at","resolved_at","resolution_reason"]},"Domain":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"dmarc_policy":{"type":"string"},"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":{"$ref":"#/components/schemas/DmarcPolicy"},"pct":{"type":"number","minimum":0,"maximum":100},"sp":{"type":"string","enum":["none","quarantine","reject","same"]},"strict_spf":{"type":"boolean"},"strict_dkim":{"type":"boolean"}},"required":["additional_emails","policy","pct","sp","strict_spf","strict_dkim"]},"spf_options":{"type":"object","properties":{"force_flattening":{"type":"boolean"},"enabled":{"type":"boolean"},"mechanism_descriptions":{"type":"object","additionalProperties":{"type":"string"}}},"required":["force_flattening","enabled"]},"managed_dns_status":{"type":"object","properties":{"dmarc":{"$ref":"#/components/schemas/DnsStatus"},"spf":{"$ref":"#/components/schemas/DnsStatus"},"dkim":{"$ref":"#/components/schemas/DnsStatus"},"bimi":{"$ref":"#/components/schemas/DnsStatus"},"mta_sts":{"$ref":"#/components/schemas/DnsStatus"}},"required":["dmarc","spf","dkim","bimi","mta_sts"]},"managed_dns_status_since":{"type":"object","properties":{"dmarc":{"type":"string","nullable":true,"format":"date-time"},"spf":{"type":"string","nullable":true,"format":"date-time"},"dkim":{"type":"string","nullable":true,"format":"date-time"},"bimi":{"type":"string","nullable":true,"format":"date-time"},"mta_sts":{"type":"string","nullable":true,"format":"date-time"}},"required":["dmarc","spf","dkim","bimi","mta_sts"],"description":"When each managed record last entered the \"syncing\" status. For dmarc, falls back to dmarc_setup_at when the transition predates tracking; other records are null when they have never synced or the transition predates tracking. Useful for estimating how long a \"syncing\" record has been waiting on DNS propagation."},"monitoring_status":{"$ref":"#/components/schemas/DmarcSetupState"},"monitoring_failure_reason":{"$ref":"#/components/schemas/DmarcSetupFailureReason"},"monitoring_failure_detail":{"$ref":"#/components/schemas/DmarcSetupFailureDetail"},"monitoring_email":{"type":"string"},"open_tasks_count":{"type":"number","minimum":0,"description":"Open tickets on this domain. Opportunity tickets such as set_up_bimi are not counted, and `expand[]=tasks` leaves them out to match."},"highest_open_task_priority":{"type":"integer","nullable":true,"minimum":0,"maximum":3,"description":"Priority of the most urgent open task on this domain, on the same inverted scale as a task's own priority: 0 critical, 1 high, 2 medium, 3 low. Null when the domain has no open tasks. Sort on \"severity\" to order domains by it."},"score":{"type":"number","minimum":0,"maximum":100},"score_band":{"type":"string","enum":["critical","bad","good","great"],"description":"Risk band the score falls in: critical, bad, good, or great. The same banding the dashboard shows, so clients do not have to reimplement the thresholds."},"is_parked":{"type":"boolean"},"is_dmarc_managed":{"type":"boolean"},"is_spf_managed":{"type":"boolean"},"is_dkim_managed":{"type":"boolean"},"is_bimi_managed":{"type":"boolean"},"is_mta_sts_managed":{"type":"boolean"},"bimi_options":{"type":"object","properties":{"enabled":{"type":"boolean"},"logo":{"type":"string","nullable":true,"format":"uri"},"certificate":{"type":"string","nullable":true,"format":"uri"}},"required":["enabled","logo","certificate"]},"bimi_error_reason":{"$ref":"#/components/schemas/ManagedDnsErrorReason"},"dmarc_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.dmarc is \"error\". \"record_conflict\" means _dmarc holds records that conflict with each other — a CNAME coexisting with other data, or more than one DMARC record — and the domain's DMARC policy is not reliably enforced. See dns_conflicts for the offending records. Null when there is no error or the error predates reason tracking."}]},"spf_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.spf is \"error\". See dmarc_error_reason for the meaning of each value."}]},"dkim_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.dkim is \"error\". See dmarc_error_reason for the meaning of each value."}]},"mta_sts_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.mta_sts is \"error\". See dmarc_error_reason for the meaning of each value."}]},"dns_conflicts":{"type":"array","items":{"$ref":"#/components/schemas/DnsConflict"},"description":"Unresolved record conflicts observed at labels Palisade manages or monitors for this domain. Empty for a healthy domain. Each entry quotes the offending records verbatim along with what every authoritative nameserver and public resolver returned, so it can be acted on without a second investigation."},"bimi_cname_target":{"type":"string","description":"The host a customer CNAMEs default._bimi.{domain} to for Palisade-served BIMI."},"bimi_txt_value":{"type":"string","nullable":true,"description":"The fully-formed BIMI TXT value, populated whenever bimi_options.logo is set (via asset ingestion or a direct bimi_options update). The a= tag is included only when a certificate URL is also set. Customers who prefer not to delegate via CNAME can publish this value at default._bimi.{domain} themselves."},"target_spf":{"type":"string","nullable":true},"last_14_days_volume":{"type":"number","minimum":0},"last_14_days_dmarc_pass_pct":{"type":"number","minimum":0,"maximum":100},"last_14_days_spf_pass_pct":{"type":"number","minimum":0,"maximum":100},"last_14_days_dkim_pass_pct":{"type":"number","minimum":0,"maximum":100},"group_id":{"type":"string","nullable":true,"format":"uuid"},"registrar":{"type":"string","nullable":true},"group":{"$ref":"#/components/schemas/Group"},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/Task"},"description":"Returns only tasks with status \"open\""},"dmarc_setup_at":{"type":"string","nullable":true,"format":"date-time"},"dmarc_verified_at":{"type":"string","nullable":true,"format":"date-time","description":"When the domain first reached the \"setup\" monitoring status and DMARC report collection began."},"monitoring_since":{"type":"string","nullable":true,"description":"The first day (yyyy-MM-dd) a DMARC aggregate report was actually received for this domain. The honest denominator for any activity claim: nothing before this day was observable, so a domain monitored for a week can never truthfully read as quiet for ninety days. Null until the first report arrives.","example":"2025-09-14"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","url","dmarc_policy","dmarc_options","spf_options","managed_dns_status","managed_dns_status_since","monitoring_status","monitoring_failure_reason","monitoring_failure_detail","monitoring_email","open_tasks_count","highest_open_task_priority","score","score_band","is_parked","is_dmarc_managed","is_spf_managed","is_dkim_managed","is_bimi_managed","is_mta_sts_managed","bimi_options","bimi_error_reason","dmarc_error_reason","spf_error_reason","dkim_error_reason","mta_sts_error_reason","dns_conflicts","bimi_cname_target","bimi_txt_value","target_spf","last_14_days_volume","last_14_days_dmarc_pass_pct","last_14_days_spf_pass_pct","last_14_days_dkim_pass_pct","group_id","registrar","dmarc_setup_at","dmarc_verified_at","monitoring_since","created_at","updated_at"]},"ApiVersion":{"type":"string","enum":["2025-04-30","unstable"]},"BimiAssetsResponse":{"type":"object","properties":{"certificate_url":{"type":"string","format":"uri"},"logo_url":{"type":"string","format":"uri"},"logotype_verified":{"type":"boolean","description":"The SVG hosted at logo_url was recovered from the certificate's logotype extension and matches the digest the certificate asserts. This covers the logo only — it says nothing about whether the certificate chain validates. Read chain_verified for that."},"svg_profile":{"type":"string","enum":["tiny-ps"]},"chain_verified":{"type":"boolean","description":"The hosted certificate chain was verified end to end: every signature checks out, no certificate in the path is expired, and the path terminates in a pinned BIMI trust anchor. Uploads that cannot reach this state are rejected, so a 200 response always carries true."},"chain_length":{"type":"integer","minimum":0,"exclusiveMinimum":true,"description":"How many certificates the hosted chain contains, counting the VMC leaf, intermediates and root."},"chain_repaired":{"type":"boolean","description":"True when the uploaded PEM was missing certificates and Palisade completed the chain (fetching the intermediate from the certificate's Authority Information Access endpoint and appending the pinned root) before hosting it. The hosted chain, not the uploaded one, is what certificate_url serves."},"trust_anchor":{"type":"string","description":"Common name of the pinned VMC root the hosted chain terminates in."},"san_verified":{"type":"boolean","description":"The leaf certificate's subjectAltName covers the domain the assets were attached to."},"not_after":{"type":"string","format":"date-time","description":"When the VMC leaf expires. VMCs are one-year certificates, so this is worth tracking."},"warnings":{"type":"array","items":{"type":"string"},"description":"Non-fatal observations about the certificate. \"vmc-mark-type-oid-missing\" means the leaf does not carry the VMC mark-type OIDs under 1.3.6.1.4.1.53087, which is certificate-authority dependent and does not block hosting."},"cert":{"type":"object","properties":{"serial":{"type":"string"},"not_after":{"type":"string","format":"date-time"},"common_name":{"type":"string","nullable":true}},"required":["serial","not_after","common_name"]}},"required":["certificate_url","logo_url","logotype_verified","svg_profile","chain_verified","chain_length","chain_repaired","trust_anchor","san_verified","not_after","warnings","cert"]},"IngestBimiAssetsMultipart":{"type":"object","properties":{"certificate":{"type":"string","description":"The VMC in PEM format.","format":"binary"}}},"IngestBimiAssetsBody":{"type":"object","properties":{"certificate_pem":{"type":"string","minLength":1,"description":"The VMC as a PEM string. Alternative to the multipart certificate file part."},"certificate_url":{"type":"string","format":"uri","description":"An https URL to fetch the VMC PEM from. Alternative to the multipart certificate file part."}}},"DnsRecordPurpose":{"type":"string","enum":["dmarc","spf","dkim","mta_sts","mta_sts_policy","tls_rpt","bimi"]},"DnsRecordType":{"type":"string","enum":["TXT","CNAME","NS"]},"DnsRecordAction":{"type":"string","enum":["create","replace","delete"],"description":"How to publish this record, derived from what is currently live in DNS. \"create\" means nothing serving this purpose exists at the host yet; \"replace\" means a record for this purpose already occupies the host and must be overwritten with this value; \"delete\" means the record must be removed. When already_published is true the record is already correct and \"replace\" would be a no-op, so use already_published to decide whether to act at all."},"DnsRecordStatus":{"type":"string","enum":["pending","verifying","verified","drifted"]},"DnsRecord":{"type":"object","properties":{"purpose":{"$ref":"#/components/schemas/DnsRecordPurpose"},"type":{"$ref":"#/components/schemas/DnsRecordType"},"host":{"type":"string","description":"Fully qualified record name."},"relative_host":{"type":"string","description":"Record name relative to the domain apex; \"@\" designates the apex itself."},"value":{"type":"string"},"ttl":{"type":"integer","minimum":0,"exclusiveMinimum":true,"description":"Recommended TTL in seconds."},"action":{"$ref":"#/components/schemas/DnsRecordAction"},"required":{"type":"boolean","description":"False for records that improve an enabled feature but are not needed for it to function."},"status":{"$ref":"#/components/schemas/DnsRecordStatus"},"already_published":{"type":"boolean","nullable":true,"description":"True when live DNS already serves exactly this value, so the record can be skipped. False when it is missing or serving something else. Null when the live lookup could not be completed, in which case \"action\" falls back to a static, type-derived value and should be treated as a best-effort hint."}},"required":["purpose","type","host","relative_host","value","ttl","action","required","status","already_published"]},"DnsRecordWarning":{"type":"object","properties":{"code":{"type":"string","description":"Stable identifier for the warning, safe to branch on."},"message":{"type":"string","description":"Human-readable explanation of the risk."}},"required":["code","message"]},"DetectedDnsProvider":{"type":"object","properties":{"id":{"type":"string","nullable":true,"description":"Identifier of the DNS provider hosting the domain, derived from its live NS records. Deliberately an open set: ids recognized today are cloudflare, route53, godaddy, azure, google, namecheap, digitalocean, vercel, dnsimple, gandi, ovh, name.com, porkbun, hetzner, linode, networksolutions, microsoft365, wix, squarespace, ionos, hostinger, siteground, bluehost, hostgator, dreamhost, dnsmadeeasy, akamai, rackspace, cloudns, comodo, easydns, encirca, webnames, whc, rebel, register.com, hover, namesilo, dynadot, spaceship, one.com, enom, wordpress.com, hurricane-electric, and more are added over time, so treat an unfamiliar id the same as null rather than rejecting the response. Null when the nameservers do not match a known provider, disagree with each other, or could not be resolved — in that case use \"nameservers\" to decide, and never assume a provider.","example":"cloudflare"},"nameservers":{"type":"array","items":{"type":"string"},"description":"The nameservers the domain is delegated to. Empty when the lookup failed or returned nothing."}},"required":["id","nameservers"],"description":"Where the domain's DNS is hosted, so the records can be published with a matching DNS tool, connector, or provider API. This is the DNS host, which is often not the registrar reported in \"registrar\"."},"DnsRecordsResponse":{"type":"object","properties":{"records":{"type":"array","items":{"$ref":"#/components/schemas/DnsRecord"}},"all_verified":{"type":"boolean","description":"True when every required record has status \"verified\"."},"warnings":{"type":"array","items":{"$ref":"#/components/schemas/DnsRecordWarning"},"description":"Risks to resolve before publishing these records. Empty in the normal case. Read these before applying anything: publishing an enforcing DMARC policy on a domain with no working SPF or DKIM causes receivers to reject or quarantine its mail."},"dns_provider":{"$ref":"#/components/schemas/DetectedDnsProvider"}},"required":["records","all_verified","warnings","dns_provider"]},"VerifyResults":{"type":"object","properties":{"dmarc":{"type":"boolean"},"spf":{"type":"boolean"},"dkim":{"type":"boolean"},"mta_sts":{"type":"boolean"},"bimi":{"type":"boolean"}},"description":"Outcome of the synchronous verification pass, keyed by the same purpose slugs as GET /domains/{id}/dns-records. A purpose is present only when it was confirmed during this pass; absence means it was not confirmed (it may already be verified, or still propagating). tls_rpt and mta_sts_policy are not covered by the synchronous pass and never appear."},"VerifyDomainResponse":{"type":"object","properties":{"queued":{"type":"boolean"},"verified":{"type":"object","additionalProperties":{"type":"boolean"}},"results":{"$ref":"#/components/schemas/VerifyResults"}},"required":["queued","verified","results"]},"EnableHostedDmarcResponse":{"type":"object","properties":{"hosted_dmarc_active":{"type":"boolean","description":"False until the hosted CNAME resolves in live DNS. Publish \"record\" and Palisade flips this to true on its own; true already on a domain that was hosted before the call."},"record":{"allOf":[{"$ref":"#/components/schemas/DnsRecord"},{"description":"The CNAME to publish at _dmarc.<domain>, rebuilt from the domain's current DMARC policy. Delete any DMARC TXT record still sitting at the same host, which conflicts with it."}]},"domain":{"$ref":"#/components/schemas/Domain"}},"required":["hosted_dmarc_active","record","domain"]},"PlanStageStatus":{"type":"string","enum":["completed","current","pending"],"description":"\"completed\" when the stage is done, \"current\" for the one being worked, \"pending\" for one not started."},"PlanStepStatus":{"type":"string","enum":["in_progress","upcoming"],"description":"\"in_progress\" when Palisade has already opened tasks for this step, listed in task_ids. \"upcoming\" when it has not, meaning there is nothing to action yet."},"PlanStep":{"type":"object","properties":{"key":{"type":"string","description":"Stable identifier for this step, safe to branch on - for example \"dmarc-setup\", \"alignment-spf\" or \"dmarc-reject-full\". Steps are added over time; existing keys keep their meaning."},"task_type":{"type":"string","nullable":true,"enum":["cleanup_spf_record","confirm_source","lockdown_domain","reduce_spf_lookups","set_up_dmarc_record","source_failing_dkim","source_failing_spf","set_up_mx_record","set_up_spf_record","update_dmarc_record","update_ttl","set_up_mta_sts","set_up_tls_rpt","resolve_dns_conflict","review_unused_sender","cleanup_sender_records","fix_mx_certificate","renew_vmc_certificate","dkim_selector_not_imported","set_up_bimi"],"description":"The type of task this step opens as, matching \"type\" on the Task object. Null for a step that is never a task, such as ongoing monitoring."},"title":{"type":"string","description":"What the step asks for, in plain language."},"description":{"type":"string","description":"Why the step matters."},"priority":{"type":"integer","description":"Severity on the same scale as a task: 0 critical, 1 high, 2 medium, 3 low. When tasks already exist for this step, this is the most severe of theirs."},"score_gain":{"type":"integer","description":"How many points of the domain's audit score completing this step would win back. 0 when none."},"status":{"$ref":"#/components/schemas/PlanStepStatus"},"task_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Open and pending tasks that carry out this step, readable through GET /tasks/{id}. Empty for an upcoming step. A step covering sending sources has one task per source, so this can hold several."}},"required":["key","task_type","title","description","priority","score_gain","status","task_ids"]},"PlanOptionalStep":{"type":"object","properties":{"key":{"type":"string","description":"Stable identifier for the optional step, for example \"enable-hosted-spf\"."},"title":{"type":"string"},"description":{"type":"string"},"recommended":{"type":"boolean","description":"True when Palisade actively suggests it for this domain right now."}},"required":["key","title","description","recommended"]},"PlanStage":{"type":"object","properties":{"number":{"type":"integer","description":"Position in the journey, 1 through 4."},"key":{"type":"string","description":"Stable identifier: \"setup\", \"alignment\", \"enforcement\" or \"monitoring\"."},"title":{"type":"string"},"description":{"type":"string"},"status":{"$ref":"#/components/schemas/PlanStageStatus"},"steps":{"type":"array","items":{"$ref":"#/components/schemas/PlanStep"},"description":"The work this stage is made of. Empty for a parked domain."},"optional_steps":{"type":"array","items":{"$ref":"#/components/schemas/PlanOptionalStep"},"description":"Improvements worth making at this stage that Palisade does not require."}},"required":["number","key","title","description","status","steps","optional_steps"]},"DomainPlan":{"type":"object","properties":{"scan_findings":{"type":"object","nullable":true,"properties":{"scanned_at":{"type":"string","format":"date-time"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"detail":{"type":"string"},"code":{"type":"string"},"status":{"type":"string","enum":["open","verifying","snoozed","ticket_completed","upcoming","waiting","optional","review"]},"guidance":{"type":"string"},"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"}},"required":["id","status"]}},"task_types":{"type":"array","items":{"type":"string","enum":["cleanup_spf_record","confirm_source","lockdown_domain","reduce_spf_lookups","set_up_dmarc_record","source_failing_dkim","source_failing_spf","set_up_mx_record","set_up_spf_record","update_dmarc_record","update_ttl","set_up_mta_sts","set_up_tls_rpt","resolve_dns_conflict","review_unused_sender","cleanup_sender_records","fix_mx_certificate","renew_vmc_certificate","dkim_selector_not_imported","set_up_bimi"]},"description":"The task types that would carry this finding out, matching \"type\" on the Task object and \"task_type\" on a plan step. A type listed here is not always opened: update_ttl and set_up_tls_rpt, for example, never are. Empty when no task type covers the finding, such as BIMI or a DKIM problem with one selector."}},"required":["id","title","detail","code","status","guidance","tasks","task_types"]}}},"required":["scanned_at","items"],"description":"Saved onboarding findings, including those without an actionable ticket. Not a fresh DNS audit."},"domain_id":{"type":"string","format":"uuid"},"domain":{"type":"string","description":"The domain this plan is for."},"is_parked":{"type":"boolean","description":"True when the domain is parked. A parked domain is monitored but has no journey to work through."},"agent_proposals_enabled":{"type":"boolean","description":"False when the plan is not being worked because the organization is on a plan without the agent. upcoming_steps is empty in that case, because Palisade will not open those tasks."},"current_stage":{"type":"integer","nullable":true,"description":"The number of the stage being worked now, or null when none is current."},"stages":{"type":"array","items":{"$ref":"#/components/schemas/PlanStage"},"description":"The four stages of the DMARC journey, in order."},"upcoming_steps":{"type":"array","items":{"$ref":"#/components/schemas/PlanStep"},"description":"The steps Palisade expects to work next: the next stage's steps, when that stage has not started. These are a subset of stages[].steps, always with status \"upcoming\", and are what the app shows below the open tasks. Empty for a parked domain, when the agent is not proposing work, and once the journey is finished."}},"required":["domain_id","domain","is_parked","agent_proposals_enabled","current_stage","stages","upcoming_steps"]},"DomainSource":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"is_known":{"type":"boolean"},"source_id":{"type":"string","nullable":true,"description":"The catalogue source this sender was matched to, or null when it is not one Palisade recognises. Lets a caller line a sender up with the SPF include or DKIM key it belongs to."},"spf_value":{"type":"string","nullable":true,"description":"The SPF mechanism discovery observed for this sender, when it saw one."},"source_name":{"type":"string","nullable":true},"source_homepage":{"type":"string","nullable":true},"sending_domain":{"type":"string","nullable":true},"dkim_selectors":{"type":"array","items":{"type":"string"}},"confirmation_status":{"type":"string","enum":["confirmed","discarded","requires_confirmation"]},"confirmed_at":{"type":"string","nullable":true,"format":"date-time"},"last_seen_at":{"type":"string","nullable":true,"description":"The last day (YYYY-MM-DD) this sender was seen sending for the domain, across all recorded history rather than a recent window, so a sender silent for a year still reports the day it went quiet. Null when no sending has been recorded for it yet. Reported from daily aggregates that can trail live reporting by a few days, so treat it as a date rather than a timestamp. Only counts sending observed since the domain was added to Palisade.","example":"2025-09-14"},"last_spf_aligned_at":{"type":"string","nullable":true,"description":"The last day (YYYY-MM-DD) mail from this sender passed aligned SPF for the domain. Distinct from last_seen_at: a sender that still sends but no longer passes SPF has an SPF problem, not a disused include. Null when it has never passed aligned SPF in recorded history.","example":"2025-09-14"},"totals":{"type":"object","properties":{"compliant":{"type":"number"},"non_compliant":{"type":"number"},"forwarded":{"type":"number"},"unknown":{"type":"number"}},"required":["compliant","non_compliant","forwarded","unknown"]}},"required":["id","is_known","source_id","spf_value","source_name","source_homepage","sending_domain","dkim_selectors","confirmation_status","confirmed_at","last_seen_at","last_spf_aligned_at","totals"]},"DkimKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"selector":{"type":"string"},"record_type":{"type":"string","enum":["TXT","CNAME"]},"value":{"type":"string"},"dns_status":{"$ref":"#/components/schemas/DnsStatus"},"publish_error":{"type":"string","nullable":true,"description":"Why the last publish attempt failed, in terms a customer can act on. Null unless dns_status is error; cleared by the next successful publish."},"ttl":{"type":"number","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"last_published_at":{"type":"string","nullable":true,"format":"date-time"},"deleted_at":{"type":"string","nullable":true,"format":"date-time"},"created_by":{"type":"string","nullable":true}},"required":["id","selector","record_type","value","dns_status","publish_error","ttl","created_at","updated_at","last_published_at","deleted_at","created_by"]},"DmarcProbeTask":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Task id. Read it with GET /tasks/{id}?expand[]=instructions for the fix Palisade has worked out."},"type":{"type":"string","enum":["cleanup_spf_record","confirm_source","lockdown_domain","reduce_spf_lookups","set_up_dmarc_record","source_failing_dkim","source_failing_spf","set_up_mx_record","set_up_spf_record","update_dmarc_record","update_ttl","set_up_mta_sts","set_up_tls_rpt","resolve_dns_conflict","review_unused_sender","cleanup_sender_records","fix_mx_certificate","renew_vmc_certificate","dkim_selector_not_imported","set_up_bimi"],"description":"What kind of problem the task describes."},"priority":{"type":"integer","description":"Lower is more urgent. 0 is the most urgent."},"description":{"type":"string","description":"What is wrong, in plain language."},"source_alias":{"type":"string","nullable":true,"description":"The sending source this task covers. Matches senders[].source_alias."},"sending_domain":{"type":"string","nullable":true,"description":"The sending domain the task is about, when it has one."}},"required":["id","type","priority","description","source_alias","sending_domain"],"description":"An open remediation task for this domain."},"DmarcSummary":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid","description":"Id of the domain the summary is for."},"domain":{"type":"string","description":"The domain name the summary is for."},"date_from":{"type":"string","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","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":"integer","description":"Total messages reported in the window, across every compliance bucket."},"dmarc_pass_pct":{"type":"number","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","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","description":"Share of volume that passed DKIM, 0-100, rounded to two decimals, and 0 when the window has no volume."},"totals":{"type":"object","properties":{"compliant":{"type":"integer","description":"Messages that passed DMARC alignment."},"non_compliant":{"type":"integer","description":"Messages that failed DMARC. This is the number to drive to zero."},"forwarded":{"type":"integer","description":"Messages the compliance categorizer attributed to forwarding."},"unknown":{"type":"integer","description":"Messages from a sending source Palisade could not identify."},"undetermined":{"type":"integer","description":"Messages whose compliance is undetermined, including reports the categorizer has not processed yet."}},"required":["compliant","non_compliant","forwarded","unknown","undetermined"],"description":"Message counts by compliance bucket. They sum to volume."},"previous_period":{"type":"object","properties":{"date_from":{"type":"string","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","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":"integer","description":"Total messages reported in the window, across every compliance bucket."},"dmarc_pass_pct":{"type":"number","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","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","description":"Share of volume that passed DKIM, 0-100, rounded to two decimals, and 0 when the window has no volume."}},"required":["date_from","date_to","volume","dmarc_pass_pct","spf_pass_pct","dkim_pass_pct"],"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":{"type":"object","properties":{"volume":{"type":"integer","description":"Change in message volume against the previous window."},"dmarc_pass_pct":{"type":"number","description":"Change in DMARC pass rate, in percentage points, rounded to two decimals."},"spf_pass_pct":{"type":"number","description":"Change in SPF pass rate, in percentage points, rounded to two decimals."},"dkim_pass_pct":{"type":"number","description":"Change in DKIM pass rate, in percentage points, rounded to two decimals."}},"required":["volume","dmarc_pass_pct","spf_pass_pct","dkim_pass_pct"],"description":"This window minus the previous one. Positive means the metric improved (or grew, for volume)."},"last_14_days":{"type":"object","properties":{"volume":{"type":"integer","description":"Messages counted over the last 14 days, as stored on the domain."},"dmarc_pass_pct":{"type":"number","description":"Share of that volume that passed DMARC, 0-100."},"spf_pass_pct":{"type":"number","description":"Share of that volume that passed SPF, 0-100."},"dkim_pass_pct":{"type":"number","description":"Share of that volume that passed DKIM, 0-100."}},"required":["volume","dmarc_pass_pct","spf_pass_pct","dkim_pass_pct"],"description":"The stored 14-day figures GET /domains/{id} reports. These 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","description":"The domain's DMARC policy state, in the same form GET /domains/{id} reports."},"monitoring_status":{"type":"string","description":"Monitoring state of the domain, in the same form GET /domains/{id} reports."},"policy_readiness":{"type":"object","properties":{"status":{"type":"string","enum":["advance_recommended","hold","at_maximum"],"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","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","nullable":true,"format":"uuid","description":"The UPDATE_DMARC_RECORD task carrying the recommendation, when Palisade has opened one."}},"required":["status","reason","open_task_id"],"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":{"type":"array","items":{"$ref":"#/components/schemas/DmarcProbeTask"},"description":"Open and pending remediation tasks for this domain, most urgent first, capped at 10."},"open_tasks_count":{"type":"integer","description":"Total open and pending tasks, which may exceed the array length."},"next_step":{"type":"string","description":"The single action Palisade suggests taking next."}},"required":["domain_id","domain","date_from","date_to","volume","dmarc_pass_pct","spf_pass_pct","dkim_pass_pct","totals","previous_period","change","last_14_days","dmarc_policy","monitoring_status","policy_readiness","open_tasks","open_tasks_count","next_step"]},"DmarcSender":{"type":"object","properties":{"source_alias":{"type":"string","description":"Stable identifier for the sending source: a Palisade source id when the sender is recognised, otherwise the sending base domain, or \"unknown\". This is the key that links a sender to a task."},"name":{"type":"string","description":"Display name when the source is recognised, otherwise source_alias."},"source_id":{"type":"string","nullable":true,"format":"uuid"},"is_known":{"type":"boolean","description":"False when DMARC reports show this sender but Palisade cannot identify it."},"sending_domains":{"type":"array","items":{"type":"string"},"description":"The header-from domains this source sent as, highest volume first, capped at 5."},"volume":{"type":"integer"},"volume_share_pct":{"type":"number","description":"Share of the domain's total windowed volume this sender accounts for."},"dmarc_pass_pct":{"type":"number"},"spf_pass_pct":{"type":"number"},"dkim_pass_pct":{"type":"number"},"totals":{"type":"object","properties":{"compliant":{"type":"integer","description":"Messages that passed DMARC alignment."},"non_compliant":{"type":"integer","description":"Messages that failed DMARC. This is the number to drive to zero."},"forwarded":{"type":"integer","description":"Messages the compliance categorizer attributed to forwarding."},"unknown":{"type":"integer","description":"Messages from a sending source Palisade could not identify."},"undetermined":{"type":"integer","description":"Messages whose compliance is undetermined, including reports the categorizer has not processed yet."}},"required":["compliant","non_compliant","forwarded","unknown","undetermined"],"description":"Message counts by compliance bucket. They sum to volume."},"is_failing":{"type":"boolean","description":"True when the sender has non-compliant volume in the window."},"open_task_id":{"type":"string","nullable":true,"format":"uuid","description":"An open or pending remediation task covering this sender, when one exists. Call GET /tasks/{id}?expand[]=instructions for the fix rather than improvising one."},"open_task_type":{"type":"string","nullable":true,"enum":["cleanup_spf_record","confirm_source","lockdown_domain","reduce_spf_lookups","set_up_dmarc_record","source_failing_dkim","source_failing_spf","set_up_mx_record","set_up_spf_record","update_dmarc_record","update_ttl","set_up_mta_sts","set_up_tls_rpt","resolve_dns_conflict","review_unused_sender","cleanup_sender_records","fix_mx_certificate","renew_vmc_certificate","dkim_selector_not_imported","set_up_bimi"]}},"required":["source_alias","name","source_id","is_known","sending_domains","volume","volume_share_pct","dmarc_pass_pct","spf_pass_pct","dkim_pass_pct","totals","is_failing","open_task_id","open_task_type"]},"DmarcSenders":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid"},"domain":{"type":"string"},"date_from":{"type":"string"},"date_to":{"type":"string"},"volume":{"type":"integer","description":"Total windowed volume across every sender, not just the ones returned."},"total_senders":{"type":"integer","description":"Distinct senders seen in the window."},"truncated":{"type":"boolean","description":"True when total_senders exceeds the returned list."},"senders":{"type":"array","items":{"$ref":"#/components/schemas/DmarcSender"}},"next_step":{"type":"string"}},"required":["domain_id","domain","date_from","date_to","volume","total_senders","truncated","senders","next_step"]},"DnsSnapshot":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"spf":{"type":"array","items":{"type":"string"}},"dmarc":{"type":"array","items":{"type":"string"}},"bimi":{"type":"array","items":{"type":"string"}},"mx":{"type":"array","items":{"type":"object","properties":{"exchange":{"type":"string"},"priority":{"type":"number"}},"required":["exchange","priority"]}},"dkim":{"type":"array","items":{"type":"object","properties":{"selector":{"type":"string"},"value":{"type":"string"}},"required":["selector","value"]}},"ns":{"type":"array","items":{"type":"string"}},"mta_sts":{"type":"array","items":{"type":"string"}},"tls_rpt":{"type":"array","items":{"type":"string"}},"records_updated":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","format":"date-time"},"diff":{"type":"object","nullable":true,"properties":{"spf":{"type":"array","items":{"type":"object","properties":{"old":{"type":"string","nullable":true},"new":{"type":"string","nullable":true}},"required":["old","new"]}},"dmarc":{"type":"array","items":{"type":"object","properties":{"old":{"type":"string","nullable":true},"new":{"type":"string","nullable":true}},"required":["old","new"]}},"bimi":{"type":"array","items":{"type":"object","properties":{"old":{"type":"string","nullable":true},"new":{"type":"string","nullable":true}},"required":["old","new"]}},"mx":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","nullable":true,"properties":{"exchange":{"type":"string"},"priority":{"type":"number"}},"required":["exchange","priority"]},"new":{"type":"object","nullable":true,"properties":{"exchange":{"type":"string"},"priority":{"type":"number"}},"required":["exchange","priority"]}},"required":["old","new"]}},"dkim":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","nullable":true,"properties":{"selector":{"type":"string"},"value":{"type":"string"}},"required":["selector","value"]},"new":{"type":"object","nullable":true,"properties":{"selector":{"type":"string"},"value":{"type":"string"}},"required":["selector","value"]}},"required":["old","new"]}},"ns":{"type":"array","items":{"type":"object","properties":{"old":{"type":"string","nullable":true},"new":{"type":"string","nullable":true}},"required":["old","new"]}},"mta_sts":{"type":"array","items":{"type":"object","properties":{"old":{"type":"string","nullable":true},"new":{"type":"string","nullable":true}},"required":["old","new"]}},"tls_rpt":{"type":"array","items":{"type":"object","properties":{"old":{"type":"string","nullable":true},"new":{"type":"string","nullable":true}},"required":["old","new"]}}}}},"required":["id","spf","dmarc","bimi","mx","dkim","ns","mta_sts","tls_rpt","records_updated","created_at"]},"Invitation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"invitee":{"type":"object","properties":{"email":{"type":"string","format":"email"},"roles":{"type":"array","items":{"type":"string","enum":["admin","editor","viewer"]}},"groups":{"type":"array","items":{"type":"string","format":"uuid"},"default":[]}},"required":["email","roles"]},"inviter_name":{"type":"string"},"created_at":{"type":"string"},"expires_at":{"type":"string"},"url":{"type":"string","format":"uri"}},"required":["id","invitee","inviter_name","created_at","expires_at","url"]},"InvitationDomainNotAllowed":{"type":"object","properties":{"code":{"type":"string","enum":["invitation_domain_not_allowed"]},"message":{"type":"string"},"details":{"type":"object","properties":{"allowed_domains":{"type":"array","items":{"type":"string"},"description":"Email domains of existing members on this organization."},"rejected_domain":{"type":"string"}},"required":["allowed_domains","rejected_domain"]}},"required":["code","message","details"]},"InvitationInviteeExists":{"type":"object","properties":{"code":{"type":"string","enum":["invitation_invitee_exists"]},"message":{"type":"string"},"details":{"type":"object","properties":{"email":{"type":"string","format":"email"}},"required":["email"]}},"required":["code","message","details"]},"InvitationUpstreamFailure":{"type":"object","properties":{"code":{"type":"string","enum":["upstream_failure"]},"message":{"type":"string"}},"required":["code","message"]},"SupportProvider":{"type":"string","enum":["palisade","sherweb"]},"OrganizationInvitationFailure":{"type":"object","properties":{"code":{"type":"string","enum":["upstream_failure"]},"message":{"type":"string"}},"required":["code","message"],"description":"Present only when invite_email was given and the identity provider returned an error while creating that invitation. The organization itself was created. The message says whether the invitation was created anyway, was not created, or could not be checked; list the organization's invitations before inviting the address again, since a retry creates a second invitation."},"Organization":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"logo_url":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone_number":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"is_active":{"type":"boolean"},"require_mfa":{"type":"boolean","description":"Whether every member must complete multi-factor authentication to sign in to this organization. A member who has not set up an authenticator is asked to at their next sign-in."},"settings":{"type":"object","nullable":true,"properties":{"email_notifications":{"type":"object","properties":{"domain_drifted":{"type":"boolean","default":true,"description":"Send notifications when domain DNS records drift from expected configuration."},"domain_failed":{"type":"boolean","default":true,"description":"Send notifications when a domain fails DMARC validation checks."},"domain_setup":{"type":"boolean","default":true,"description":"Send notifications when a domain DMARC configuration is successfully set up."},"tasks_digests":{"type":"boolean","default":true,"description":"Send daily digest of new and pending tasks."},"setup_reminders":{"type":"boolean","default":true,"description":"Send reminders to finish adding and verifying a first domain."},"first_report":{"type":"boolean","default":true,"description":"Send an email when a newly set-up domain's first DMARC data arrives, and its first report a week later."},"invitation_reminders":{"type":"boolean","default":true,"description":"Remind invitees about an unaccepted invitation, and tell the inviter which invitations are about to expire."},"billing_notices":{"type":"boolean","default":true,"description":"Send plan-usage notices, such as passing the Free plan monthly email allowance."},"hosted_spf_live":{"type":"boolean","default":true,"description":"Send a one-time email when a domain's Hosted SPF setup finishes and starts serving."},"delegation_missing":{"type":"boolean","default":true,"description":"Send a notification when a domain never delegated to a record Palisade hosts for it, so the hosted record is not serving. Controlled separately from domain_drifted."}},"description":"Email notification preferences for the organization. Controls which types of emails are sent."},"onboarding_dismissed_at":{"type":"string","nullable":true,"format":"date-time","description":"When someone dismissed or finished the onboarding wizard for this organization. Absent or null means nobody has, and the wizard is still offered to an organization with no domains."}}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"support_provider":{"$ref":"#/components/schemas/SupportProvider"},"has_domains":{"type":"boolean"}},"required":["id","name","logo_url","email","phone_number","website","is_active","require_mfa","created_at","updated_at","support_provider"]},"CreatedOrganization":{"allOf":[{"$ref":"#/components/schemas/Organization"},{"type":"object","properties":{"invitation_failure":{"$ref":"#/components/schemas/OrganizationInvitationFailure"}}}]},"LineItem":{"type":"object","properties":{"sku":{"type":"string","enum":["DOMAIN-ACTIVE-TRIAL","DOMAIN-ACTIVE-FREE","DOMAIN-ACTIVE-1TO10","DOMAIN-ACTIVE-11TO100","DOMAIN-ACTIVE-101TO1000","DOMAIN-ACTIVE-1001PLUS","DOMAIN-INACTIVE","DOMAIN-PARKED","AGENT-ADDON"]},"quantity":{"type":"number"}},"required":["sku","quantity"]},"Usage":{"type":"object","properties":{"active_domains":{"type":"number"},"inactive_domains":{"type":"number"},"parked_domains":{"type":"number"},"deleted_domains":{"type":"number"},"domains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"is_active":{"type":"boolean"},"is_parked":{"type":"boolean"},"organization_id":{"type":"string","format":"uuid"}},"required":["id","url","is_active","is_parked","organization_id"]}},"organizations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"active_domains":{"type":"number"},"inactive_domains":{"type":"number"},"parked_domains":{"type":"number"},"is_trialing":{"type":"boolean"},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/LineItem"}}},"required":["id","name","active_domains","inactive_domains","parked_domains","is_trialing"]}},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/LineItem"}},"aligned_messages":{"type":"integer","minimum":0,"description":"How many messages authenticated as the organization's own mail (SPF- or DKIM-aligned) were reported over the requested range, across the organization and any active children. This is the volume an IT teams tier is sold against. Returned only when requested through expand, since it aggregates the DMARC report table."}},"required":["active_domains","inactive_domains","parked_domains","deleted_domains"]},"MemberRole":{"type":"string","enum":["admin","editor","viewer"]},"Member":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid"},"email":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"picture":{"type":"string","nullable":true,"format":"uri"},"login_method":{"type":"string","enum":["email_password","google","microsoft"]},"restricted_to_groups":{"type":"boolean"},"groups":{"type":"array","items":{"type":"string","format":"uuid"}},"roles":{"type":"array","items":{"$ref":"#/components/schemas/MemberRole"}}},"required":["user_id","email","name","picture","login_method","restricted_to_groups","groups","roles"]},"MemberAccountNotFound":{"type":"object","properties":{"code":{"type":"string","enum":["parameter_invalid_or_missing"]},"param":{"type":"string","enum":["email"]},"message":{"type":"string"}},"required":["code","param","message"]},"BulkMfaStatus":{"type":"object","properties":{"is_enrolled":{"type":"boolean","nullable":true}},"required":["is_enrolled"]},"GetMembersMfaStatusesResponse":{"type":"object","properties":{"statuses":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/BulkMfaStatus"}}},"required":["statuses"]},"MtaSts":{"type":"object","properties":{"is_managed":{"type":"boolean"},"status":{"allOf":[{"$ref":"#/components/schemas/DnsStatus"},{"nullable":true}]},"mode":{"type":"string","nullable":true,"enum":["testing","enforce","none"]},"max_age":{"type":"number","nullable":true},"mx":{"type":"array","nullable":true,"items":{"type":"string"}},"policy_id":{"type":"string","nullable":true},"custom_hostname_id":{"type":"string","nullable":true},"cname_target":{"type":"string"},"txt_cname_target":{"type":"string"},"tls_reporting_email":{"type":"string","nullable":true},"tls_rpt_record":{"type":"string","nullable":true}},"required":["is_managed","status","mode","max_age","mx","policy_id","custom_hostname_id","cname_target","txt_cname_target","tls_reporting_email","tls_rpt_record"]},"ReportSenderVolume":{"type":"object","properties":{"name":{"type":"string"},"volume_pass":{"type":"integer"},"volume_fail":{"type":"integer"}},"required":["name","volume_pass","volume_fail"]},"ReportSummary":{"type":"object","properties":{"date_from":{"type":"string"},"date_to":{"type":"string"},"total_domains":{"type":"integer"},"managed_domains":{"type":"integer"},"monitored_domains":{"type":"integer"},"avg_last_14_days_pass_rate":{"type":"number"},"senders":{"type":"array","items":{"$ref":"#/components/schemas/ReportSenderVolume"}}},"required":["date_from","date_to","total_domains","managed_domains","monitored_domains","avg_last_14_days_pass_rate","senders"]},"ReportDomain":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"score":{"type":"integer"},"score_band":{"type":"string","enum":["critical","bad","good","great"]},"policy":{"type":"string"},"is_dmarc_managed":{"type":"boolean"},"last_14_days_volume":{"type":"integer"},"last_14_days_pass_rate_dmarc":{"type":"number"},"last_14_days_pass_rate_spf":{"type":"number"},"last_14_days_pass_rate_dkim":{"type":"number"},"threats_count":{"type":"integer"}},"required":["id","url","score","score_band","policy","is_dmarc_managed","last_14_days_volume","last_14_days_pass_rate_dmarc","last_14_days_pass_rate_spf","last_14_days_pass_rate_dkim","threats_count"]},"ReportSenderAggregate":{"type":"object","properties":{"name":{"type":"string"},"message_count":{"type":"integer"},"dmarc_passed":{"type":"integer"},"dmarc_failed":{"type":"integer"},"spf_passed":{"type":"integer"},"dkim_passed":{"type":"integer"}},"required":["name","message_count","dmarc_passed","dmarc_failed","spf_passed","dkim_passed"]},"ReportDomainSenders":{"type":"object","properties":{"date_from":{"type":"string"},"date_to":{"type":"string"},"senders":{"type":"array","items":{"$ref":"#/components/schemas/ReportSenderAggregate"}}},"required":["date_from","date_to","senders"]},"ReportTimeseriesPoint":{"type":"object","properties":{"date":{"type":"string"},"message_count":{"type":"integer"},"compliant":{"type":"integer"},"non_compliant":{"type":"integer"},"forwarded":{"type":"integer"},"unknown":{"type":"integer"},"undetermined":{"type":"integer"}},"required":["date","message_count","compliant","non_compliant","forwarded","unknown","undetermined"]},"ReportDomainTimeseries":{"type":"object","properties":{"date_from":{"type":"string"},"date_to":{"type":"string"},"series":{"type":"array","items":{"$ref":"#/components/schemas/ReportTimeseriesPoint"}}},"required":["date_from","date_to","series"]},"ProspectingReportGone":{"type":"object","properties":{"code":{"type":"string","enum":["prospecting_report_expired"]},"message":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}},"required":["code","message","expires_at"]},"ProspectingReport":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"generated_for":{"type":"string","minLength":1},"language":{"type":"string","enum":["en","fr"]},"requested_by":{"type":"string","minLength":1},"domains":{"type":"array","items":{"type":"string"}},"include_organization_info":{"type":"boolean"},"include_bimi":{"type":"boolean"},"include_dns_records":{"type":"boolean"},"include_potential_loss":{"type":"boolean"},"theme":{"type":"string","enum":["light","dark"],"description":"The skin the report is drawn in: light on paper, or dark on a near-black ground."},"currency":{"type":"string","enum":["USD","CAD","EUR","GBP","AUD"],"description":"The currency every price band in the report is printed in."},"deadline_weeks":{"type":"integer","minimum":1,"maximum":52,"description":"How many weeks the plan and closing pages give the reader to fix what the report found."},"url":{"type":"string","format":"uri"},"expires_at":{"type":"string","format":"date-time"},"expired":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","generated_for","language","requested_by","domains","include_organization_info","include_bimi","include_dns_records","include_potential_loss","theme","currency","deadline_weeks","url","expires_at","expired","created_at","updated_at"]},"ProspectingReportCoverRow":{"type":"object","properties":{"url":{"type":"string"},"logo":{"type":"string","nullable":true},"score":{"type":"integer"},"score_band":{"type":"string","enum":["critical","bad","good","great"]},"risk_level":{"type":"string","enum":["critical","high","medium","low"]},"is_exposed":{"type":"boolean"},"is_fully_protected":{"type":"boolean"},"findings":{"type":"integer","description":"How many of the domain's checks are issues rather than passes."},"findings_label":{"type":"string"},"score_label":{"type":"string"},"chip":{"type":"object","properties":{"type":{"type":"string","enum":["pass","fail","soft-warn","info"]},"label":{"type":"string","nullable":true}},"required":["type","label"]}},"required":["url","logo","score","score_band","risk_level","is_exposed","is_fully_protected","findings","findings_label","score_label","chip"]},"ProspectingReportCover":{"type":"object","properties":{"year":{"type":"integer"},"domains_analyzed":{"type":"string"},"date":{"type":"string","description":"The generated date as the report prints it, in the requested language."},"verdict_key":{"type":"string","enum":["one","some","all","partial_one","partial","none"]},"verdict":{"type":"string"},"verdict_detail":{"type":"string"},"high_risk_findings":{"type":"integer"},"medium_findings":{"type":"integer"},"exposed_domains":{"type":"integer"},"protected_domains":{"type":"integer"},"high_risk_label":{"type":"string","nullable":true},"medium_label":{"type":"string","nullable":true},"protected_label":{"type":"string","nullable":true},"rows":{"type":"array","items":{"$ref":"#/components/schemas/ProspectingReportCoverRow"},"description":"Worst score first, capped at the number of rows the cover prints."},"more_domains":{"type":"string","nullable":true,"description":"Set when the report holds more domains than the cover lists."}},"required":["year","domains_analyzed","date","verdict_key","verdict","verdict_detail","high_risk_findings","medium_findings","exposed_domains","protected_domains","high_risk_label","medium_label","protected_label","rows","more_domains"]},"ProspectingReportRiskSummary":{"type":"object","properties":{"high_risk_items":{"type":"integer"},"medium_risk_items":{"type":"integer"},"by_category":{"type":"object","properties":{"security":{"type":"object","properties":{"high":{"type":"integer"},"medium":{"type":"integer"}},"required":["high","medium"]},"deliverability":{"type":"object","properties":{"high":{"type":"integer"},"medium":{"type":"integer"}},"required":["high","medium"]}},"required":["security","deliverability"]}},"required":["high_risk_items","medium_risk_items","by_category"]},"ProspectingReportFinding":{"type":"object","properties":{"key":{"type":"string","description":"Which check this is: mx, spf, dmarc, bimi."},"status":{"type":"string","description":"pass, warn, fail or error."},"status_label":{"type":"string","nullable":true,"description":"The risk word the report prints for this status, in `language`."},"risk_category":{"type":"string","nullable":true,"description":"security or deliverability."},"risk_category_label":{"type":"string","nullable":true},"message":{"type":"string","nullable":true,"description":"The finding as the report states it, in `language`."}},"required":["key","status","status_label","risk_category","risk_category_label","message"]},"ProspectingReportPotentialLoss":{"type":"object","nullable":true,"properties":{"revenue_loss_pct":{"type":"number"},"revenue_loss_display":{"type":"string","description":"The figure as the report prints it, which floors a perfect score at \"<10%\"."},"financial_loss":{"type":"number"},"financial_loss_display":{"type":"string"}},"required":["revenue_loss_pct","revenue_loss_display","financial_loss","financial_loss_display"],"description":"Null when include_potential_loss is false."},"ProspectingReportDomain":{"type":"object","properties":{"url":{"type":"string"},"score":{"type":"integer"},"score_band":{"type":"string","enum":["critical","bad","good","great"]},"risk_level":{"type":"string","enum":["critical","high","medium","low"]},"risk_level_label":{"type":"string","nullable":true},"percentile":{"type":"number","description":"Deprecated. A value read off a fixed curve, not a comparison with the scores of other domains, so it does not say how this domain ranks. The report stopped printing it for that reason; it is kept only so existing callers do not break. Do not show it to a reader as a ranking.","deprecated":true},"logo":{"type":"string","nullable":true},"dmarc":{"type":"object","properties":{"policy":{"type":"string","description":"none, quarantine, reject, or unknown when the domain publishes no DMARC record."},"pct":{"type":"number","nullable":true},"spoofing_risk":{"type":"string","enum":["high","medium","low","unknown"]},"spoofing_risk_label":{"type":"string","nullable":true},"is_fully_protected":{"type":"boolean","description":"True only at reject with pct=100."}},"required":["policy","pct","spoofing_risk","spoofing_risk_label","is_fully_protected"]},"risk_summary":{"$ref":"#/components/schemas/ProspectingReportRiskSummary"},"findings":{"type":"array","items":{"$ref":"#/components/schemas/ProspectingReportFinding"},"description":"Every check, including the ones that pass."},"potential_loss":{"$ref":"#/components/schemas/ProspectingReportPotentialLoss"},"dns_records":{"type":"object","nullable":true,"properties":{"dmarc":{"type":"array","items":{"type":"string"}},"spf":{"type":"array","items":{"type":"string"}},"mx":{"type":"array","items":{"type":"string"}},"bimi":{"type":"array","items":{"type":"string"}}},"required":["dmarc","spf","mx","bimi"],"description":"The records as published. Null unless include_dns_records is true."},"senders":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The catalogue's name for the service, or the SPF host itself when the catalogue has no entry."},"include":{"type":"string","description":"The host as it stands in the domain's own SPF record."},"logo":{"type":"string","nullable":true},"dkim":{"type":"string","enum":["signed","missing","unknown"],"description":"Whether a DKIM key for this service was found on the domain. unknown when the service publishes per-customer selectors, which cannot be enumerated, or when it publishes none the check knows."},"mail_kind":{"type":"string","nullable":true,"enum":["invoices","marketing","sales","support","notifications","transactional","payroll","mailbox","security-gateway","relay"],"description":"What kind of mail the service carries for the domain, from the curated catalogue. Null when unknown."}},"required":["name","include","logo","dkim","mail_kind"]},"description":"The services the domain's SPF record authorises to send as it. Empty when it publishes none."}},"required":["url","score","score_band","risk_level","risk_level_label","percentile","logo","dmarc","risk_summary","findings","potential_loss","dns_records","senders"]},"ProspectingReportContent":{"type":"object","properties":{"language":{"type":"string","enum":["en","fr"]},"theme":{"type":"string","enum":["light","dark"],"description":"The skin the report is drawn in: light on paper, or dark on a near-black ground."},"currency":{"type":"string","enum":["USD","CAD","EUR","GBP","AUD"],"description":"The currency every price band in the report is printed in."},"deadline_weeks":{"type":"integer","minimum":1,"maximum":52,"description":"How many weeks the plan and closing pages give the reader to fix what the report found."},"generated_at":{"type":"string","format":"date-time"},"generated_by":{"type":"string","description":"Your organization name, as the report signs itself."},"generated_for":{"type":"string"},"organization_logo":{"type":"string","nullable":true},"contact_info":{"type":"object","properties":{"email":{"type":"string"},"phone":{"type":"string"},"website":{"type":"string"}},"required":["email","phone","website"]},"options":{"type":"object","properties":{"include_bimi":{"type":"boolean"},"include_dns_records":{"type":"boolean"},"include_potential_loss":{"type":"boolean"}},"required":["include_bimi","include_dns_records","include_potential_loss"]},"cover":{"$ref":"#/components/schemas/ProspectingReportCover"},"domains":{"type":"array","items":{"$ref":"#/components/schemas/ProspectingReportDomain"}},"domains_without_audit":{"type":"array","items":{"type":"string"},"description":"Domains whose nameservers returned nothing usable, so the report has no page for them."}},"required":["language","theme","currency","deadline_weeks","generated_at","generated_by","generated_for","organization_logo","contact_info","options","cover","domains","domains_without_audit"]},"ScheduledReport":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customer_name":{"type":"string","minLength":1},"domain_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"schedule_frequency":{"type":"string","minLength":1},"auto_send":{"type":"boolean"},"email_recipients":{"type":"array","items":{"type":"string","format":"email"},"default":[]},"generated_at":{"type":"string","nullable":true,"format":"date-time"},"created_by":{"type":"string","minLength":1},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","customer_name","domain_ids","schedule_frequency","auto_send","generated_at","created_by","created_at","updated_at"]},"SupportTicketStatus":{"type":"string","enum":["open","pending_customer","pending_support","resolved","closed"]},"SupportTicketPriority":{"type":"string","enum":["low","normal","high","urgent","critical"]},"SupportTicket":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ticket_id":{"type":"string","nullable":true},"subject":{"type":"string"},"status":{"$ref":"#/components/schemas/SupportTicketStatus"},"priority":{"$ref":"#/components/schemas/SupportTicketPriority"},"hostname_url":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"last_message_at":{"type":"string","nullable":true,"format":"date-time"},"unread_count":{"type":"number"},"organization_name":{"type":"string","nullable":true},"created_by_name":{"type":"string","nullable":true}},"required":["id","ticket_id","subject","status","priority","hostname_url","created_at","updated_at","last_message_at","unread_count"]},"SupportTicketAuthorType":{"type":"string","enum":["customer","support","system"]},"SupportTicketAttachmentType":{"type":"string","enum":["file","link"]},"SupportTicketAttachment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"file_name":{"type":"string"},"file_url":{"type":"string","description":"Download URL for the attachment. For uploaded files this is a signed URL minted for this response and valid for one hour; fetch the ticket again for a fresh one rather than storing it. For link attachments it is the URL as supplied."},"file_size":{"type":"number","nullable":true},"mime_type":{"type":"string","nullable":true},"attachment_type":{"$ref":"#/components/schemas/SupportTicketAttachmentType"},"created_at":{"type":"string","format":"date-time"}},"required":["id","file_name","file_url","file_size","mime_type","attachment_type","created_at"]},"SupportTicketMessage":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"author_id":{"type":"string"},"author_type":{"$ref":"#/components/schemas/SupportTicketAuthorType"},"author_name":{"type":"string","nullable":true},"author_email":{"type":"string","nullable":true},"content":{"type":"string"},"is_ai_draft":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"read_at":{"type":"string","nullable":true,"format":"date-time"},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/SupportTicketAttachment"}}},"required":["id","author_id","author_type","author_name","author_email","content","is_ai_draft","created_at","read_at","attachments"]},"SupportTicketDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ticket_id":{"type":"string","nullable":true},"subject":{"type":"string"},"status":{"$ref":"#/components/schemas/SupportTicketStatus"},"priority":{"$ref":"#/components/schemas/SupportTicketPriority"},"hostname_id":{"type":"string","nullable":true},"hostname_url":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"resolved_at":{"type":"string","nullable":true,"format":"date-time"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/SupportTicketMessage"}},"organization_name":{"type":"string","nullable":true}},"required":["id","ticket_id","subject","status","priority","hostname_id","hostname_url","created_at","updated_at","resolved_at","messages"]},"AllowedUserStatusUpdate":{"type":"string","enum":["open","closed"]},"SupportAttachmentMimeType":{"type":"string","enum":["image/png","image/jpeg","image/gif","image/webp","application/pdf"]},"ActivityActorType":{"type":"string","enum":["USER","API_KEY","PALISADE_STAFF","SYSTEM"]},"ActivityLogEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"actor_type":{"$ref":"#/components/schemas/ActivityActorType"},"actor_id":{"type":"string"},"actor_email":{"type":"string","nullable":true},"actor_name":{"type":"string","nullable":true},"actor_api_key_reminder":{"type":"string","nullable":true},"action":{"type":"string"},"resource_type":{"type":"string","nullable":true},"resource_id":{"type":"string","nullable":true},"resource_label":{"type":"string","nullable":true},"method":{"type":"string"},"path":{"type":"string"},"status_code":{"type":"number"},"request_id":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true,"additionalProperties":{"nullable":true}},"created_at":{"type":"string","format":"date-time"}},"required":["id","actor_type","actor_id","actor_email","actor_name","actor_api_key_reminder","action","resource_type","resource_id","resource_label","method","path","status_code","request_id","metadata","created_at"]},"ActivityLogList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ActivityLogEntry"}},"page_info":{"type":"object","properties":{"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true},"per_page":{"type":"number"}},"required":["has_more","next_cursor","per_page"]}},"required":["data","page_info"]},"ActivityLogActions":{"type":"object","properties":{"actions":{"type":"array","items":{"type":"string"}}},"required":["actions"]},"SpfLookup":{"type":"object","properties":{"mechanism":{"type":"string","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","nullable":true,"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":"integer","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":"integer","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","description":"True when this lookup failed to resolve to a usable record."},"note":{"type":"string","nullable":true,"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."}},"required":["mechanism","host","depth","lookups","error","note"],"description":"One DNS lookup the record costs receivers."},"SpfProblem":{"type":"object","properties":{"code":{"type":"string","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","enum":["error","warning"],"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","nullable":true,"description":"The hostname the problem is about, when it concerns a specific lookup."},"message":{"type":"string","description":"What is wrong and what it means for mail, in plain language."}},"required":["code","severity","host","message"],"description":"A problem found in the live SPF setup."},"SpfDiagnostics":{"type":"object","properties":{"domain_id":{"type":"string","format":"uuid","description":"Id of the domain the diagnostics are for."},"domain":{"type":"string","description":"The domain name whose live DNS was read, for example \"example.com\"."},"record":{"type":"string","nullable":true,"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":{"type":"array","items":{"type":"string","description":"One v=spf1 TXT record."},"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","nullable":true,"description":"TTL of the SPF TXT record in seconds, when it could be read."},"status":{"type":"string","description":"Overall SPF health: \"pass\", \"warn\" or \"fail\"."},"lookup_count":{"type":"integer","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":"integer","description":"The limit SPF imposes before receivers return a permanent error: 10."},"lookups_remaining":{"type":"integer","description":"How many more lookups the record could add before breaking. 0 when at or over the limit."},"lookup_breakdown":{"type":"array","items":{"$ref":"#/components/schemas/SpfLookup"},"description":"Every lookup in the chain, nested entries following their parent."},"problems":{"type":"array","items":{"$ref":"#/components/schemas/SpfProblem"},"description":"Problems found, errors before warnings. Empty when SPF is healthy."},"hosted_spf":{"type":"object","properties":{"is_managed":{"type":"boolean","description":"True when Palisade hosts and flattens the SPF record for this domain."},"status":{"allOf":[{"$ref":"#/components/schemas/DnsStatus"},{"nullable":true,"description":"Live status of the hosted SPF chain, when managed."}]},"target_spf":{"type":"string","nullable":true,"description":"The record Palisade flattens and publishes, when hosted SPF is configured."},"error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why the hosted SPF chain is in error, when it is."}]},"last_flattened_at":{"type":"string","nullable":true,"description":"When Palisade last flattened and published the hosted chain, ISO 8601."}},"required":["is_managed","status","target_spf","error_reason","last_flattened_at"],"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","description":"The single action Palisade suggests taking next."}},"required":["domain_id","domain","record","records","ttl","status","lookup_count","max_lookups","lookups_remaining","lookup_breakdown","problems","hosted_spf","next_step"]},"AuditResult":{"type":"object","properties":{"status":{"type":"string","enum":["success","error"],"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":{"type":"object","properties":{"hostname":{"type":"string","description":"The domain audited, as requested."},"email":{"type":"string","description":"The `email` query parameter, echoed back. Empty when none was sent, and empty on the `error` status whether or not one was."},"parked":{"type":"boolean","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","description":"0-100: 100 less what each counted dimension loses, as each dimension's `score` reports."},"mx":{"type":"string","description":"The MX verdict, the same value as `mx.status`."},"spf":{"type":"string","description":"The SPF verdict, the same value as `spf.status`."},"dkim":{"type":"string","description":"The DKIM verdict, the same value as `dkim.status`."},"dmarc":{"type":"string","description":"The DMARC verdict, the same value as `dmarc.status`."},"bimi":{"type":"string","description":"The BIMI verdict, the same value as `bimi.status`."},"mtaSts":{"type":"string","description":"The MTA-STS verdict, the same value as `mtaSts.status`."},"tlsRpt":{"type":"string","description":"The TLS-RPT verdict, the same value as `tlsRpt.status`."},"errors":{"type":"array","items":{"type":"string"},"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":{"type":"array","items":{"type":"string"},"description":"Always empty. Findings are reported on each dimension."},"assessments":{"type":"array","items":{"type":"string"},"description":"Always empty. Findings are reported on each dimension."}},"required":["hostname","email","parked","score","errors"],"description":"The headline: the overall score and one verdict per dimension."},"nameservers":{"type":"array","items":{"type":"string"},"description":"The NS records the audit resolved: the domain's own, or its parent's when it has none."},"mx":{"type":"object","properties":{"score":{"type":"number","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","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":{"type":"array","items":{"type":"string"},"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"type":"array","items":{"type":"string"},"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":{"type":"array","items":{"type":"string"},"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":{"type":"object","properties":{"status":{"type":"string","description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":"string","description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":"number","description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"type":"array","items":{"type":"string"},"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"required":["status","state","score","penalties"],"description":"The MX check as a state: \"mx-present\" or \"mx-missing\"."},"mxHost":{"type":"string","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":{"type":"array","items":{"type":"object","properties":{"exchange":{"type":"string","description":"The mail server hostname."},"priority":{"type":"number","description":"MX preference. Lower values are tried first."}},"required":["exchange","priority"]},"description":"The MX records found, most preferred first."},"resolutionFailed":{"type":"boolean","description":"True when the MX lookup itself failed, so an empty `mxValue` is not evidence of absence."}},"required":["score","status","assessments","errors","warnings","mxPresent","mxHost","mxValue"],"description":"The MX check."},"spf":{"type":"object","properties":{"score":{"type":"number","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","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":{"type":"array","items":{"type":"string"},"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"type":"array","items":{"type":"string"},"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":{"type":"array","items":{"type":"string"},"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":{"type":"object","properties":{"status":{"type":"string","description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":"string","description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":"number","description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"type":"array","items":{"type":"string"},"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"required":["status","state","score","penalties"],"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","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","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":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","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","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":{"type":"array","items":{"nullable":true},"description":"The lookups this one expands into, each an SpfLookupNode."},"note":{"type":"string","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","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\"."}},"required":["type"]},"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","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":{"type":"array","items":{"nullable":true},"description":"Always empty."},"deprecated":{"type":"boolean","description":"True when the domain still publishes the obsolete SPF record type (DNS type 99), which receivers ignore."},"ip4Ranges":{"type":"array","items":{"type":"string"},"description":"The `ip4:` ranges the record authorizes itself, not counting its includes. Empty when the record could not be evaluated."},"ip6Ranges":{"type":"array","items":{"type":"string"},"description":"The `ip6:` ranges the record authorizes itself, not counting its includes. Empty when the record could not be evaluated."},"ttl":{"type":"number","nullable":true,"description":"TTL of the SPF record in seconds, from the domain's authoritative nameservers. 0 or null when it could not be read."},"recommendations":{"type":"array","items":{"type":"string"},"description":"Improvements worth making beyond `errors` and `warnings`, as codes such as \"dmarc-policy-quarantine\", which urges moving on to reject. Usually empty."}},"required":["score","status","assessments","errors","warnings","spfSoftFail","spfValue","lookupCount","lookupStructure","services","deprecated","ip4Ranges","ip6Ranges","ttl","recommendations"],"description":"The SPF check, including what the record costs in DNS lookups."},"dkim":{"type":"object","properties":{"score":{"type":"number","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","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":{"type":"array","items":{"type":"string"},"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"type":"array","items":{"type":"string"},"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":{"type":"array","items":{"type":"string"},"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","description":"The domain whose DKIM selectors were checked."},"dkimValues":{"type":"array","items":{"type":"object","properties":{"record":{"type":"string","description":"The TXT record published at the selector."},"selector":{"type":"string","description":"The selector, published at `<selector>._domainkey.<domain>`."},"source":{"type":"string","description":"The sending service in Palisade's catalogue that uses this selector."},"k":{"type":"string","description":"The key type from `k=`, such as \"rsa\". Empty when the tag is absent, which means rsa."},"p":{"type":"string","description":"The public key from `p=`. Empty when the key is revoked (`p=` with no value) or the tag is missing."},"score":{"type":"number","description":"Points this record takes off the DKIM score. 0 when it is sound."},"assessments":{"type":"array","items":{"type":"string"},"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"type":"array","items":{"type":"string"},"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":{"type":"array","items":{"type":"string"},"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."}},"required":["record","selector","source","k","p","score","assessments","errors","warnings"]},"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."}},"required":["score","status","assessments","errors","warnings","hostname","dkimValues"],"description":"The DKIM check, over the selectors Palisade knows to probe."},"dmarc":{"type":"object","properties":{"hostname":{"type":"string","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","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","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":{"type":"array","items":{"type":"string"},"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"type":"array","items":{"type":"string"},"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":{"type":"array","items":{"type":"string"},"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","description":"Whether the record already sends aggregate reports to Palisade: \"SETUP\" if so, otherwise \"NONE\"."},"dmarcClosed":{"type":"object","properties":{"status":{"type":"string","description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":"string","description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":"number","description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"type":"array","items":{"type":"string"},"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"required":["status","state","score","penalties"],"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":{"type":"object","properties":{"status":{"type":"string","description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":"string","description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":"number","description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"type":"array","items":{"type":"string"},"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"required":["status","state","score","penalties"],"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":{"type":"array","items":{"type":"string"},"description":"Every `v=DMARC1` record found. More than one is an error: receivers then ignore DMARC."},"dmarcValue":{"type":"string","nullable":true,"description":"The record evaluated. Null when none was found or more than one is published."},"ttl":{"type":"number","nullable":true,"description":"TTL of the DMARC record in seconds, from the authoritative nameservers. Null when it could not be read."},"tags":{"type":"object","properties":{"policy":{"type":"string","nullable":true,"description":"The `p=` policy, lowercased: \"none\", \"quarantine\" or \"reject\"."},"pct":{"type":"number","nullable":true,"description":"The `pct=` percentage of failing mail the policy applies to. Null when absent, which means 100."},"fo":{"type":"string","nullable":true,"description":"The `fo=` failure-reporting option: \"0\", \"1\", \"d\" or \"s\"."},"ruaMailto":{"type":"array","items":{"type":"string"},"description":"Where aggregate reports go: the `rua=` addresses, without `mailto:`."},"rufMailto":{"type":"array","items":{"type":"string"},"description":"Where failure reports go: the `ruf=` addresses, without `mailto:`."},"adkim":{"type":"string","nullable":true,"description":"The `adkim=` DKIM alignment mode: \"r\" relaxed or \"s\" strict. Null when absent, which means relaxed."},"aspf":{"type":"string","nullable":true,"description":"The `aspf=` SPF alignment mode: \"r\" relaxed or \"s\" strict. Null when absent, which means relaxed."},"sp":{"type":"string","nullable":true,"description":"The `sp=` policy for subdomains. Null when absent, in which case subdomains take `p=`."}},"required":["policy","pct","fo","ruaMailto","rufMailto","adkim","aspf","sp"],"description":"The tags the record sets, parsed. A tag it omits is null, or an empty list."},"monitoring":{"type":"array","items":{"type":"string"},"description":"Reporting findings, kept apart from `errors`: codes such as \"dmarc-missing-rua-tag\", which means no aggregate reports are requested."},"recommendations":{"type":"array","items":{"type":"string"},"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","description":"True when the record is served through a CNAME rather than published directly."},"isPalisadeCname":{"type":"boolean","description":"True when that CNAME points at Palisade, which hosts the record."},"resolutionFailed":{"type":"boolean","description":"True when the DMARC lookup itself failed, so what `errors` reports missing is not evidence of absence."}},"required":["hostname","score","status","assessments","errors","warnings","setupState","dmarcClosed","dmarcMonitored","records","ttl","tags","monitoring","recommendations","viaCname","isPalisadeCname","resolutionFailed"],"description":"The DMARC check, which scores the policy and the reporting separately."},"bimi":{"type":"object","properties":{"score":{"type":"number","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","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":{"type":"array","items":{"type":"string"},"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"type":"array","items":{"type":"string"},"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":{"type":"array","items":{"type":"string"},"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":{"type":"object","properties":{"status":{"type":"string","description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":"string","description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":"number","description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"type":"array","items":{"type":"string"},"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"required":["status","state","score","penalties"],"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","nullable":true,"description":"The record at `default._bimi.<domain>`. Null when none, or more than one, is published."},"location":{"type":"string","nullable":true,"description":"The logo URL from `l=`. Null when the tag is absent."},"assertion":{"type":"string","nullable":true,"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":{"type":"array","items":{"type":"string"},"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","format":"date-time","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","description":"True when the record is served through a CNAME rather than published directly."},"isPalisadeCname":{"type":"boolean","description":"True when that CNAME points at Palisade, which hosts the record."},"deepValidated":{"type":"boolean","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":{"type":"object","properties":{"fetched":{"type":"boolean","description":"True when the logo could be downloaded."},"valid":{"type":"boolean","description":"True when it passed Palisade's checks for a BIMI SVG."},"error":{"type":"string","nullable":true,"description":"Why the logo failed, when it did."}},"required":["fetched","valid","error"],"description":"What fetching and checking the logo at `location` found. Present when `deepValidated` is true."},"vmcValidation":{"type":"object","properties":{"fetched":{"type":"boolean","description":"True when the certificate file could be downloaded."},"valid":{"type":"boolean","description":"True when every certificate check passed. Warnings such as a logo mismatch do not count against it."},"certificateCount":{"type":"number","description":"PEM certificates in the file."},"chainComplete":{"type":"boolean","nullable":true,"description":"True when the file carries every certificate from the leaf up to a self-signed root. Null until checked."},"chainTrusted":{"type":"boolean","nullable":true,"description":"True when the chain, exactly as published, ends in a root Palisade trusts for BIMI. Null until checked."},"trustAnchor":{"type":"string","nullable":true,"description":"The trusted root the chain ends in. Null when it is not trusted."},"markTypePresent":{"type":"boolean","nullable":true,"description":"True when the certificate carries the BIMI mark-type attribute a VMC or CMC must have. Null until checked."},"isVmc":{"type":"boolean","nullable":true,"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","nullable":true,"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","nullable":true,"description":"True when the certificate names the audited domain. Null until checked."},"issuer":{"type":"string","nullable":true,"description":"Common name of the certificate's issuer."},"subjectCommonName":{"type":"string","nullable":true,"description":"Common name the certificate was issued to."},"validFrom":{"type":"string","nullable":true,"description":"Start of the validity period, ISO 8601."},"validTo":{"type":"string","nullable":true,"description":"End of the validity period, ISO 8601."},"sanDomains":{"type":"array","items":{"type":"string"},"description":"The domains the certificate covers, from its subject alternative names."},"error":{"type":"string","nullable":true,"description":"Why the certificate failed, when it did."}},"required":["fetched","valid","certificateCount","chainComplete","chainTrusted","trustAnchor","markTypePresent","isVmc","logoMatchesCertificate","domainCovered","issuer","subjectCommonName","validFrom","validTo","sanDomains","error"],"description":"What fetching and checking the certificate at `assertion` found. Present when `deepValidated` is true and `a=` names a certificate."}},"required":["score","status","assessments","errors","warnings","bimiCertified","bimiValue","location","assertion","recommendations","viaCname","isPalisadeCname"],"description":"The BIMI check, including the logo and certificate the record links to."},"mtaSts":{"type":"object","properties":{"score":{"type":"number","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","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":{"type":"array","items":{"type":"string"},"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"type":"array","items":{"type":"string"},"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":{"type":"array","items":{"type":"string"},"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":{"type":"object","properties":{"status":{"type":"string","description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":"string","description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":"number","description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"type":"array","items":{"type":"string"},"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"required":["status","state","score","penalties"],"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","nullable":true,"description":"The record at `_mta-sts.<domain>`. Null when none, or more than one, is published."},"policyFile":{"type":"string","nullable":true,"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","nullable":true,"description":"The policy `mode`: \"enforce\", \"testing\" or \"none\". Null when there is no policy to read."},"mxEntries":{"type":"array","items":{"type":"string"},"description":"The `mx:` patterns the policy covers."},"maxAge":{"type":"number","nullable":true,"description":"The policy `max_age` in seconds. Null when there is no readable value."},"policyId":{"type":"string","nullable":true,"description":"The `id=` of the TXT record. Senders fetch the policy again when it changes."}},"required":["score","status","assessments","errors","warnings","mtaStsStatus","txtValue","policyFile","mode","mxEntries","maxAge","policyId"],"description":"The MTA-STS check. It does not count toward the overall score."},"tlsRpt":{"type":"object","properties":{"score":{"type":"number","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","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":{"type":"array","items":{"type":"string"},"description":"What was found that is not a problem, such as \"spf-success\". MTA-STS and TLS-RPT write sentences instead of codes."},"errors":{"type":"array","items":{"type":"string"},"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":{"type":"array","items":{"type":"string"},"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":{"type":"object","properties":{"status":{"type":"string","description":"The verdict this state carries: \"pass\", \"warn\", \"fail\", \"in progress\" or \"info\"."},"state":{"type":"string","description":"Stable identifier for what was found, safe to branch on. The field holding this object lists its values."},"score":{"type":"number","description":"Points this state earns toward its dimension. Unlike the dimension's own `score`, it counts up from 0."},"penalties":{"type":"array","items":{"type":"string"},"description":"Codes for lesser problems found alongside the state, such as \"spf-ptr-unrecommended\". Usually empty."}},"required":["status","state","score","penalties"],"description":"The TLS-RPT record as a state: \"tls-rpt-present\", \"tls-rpt-missing\" or \"tls-rpt-invalid\"."},"txtValue":{"type":"string","nullable":true,"description":"The record at `_smtp._tls.<domain>`. Null when none, or more than one, is published."},"reportingEndpoints":{"type":"array","items":{"type":"string"},"description":"Where TLS failure reports go: the valid `rua=` destinations, each `mailto:` or `https://`."}},"required":["score","status","assessments","errors","warnings","tlsRptStatus","txtValue","reportingEndpoints"],"description":"The TLS-RPT check. It does not count toward the overall score."}},"required":["status","summary"]},"MailboxProviderResult":{"type":"object","properties":{"domain":{"type":"string"},"provider":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}},"required":["id","name","url"],"description":"Null when the MX records match no known mailbox provider, or when the domain has no MX at all."}},"required":["domain","provider"]},"ValidateIncludeResult":{"type":"object","properties":{"valid":{"type":"boolean"},"lookup_count":{"type":"number","description":"DNS lookups the include costs, counting the include itself. Only present when `valid` is true."},"error":{"type":"string","description":"Why the domain is not usable as an SPF include. Only present when `valid` is false."}},"required":["valid"]},"DnsLookupResult":{"type":"object","properties":{"records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","MX","NS","A","CNAME"]},"value":{"type":"string"},"priority":{"type":"number","description":"MX preference. Only present on MX records."}},"required":["type","value"]}}},"required":["records"]},"HeaderAnalysisResult":{"type":"object","properties":{"summary":{"type":"object","properties":{"from":{"type":"string"},"returnPath":{"type":"string"},"subject":{"type":"string"},"date":{"type":"string"},"messageId":{"type":"string"},"spf":{"type":"string","description":"pass, fail, softfail, neutral, none, temperror, permerror or unknown."},"dkim":{"type":"string","description":"pass, fail, softfail, neutral, none, temperror, permerror or unknown."},"dmarc":{"type":"string","description":"pass, fail, softfail, neutral, none, temperror, permerror or unknown."},"spfAlignment":{"type":"string","description":"Whether the SPF domain aligns with the From domain, strictly or relaxed."},"dkimAlignment":{"type":"string"},"overallVerdict":{"type":"string"}},"required":["from","returnPath","subject","date","messageId","spf","dkim","dmarc","spfAlignment","dkimAlignment","overallVerdict"]},"authenticationResults":{"type":"array","items":{"type":"object","properties":{"authServId":{"type":"string"},"spf":{"type":"string","description":"pass, fail, softfail, neutral, none, temperror, permerror or unknown."},"spfReason":{"type":"string"},"spfFrom":{"type":"string"},"dkim":{"type":"string","description":"pass, fail, softfail, neutral, none, temperror, permerror or unknown."},"dkimReason":{"type":"string"},"dkimDomain":{"type":"string"},"dmarc":{"type":"string","description":"pass, fail, softfail, neutral, none, temperror, permerror or unknown."},"dmarcReason":{"type":"string"},"compAuth":{"type":"string","description":"Microsoft's composite authentication verdict, when present."},"raw":{"type":"string"}},"required":["authServId","spf","spfReason","spfFrom","dkim","dkimReason","dkimDomain","dmarc","dmarcReason","compAuth","raw"]}},"dkimSignatures":{"type":"array","items":{"type":"object","properties":{"version":{"type":"string"},"algorithm":{"type":"string"},"domain":{"type":"string"},"selector":{"type":"string"},"headerFields":{"type":"string"},"bodyHash":{"type":"string"},"raw":{"type":"string"}},"required":["version","algorithm","domain","selector","headerFields","bodyHash","raw"]}},"arcChain":{"type":"array","items":{"type":"object","properties":{"instance":{"type":"number"},"arcAuthenticationResults":{"type":"string"},"arcSeal":{"type":"string"},"arcMessageSignature":{"type":"string"},"cv":{"type":"string"}},"required":["instance","arcAuthenticationResults","arcSeal","arcMessageSignature","cv"]}},"receivedHops":{"type":"array","items":{"type":"object","properties":{"index":{"type":"number"},"from":{"type":"string"},"by":{"type":"string"},"protocol":{"type":"string"},"timestamp":{"type":"string"},"delayMs":{"type":"number","nullable":true,"description":"Milliseconds spent at this hop, or null when it cannot be derived."},"ip":{"type":"string"},"raw":{"type":"string"}},"required":["index","from","by","protocol","timestamp","delayMs","ip","raw"]}},"dnsValidation":{"type":"object","nullable":true,"properties":{"fromDomain":{"type":"string"},"spfRecord":{"type":"string","nullable":true},"dmarcRecord":{"type":"string","nullable":true},"dmarcPolicy":{"type":"string","nullable":true},"dkimSelectors":{"type":"array","items":{"type":"object","properties":{"selector":{"type":"string"},"domain":{"type":"string"},"publicKeyFound":{"type":"boolean"}},"required":["selector","domain","publicKeyFound"]}}},"required":["fromDomain","spfRecord","dmarcRecord","dmarcPolicy","dkimSelectors"],"description":"Live DNS read for the From domain. Null when `include_dns_validation` was false."},"warnings":{"type":"array","items":{"type":"string"}},"rawHeaderCount":{"type":"number"}},"required":["summary","authenticationResults","dkimSignatures","arcChain","receivedHops","dnsValidation","warnings","rawHeaderCount"]},"WebhookEndpoint":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"description":{"type":"string","nullable":true},"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 this endpoint receives. An empty array means every event, including ones added later — not none."},"status":{"type":"string","enum":["ENABLED","DISABLED","AUTO_DISABLED"],"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 you re-enable it, which also clears the failure count."},"secret_reminder":{"type":"string","description":"Last 4 characters of the signing secret. The full secret is returned only at creation."},"api_version":{"type":"string","description":"API version the payload shape is pinned to, captured when the endpoint was created."},"consecutive_failures":{"type":"number","minimum":0},"disabled_reason":{"type":"string","nullable":true},"last_success_at":{"type":"string","nullable":true,"format":"date-time"},"last_failure_at":{"type":"string","nullable":true,"format":"date-time"},"created_by":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","url","description","enabled_events","status","secret_reminder","api_version","consecutive_failures","disabled_reason","last_success_at","last_failure_at","created_by","created_at","updated_at"]},"WebhookEndpointWithSecret":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","properties":{"secret":{"type":"string","description":"The signing secret, shown once. Store it now; it cannot be retrieved later."}},"required":["secret"]}]},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_id":{"type":"string"},"event_type":{"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"]},"status":{"type":"string","enum":["PENDING","SUCCEEDED","FAILED"]},"attempts":{"type":"number","minimum":0},"response_status":{"type":"number","nullable":true},"error_message":{"type":"string","nullable":true},"delivered_at":{"type":"string","nullable":true,"format":"date-time"},"created_at":{"type":"string","format":"date-time"}},"required":["id","event_id","event_type","status","attempts","response_status","error_message","delivered_at","created_at"]},"WebhookEventDescriptor":{"type":"object","properties":{"type":{"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":{"type":"string"},"payload_type":{"type":"string"}},"required":["type","description","payload_type"]}},"parameters":{}},"paths":{"/health-check":{"get":{"tags":["Health Check"],"summary":"Check API availability","operationId":"getHealthCheck","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"success":{"type":"boolean"}},"required":["message","success"]}}}}}}},"/domains":{"get":{"tags":["Domain"],"summary":"List domains","operationId":"listDomains","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"url","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"group","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"ungrouped","in":"query"},{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DmarcPolicy"}},"required":false,"style":"form","explode":true,"name":"dmarc_policies","in":"query"},{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DmarcSetupState"}},"required":false,"style":"form","explode":true,"name":"monitoring_statuses","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"is_parked","in":"query"},{"schema":{"type":"object","properties":{"gt":{"type":"number","nullable":true,"minimum":0,"maximum":100},"gte":{"type":"number","nullable":true,"minimum":0,"maximum":100},"lt":{"type":"number","nullable":true,"minimum":0,"maximum":100},"lte":{"type":"number","nullable":true,"minimum":0,"maximum":100},"eq":{"type":"number","nullable":true,"minimum":0,"maximum":100}}},"required":false,"style":"deepObject","name":"score","in":"query"},{"schema":{"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":"\"severity\" orders by ticket priority, then count, with no-ticket domains last under desc and first under asc. \"status\" keeps configured domains (including zero tickets) before monitoring/setup states in both directions, and applies severity ordering within each group."},"required":false,"name":"sort","in":"query"},{"schema":{"type":"string","enum":["desc","asc"]},"required":false,"name":"sort_direction","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":50,"default":20},"required":false,"name":"per_page","in":"query"},{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["group","tasks"]}},"required":false,"style":"form","explode":true,"name":"expand","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Domain"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}},"post":{"tags":["Domain"],"summary":"Add a domain","operationId":"createDomain","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"group_id":{"type":"string","format":"uuid"},"use_stored_scan":{"type":"boolean"}},"required":["url"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"dmarc_policy":{"type":"string"},"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":{"$ref":"#/components/schemas/DmarcPolicy"},"pct":{"type":"number","minimum":0,"maximum":100},"sp":{"type":"string","enum":["none","quarantine","reject","same"]},"strict_spf":{"type":"boolean"},"strict_dkim":{"type":"boolean"}},"required":["additional_emails","policy","pct","sp","strict_spf","strict_dkim"]},"spf_options":{"type":"object","properties":{"force_flattening":{"type":"boolean"},"enabled":{"type":"boolean"},"mechanism_descriptions":{"type":"object","additionalProperties":{"type":"string"}}},"required":["force_flattening","enabled"]},"managed_dns_status":{"type":"object","properties":{"dmarc":{"$ref":"#/components/schemas/DnsStatus"},"spf":{"$ref":"#/components/schemas/DnsStatus"},"dkim":{"$ref":"#/components/schemas/DnsStatus"},"bimi":{"$ref":"#/components/schemas/DnsStatus"},"mta_sts":{"$ref":"#/components/schemas/DnsStatus"}},"required":["dmarc","spf","dkim","bimi","mta_sts"]},"managed_dns_status_since":{"type":"object","properties":{"dmarc":{"type":"string","nullable":true,"format":"date-time"},"spf":{"type":"string","nullable":true,"format":"date-time"},"dkim":{"type":"string","nullable":true,"format":"date-time"},"bimi":{"type":"string","nullable":true,"format":"date-time"},"mta_sts":{"type":"string","nullable":true,"format":"date-time"}},"required":["dmarc","spf","dkim","bimi","mta_sts"],"description":"When each managed record last entered the \"syncing\" status. For dmarc, falls back to dmarc_setup_at when the transition predates tracking; other records are null when they have never synced or the transition predates tracking. Useful for estimating how long a \"syncing\" record has been waiting on DNS propagation."},"monitoring_status":{"$ref":"#/components/schemas/DmarcSetupState"},"monitoring_failure_reason":{"$ref":"#/components/schemas/DmarcSetupFailureReason"},"monitoring_failure_detail":{"$ref":"#/components/schemas/DmarcSetupFailureDetail"},"monitoring_email":{"type":"string"},"open_tasks_count":{"type":"number","minimum":0,"description":"Open tickets on this domain. Opportunity tickets such as set_up_bimi are not counted, and `expand[]=tasks` leaves them out to match."},"highest_open_task_priority":{"type":"integer","nullable":true,"minimum":0,"maximum":3,"description":"Priority of the most urgent open task on this domain, on the same inverted scale as a task's own priority: 0 critical, 1 high, 2 medium, 3 low. Null when the domain has no open tasks. Sort on \"severity\" to order domains by it."},"score":{"type":"number","minimum":0,"maximum":100},"score_band":{"type":"string","enum":["critical","bad","good","great"],"description":"Risk band the score falls in: critical, bad, good, or great. The same banding the dashboard shows, so clients do not have to reimplement the thresholds."},"is_parked":{"type":"boolean"},"is_dmarc_managed":{"type":"boolean"},"is_spf_managed":{"type":"boolean"},"is_dkim_managed":{"type":"boolean"},"is_bimi_managed":{"type":"boolean"},"is_mta_sts_managed":{"type":"boolean"},"bimi_options":{"type":"object","properties":{"enabled":{"type":"boolean"},"logo":{"type":"string","nullable":true,"format":"uri"},"certificate":{"type":"string","nullable":true,"format":"uri"}},"required":["enabled","logo","certificate"]},"bimi_error_reason":{"$ref":"#/components/schemas/ManagedDnsErrorReason"},"dmarc_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.dmarc is \"error\". \"record_conflict\" means _dmarc holds records that conflict with each other — a CNAME coexisting with other data, or more than one DMARC record — and the domain's DMARC policy is not reliably enforced. See dns_conflicts for the offending records. Null when there is no error or the error predates reason tracking."}]},"spf_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.spf is \"error\". See dmarc_error_reason for the meaning of each value."}]},"dkim_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.dkim is \"error\". See dmarc_error_reason for the meaning of each value."}]},"mta_sts_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.mta_sts is \"error\". See dmarc_error_reason for the meaning of each value."}]},"dns_conflicts":{"type":"array","items":{"$ref":"#/components/schemas/DnsConflict"},"description":"Unresolved record conflicts observed at labels Palisade manages or monitors for this domain. Empty for a healthy domain. Each entry quotes the offending records verbatim along with what every authoritative nameserver and public resolver returned, so it can be acted on without a second investigation."},"bimi_cname_target":{"type":"string","description":"The host a customer CNAMEs default._bimi.{domain} to for Palisade-served BIMI."},"bimi_txt_value":{"type":"string","nullable":true,"description":"The fully-formed BIMI TXT value, populated whenever bimi_options.logo is set (via asset ingestion or a direct bimi_options update). The a= tag is included only when a certificate URL is also set. Customers who prefer not to delegate via CNAME can publish this value at default._bimi.{domain} themselves."},"target_spf":{"type":"string","nullable":true},"last_14_days_volume":{"type":"number","minimum":0},"last_14_days_dmarc_pass_pct":{"type":"number","minimum":0,"maximum":100},"last_14_days_spf_pass_pct":{"type":"number","minimum":0,"maximum":100},"last_14_days_dkim_pass_pct":{"type":"number","minimum":0,"maximum":100},"group_id":{"type":"string","nullable":true,"format":"uuid"},"registrar":{"type":"string","nullable":true},"dmarc_setup_at":{"type":"string","nullable":true,"format":"date-time"},"dmarc_verified_at":{"type":"string","nullable":true,"format":"date-time","description":"When the domain first reached the \"setup\" monitoring status and DMARC report collection began."},"monitoring_since":{"type":"string","nullable":true,"description":"The first day (yyyy-MM-dd) a DMARC aggregate report was actually received for this domain. The honest denominator for any activity claim: nothing before this day was observable, so a domain monitored for a week can never truthfully read as quiet for ninety days. Null until the first report arrives.","example":"2025-09-14"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","url","dmarc_policy","dmarc_options","spf_options","managed_dns_status","managed_dns_status_since","monitoring_status","monitoring_failure_reason","monitoring_failure_detail","monitoring_email","open_tasks_count","highest_open_task_priority","score","score_band","is_parked","is_dmarc_managed","is_spf_managed","is_dkim_managed","is_bimi_managed","is_mta_sts_managed","bimi_options","bimi_error_reason","dmarc_error_reason","spf_error_reason","dkim_error_reason","mta_sts_error_reason","dns_conflicts","bimi_cname_target","bimi_txt_value","target_spf","last_14_days_volume","last_14_days_dmarc_pass_pct","last_14_days_spf_pass_pct","last_14_days_dkim_pass_pct","group_id","registrar","dmarc_setup_at","dmarc_verified_at","monitoring_since","created_at","updated_at"]}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/domains/{id}":{"get":{"tags":["Domain"],"summary":"Get a domain","operationId":"getDomain","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}}}},"post":{"tags":["Domain"],"summary":"Update a domain","operationId":"updateDomain","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"group_id":{"type":"string","nullable":true,"format":"uuid"},"is_parked":{"type":"boolean"},"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":{"$ref":"#/components/schemas/DmarcPolicy"},"pct":{"type":"number","minimum":0,"maximum":100},"sp":{"type":"string","enum":["none","quarantine","reject","same"]},"strict_spf":{"type":"boolean"},"strict_dkim":{"type":"boolean"}}},"spf_options":{"type":"object","properties":{"force_flattening":{"type":"boolean"},"enabled":{"type":"boolean"},"mechanism_descriptions":{"type":"object","additionalProperties":{"type":"string"}}}},"dmarc_setup_at":{"type":"string","format":"date-time"},"target_spf":{"type":"string"},"bimi_options":{"type":"object","properties":{"enabled":{"type":"boolean"},"logo":{"type":"string","nullable":true,"format":"uri"},"certificate":{"type":"string","nullable":true,"format":"uri"}}},"backfill_tickets":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"dmarc_policy":{"type":"string"},"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":{"$ref":"#/components/schemas/DmarcPolicy"},"pct":{"type":"number","minimum":0,"maximum":100},"sp":{"type":"string","enum":["none","quarantine","reject","same"]},"strict_spf":{"type":"boolean"},"strict_dkim":{"type":"boolean"}},"required":["additional_emails","policy","pct","sp","strict_spf","strict_dkim"]},"spf_options":{"type":"object","properties":{"force_flattening":{"type":"boolean"},"enabled":{"type":"boolean"},"mechanism_descriptions":{"type":"object","additionalProperties":{"type":"string"}}},"required":["force_flattening","enabled"]},"managed_dns_status":{"type":"object","properties":{"dmarc":{"$ref":"#/components/schemas/DnsStatus"},"spf":{"$ref":"#/components/schemas/DnsStatus"},"dkim":{"$ref":"#/components/schemas/DnsStatus"},"bimi":{"$ref":"#/components/schemas/DnsStatus"},"mta_sts":{"$ref":"#/components/schemas/DnsStatus"}},"required":["dmarc","spf","dkim","bimi","mta_sts"]},"managed_dns_status_since":{"type":"object","properties":{"dmarc":{"type":"string","nullable":true,"format":"date-time"},"spf":{"type":"string","nullable":true,"format":"date-time"},"dkim":{"type":"string","nullable":true,"format":"date-time"},"bimi":{"type":"string","nullable":true,"format":"date-time"},"mta_sts":{"type":"string","nullable":true,"format":"date-time"}},"required":["dmarc","spf","dkim","bimi","mta_sts"],"description":"When each managed record last entered the \"syncing\" status. For dmarc, falls back to dmarc_setup_at when the transition predates tracking; other records are null when they have never synced or the transition predates tracking. Useful for estimating how long a \"syncing\" record has been waiting on DNS propagation."},"monitoring_status":{"$ref":"#/components/schemas/DmarcSetupState"},"monitoring_failure_reason":{"$ref":"#/components/schemas/DmarcSetupFailureReason"},"monitoring_failure_detail":{"$ref":"#/components/schemas/DmarcSetupFailureDetail"},"monitoring_email":{"type":"string"},"open_tasks_count":{"type":"number","minimum":0,"description":"Open tickets on this domain. Opportunity tickets such as set_up_bimi are not counted, and `expand[]=tasks` leaves them out to match."},"highest_open_task_priority":{"type":"integer","nullable":true,"minimum":0,"maximum":3,"description":"Priority of the most urgent open task on this domain, on the same inverted scale as a task's own priority: 0 critical, 1 high, 2 medium, 3 low. Null when the domain has no open tasks. Sort on \"severity\" to order domains by it."},"score":{"type":"number","minimum":0,"maximum":100},"score_band":{"type":"string","enum":["critical","bad","good","great"],"description":"Risk band the score falls in: critical, bad, good, or great. The same banding the dashboard shows, so clients do not have to reimplement the thresholds."},"is_parked":{"type":"boolean"},"is_dmarc_managed":{"type":"boolean"},"is_spf_managed":{"type":"boolean"},"is_dkim_managed":{"type":"boolean"},"is_bimi_managed":{"type":"boolean"},"is_mta_sts_managed":{"type":"boolean"},"bimi_options":{"type":"object","properties":{"enabled":{"type":"boolean"},"logo":{"type":"string","nullable":true,"format":"uri"},"certificate":{"type":"string","nullable":true,"format":"uri"}},"required":["enabled","logo","certificate"]},"bimi_error_reason":{"$ref":"#/components/schemas/ManagedDnsErrorReason"},"dmarc_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.dmarc is \"error\". \"record_conflict\" means _dmarc holds records that conflict with each other — a CNAME coexisting with other data, or more than one DMARC record — and the domain's DMARC policy is not reliably enforced. See dns_conflicts for the offending records. Null when there is no error or the error predates reason tracking."}]},"spf_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.spf is \"error\". See dmarc_error_reason for the meaning of each value."}]},"dkim_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.dkim is \"error\". See dmarc_error_reason for the meaning of each value."}]},"mta_sts_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.mta_sts is \"error\". See dmarc_error_reason for the meaning of each value."}]},"dns_conflicts":{"type":"array","items":{"$ref":"#/components/schemas/DnsConflict"},"description":"Unresolved record conflicts observed at labels Palisade manages or monitors for this domain. Empty for a healthy domain. Each entry quotes the offending records verbatim along with what every authoritative nameserver and public resolver returned, so it can be acted on without a second investigation."},"bimi_cname_target":{"type":"string","description":"The host a customer CNAMEs default._bimi.{domain} to for Palisade-served BIMI."},"bimi_txt_value":{"type":"string","nullable":true,"description":"The fully-formed BIMI TXT value, populated whenever bimi_options.logo is set (via asset ingestion or a direct bimi_options update). The a= tag is included only when a certificate URL is also set. Customers who prefer not to delegate via CNAME can publish this value at default._bimi.{domain} themselves."},"target_spf":{"type":"string","nullable":true},"last_14_days_volume":{"type":"number","minimum":0},"last_14_days_dmarc_pass_pct":{"type":"number","minimum":0,"maximum":100},"last_14_days_spf_pass_pct":{"type":"number","minimum":0,"maximum":100},"last_14_days_dkim_pass_pct":{"type":"number","minimum":0,"maximum":100},"group_id":{"type":"string","nullable":true,"format":"uuid"},"registrar":{"type":"string","nullable":true},"dmarc_setup_at":{"type":"string","nullable":true,"format":"date-time"},"dmarc_verified_at":{"type":"string","nullable":true,"format":"date-time","description":"When the domain first reached the \"setup\" monitoring status and DMARC report collection began."},"monitoring_since":{"type":"string","nullable":true,"description":"The first day (yyyy-MM-dd) a DMARC aggregate report was actually received for this domain. The honest denominator for any activity claim: nothing before this day was observable, so a domain monitored for a week can never truthfully read as quiet for ninety days. Null until the first report arrives.","example":"2025-09-14"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","url","dmarc_policy","dmarc_options","spf_options","managed_dns_status","managed_dns_status_since","monitoring_status","monitoring_failure_reason","monitoring_failure_detail","monitoring_email","open_tasks_count","highest_open_task_priority","score","score_band","is_parked","is_dmarc_managed","is_spf_managed","is_dkim_managed","is_bimi_managed","is_mta_sts_managed","bimi_options","bimi_error_reason","dmarc_error_reason","spf_error_reason","dkim_error_reason","mta_sts_error_reason","dns_conflicts","bimi_cname_target","bimi_txt_value","target_spf","last_14_days_volume","last_14_days_dmarc_pass_pct","last_14_days_spf_pass_pct","last_14_days_dkim_pass_pct","group_id","registrar","dmarc_setup_at","dmarc_verified_at","monitoring_since","created_at","updated_at"]}}}}}},"delete":{"tags":["Domain"],"summary":"Delete a domain","operationId":"deleteDomain","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"object":{"type":"string"},"id":{"type":"string","format":"uuid"}},"required":["deleted","object","id"]}}}}}}},"/domains/bulk":{"post":{"tags":["Domain"],"summary":"Add domains in bulk","operationId":"createDomainsBulk","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"domains":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"group_id":{"type":"string","format":"uuid"}},"required":["url"]},"minItems":1,"maxItems":50}},"required":["domains"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"number","description":"How many domains were created."},"failed":{"type":"number","description":"How many entries could not be created."},"results":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","description":"The domain from the request, echoed in the order it was sent."},"status":{"type":"string","enum":["created","failed"]},"domain":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"dmarc_policy":{"type":"string"},"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":{"$ref":"#/components/schemas/DmarcPolicy"},"pct":{"type":"number","minimum":0,"maximum":100},"sp":{"type":"string","enum":["none","quarantine","reject","same"]},"strict_spf":{"type":"boolean"},"strict_dkim":{"type":"boolean"}},"required":["additional_emails","policy","pct","sp","strict_spf","strict_dkim"]},"spf_options":{"type":"object","properties":{"force_flattening":{"type":"boolean"},"enabled":{"type":"boolean"},"mechanism_descriptions":{"type":"object","additionalProperties":{"type":"string"}}},"required":["force_flattening","enabled"]},"managed_dns_status":{"type":"object","properties":{"dmarc":{"$ref":"#/components/schemas/DnsStatus"},"spf":{"$ref":"#/components/schemas/DnsStatus"},"dkim":{"$ref":"#/components/schemas/DnsStatus"},"bimi":{"$ref":"#/components/schemas/DnsStatus"},"mta_sts":{"$ref":"#/components/schemas/DnsStatus"}},"required":["dmarc","spf","dkim","bimi","mta_sts"]},"managed_dns_status_since":{"type":"object","properties":{"dmarc":{"type":"string","nullable":true,"format":"date-time"},"spf":{"type":"string","nullable":true,"format":"date-time"},"dkim":{"type":"string","nullable":true,"format":"date-time"},"bimi":{"type":"string","nullable":true,"format":"date-time"},"mta_sts":{"type":"string","nullable":true,"format":"date-time"}},"required":["dmarc","spf","dkim","bimi","mta_sts"],"description":"When each managed record last entered the \"syncing\" status. For dmarc, falls back to dmarc_setup_at when the transition predates tracking; other records are null when they have never synced or the transition predates tracking. Useful for estimating how long a \"syncing\" record has been waiting on DNS propagation."},"monitoring_status":{"$ref":"#/components/schemas/DmarcSetupState"},"monitoring_failure_reason":{"$ref":"#/components/schemas/DmarcSetupFailureReason"},"monitoring_failure_detail":{"$ref":"#/components/schemas/DmarcSetupFailureDetail"},"monitoring_email":{"type":"string"},"open_tasks_count":{"type":"number","minimum":0,"description":"Open tickets on this domain. Opportunity tickets such as set_up_bimi are not counted, and `expand[]=tasks` leaves them out to match."},"highest_open_task_priority":{"type":"integer","nullable":true,"minimum":0,"maximum":3,"description":"Priority of the most urgent open task on this domain, on the same inverted scale as a task's own priority: 0 critical, 1 high, 2 medium, 3 low. Null when the domain has no open tasks. Sort on \"severity\" to order domains by it."},"score":{"type":"number","minimum":0,"maximum":100},"score_band":{"type":"string","enum":["critical","bad","good","great"],"description":"Risk band the score falls in: critical, bad, good, or great. The same banding the dashboard shows, so clients do not have to reimplement the thresholds."},"is_parked":{"type":"boolean"},"is_dmarc_managed":{"type":"boolean"},"is_spf_managed":{"type":"boolean"},"is_dkim_managed":{"type":"boolean"},"is_bimi_managed":{"type":"boolean"},"is_mta_sts_managed":{"type":"boolean"},"bimi_options":{"type":"object","properties":{"enabled":{"type":"boolean"},"logo":{"type":"string","nullable":true,"format":"uri"},"certificate":{"type":"string","nullable":true,"format":"uri"}},"required":["enabled","logo","certificate"]},"bimi_error_reason":{"$ref":"#/components/schemas/ManagedDnsErrorReason"},"dmarc_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.dmarc is \"error\". \"record_conflict\" means _dmarc holds records that conflict with each other — a CNAME coexisting with other data, or more than one DMARC record — and the domain's DMARC policy is not reliably enforced. See dns_conflicts for the offending records. Null when there is no error or the error predates reason tracking."}]},"spf_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.spf is \"error\". See dmarc_error_reason for the meaning of each value."}]},"dkim_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.dkim is \"error\". See dmarc_error_reason for the meaning of each value."}]},"mta_sts_error_reason":{"allOf":[{"$ref":"#/components/schemas/ManagedDnsErrorReason"},{"description":"Why managed_dns_status.mta_sts is \"error\". See dmarc_error_reason for the meaning of each value."}]},"dns_conflicts":{"type":"array","items":{"$ref":"#/components/schemas/DnsConflict"},"description":"Unresolved record conflicts observed at labels Palisade manages or monitors for this domain. Empty for a healthy domain. Each entry quotes the offending records verbatim along with what every authoritative nameserver and public resolver returned, so it can be acted on without a second investigation."},"bimi_cname_target":{"type":"string","description":"The host a customer CNAMEs default._bimi.{domain} to for Palisade-served BIMI."},"bimi_txt_value":{"type":"string","nullable":true,"description":"The fully-formed BIMI TXT value, populated whenever bimi_options.logo is set (via asset ingestion or a direct bimi_options update). The a= tag is included only when a certificate URL is also set. Customers who prefer not to delegate via CNAME can publish this value at default._bimi.{domain} themselves."},"target_spf":{"type":"string","nullable":true},"last_14_days_volume":{"type":"number","minimum":0},"last_14_days_dmarc_pass_pct":{"type":"number","minimum":0,"maximum":100},"last_14_days_spf_pass_pct":{"type":"number","minimum":0,"maximum":100},"last_14_days_dkim_pass_pct":{"type":"number","minimum":0,"maximum":100},"group_id":{"type":"string","nullable":true,"format":"uuid"},"registrar":{"type":"string","nullable":true},"dmarc_setup_at":{"type":"string","nullable":true,"format":"date-time"},"dmarc_verified_at":{"type":"string","nullable":true,"format":"date-time","description":"When the domain first reached the \"setup\" monitoring status and DMARC report collection began."},"monitoring_since":{"type":"string","nullable":true,"description":"The first day (yyyy-MM-dd) a DMARC aggregate report was actually received for this domain. The honest denominator for any activity claim: nothing before this day was observable, so a domain monitored for a week can never truthfully read as quiet for ninety days. Null until the first report arrives.","example":"2025-09-14"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","url","dmarc_policy","dmarc_options","spf_options","managed_dns_status","managed_dns_status_since","monitoring_status","monitoring_failure_reason","monitoring_failure_detail","monitoring_email","open_tasks_count","highest_open_task_priority","score","score_band","is_parked","is_dmarc_managed","is_spf_managed","is_dkim_managed","is_bimi_managed","is_mta_sts_managed","bimi_options","bimi_error_reason","dmarc_error_reason","spf_error_reason","dkim_error_reason","mta_sts_error_reason","dns_conflicts","bimi_cname_target","bimi_txt_value","target_spf","last_14_days_volume","last_14_days_dmarc_pass_pct","last_14_days_spf_pass_pct","last_14_days_dkim_pass_pct","group_id","registrar","dmarc_setup_at","dmarc_verified_at","monitoring_since","created_at","updated_at"],"description":"The created domain, or null when this entry failed."},"error":{"type":"object","nullable":true,"properties":{"code":{"type":"string","nullable":true},"message":{"type":"string","nullable":true}},"required":["code","message"],"description":"Why this entry failed, or null when it succeeded."}},"required":["url","status","domain","error"]},"description":"One entry per submitted domain, in request order. Entries are independent: a failure here does not roll back the domains that succeeded, and the response is 200 even when every entry failed, so inspect \"failed\" rather than the status code."}},"required":["created","failed","results"]}}}}}}},"/domains/{id}/bimi/assets":{"post":{"tags":["Domain"],"summary":"Upload BIMI assets","operationId":"uploadBimiAssets","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Ingests a Verified Mark Certificate (VMC), extracts and verifies the logotype SVG embedded in the certificate, and hosts the matched certificate + logo pair at per-domain asset URLs with a short content-derived version suffix ({domain}/bimi/logo-{v}.svg and {domain}/bimi/certchain-{v}.pem). Re-posting the same PEM returns the same URLs; a new PEM gets fresh URLs so caches never serve stale assets.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/IngestBimiAssetsMultipart"}},"application/json":{"schema":{"$ref":"#/components/schemas/IngestBimiAssetsBody"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BimiAssetsResponse"}}}},"422":{"description":"Certificate rejected","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","enum":["bimi_certificate_invalid"]},"reason":{"type":"string","enum":["pem_parse_failed","not_a_vmc","logotype_missing","logo_hash_mismatch","svg_not_tiny_ps","incomplete_chain","chain_untrusted","chain_expired","chain_signature_invalid","certificate_not_yet_valid","san_mismatch","contains_private_key"]},"message":{"type":"string"}},"required":["code","reason","message"]}}}}}}},"/domains/{id}/dns-records":{"get":{"tags":["Domain"],"summary":"Get DNS records","operationId":"getDomainDnsRecords","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Returns the consolidated, server-computed list of DNS records to publish for this domain, covering every enabled Palisade-managed feature (DMARC, SPF, DKIM, MTA-STS, TLS-RPT, BIMI) with per-record verification status. Records that are not yet verified are compared against live DNS, so \"action\" reflects what is actually published and \"already_published\" identifies records that need no change. Also reports the detected DNS provider hosting the domain, resolved from its live NS records, so the records can be published with a matching DNS integration.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DnsRecordsResponse"}}}}}}},"/domains/{id}/verify":{"post":{"operationId":"verifyDomain","tags":["Domain"],"summary":"Verify a domain","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Queues full domain monitoring and synchronously re-checks records that are still propagating. \"results\" reports the purposes confirmed during this pass using the same slugs as GET /domains/{id}/dns-records.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyDomainResponse"}}}}}}},"/domains/{id}/verify/spf":{"post":{"operationId":"verifyDomainSpf","tags":["Domain"],"summary":"Verify SPF for a domain","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Queues SPF verification and synchronously re-checks records that are still propagating. \"results\" reports the purposes confirmed during this pass using the same slugs as GET /domains/{id}/dns-records.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyDomainResponse"}}}}}}},"/domains/{id}/verify/dkim":{"post":{"operationId":"verifyDomainDkim","tags":["Domain"],"summary":"Verify DKIM for a domain","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Queues DKIM verification and synchronously re-checks records that are still propagating. \"results\" reports the purposes confirmed during this pass using the same slugs as GET /domains/{id}/dns-records.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyDomainResponse"}}}}}}},"/domains/{id}/hosted-dmarc":{"post":{"tags":["Domain"],"summary":"Enable hosted DMARC","operationId":"enableHostedDmarc","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Rebuilds the Palisade-hosted DMARC record from the domain's current policy, publishes it, and returns the CNAME to point _dmarc at. Call this before showing anyone the CNAME: the delegation target is otherwise whatever was written when the domain was created, so a domain whose owner tightened their own DMARC policy since then would be switched onto a weaker one. Nothing is written to the database unless the publish succeeds, so a failure is safe to retry, and calling it on a domain that is already hosted changes nothing and answers hosted_dmarc_active: true.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnableHostedDmarcResponse"}}}}}}},"/domains/{domainId}/plan":{"get":{"tags":["Domain"],"summary":"Get the remediation plan","operationId":"getDomainPlan","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"The work Palisade has planned for this domain, as the four stages of the DMARC journey with the steps each is made of. A step Palisade has already opened tickets for carries their ids in task_ids and is readable through GET /tasks; a step it has not carries none, and is work it expects to open later. upcoming_steps is the shortlist of what comes next, which is what the app shows under a domain's open tickets. Read /tasks for what is actionable now and this for what is coming. Computed on read, so nothing here is a stored record and none of it emits webhooks.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainPlan"}}}}}}},"/domains/{domainId}/sources":{"get":{"tags":["DomainSource"],"summary":"List confirmed senders","operationId":"listDomainSources","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DomainSource"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}}},"/domains/{domainId}/sources/{sourceId}":{"post":{"tags":["DomainSource"],"summary":"Confirm or update a sender","operationId":"updateDomainSource","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"sourceId","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"confirmation_status":{"type":"string","enum":["confirmed","discarded","requires_confirmation"]}},"required":["confirmation_status"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainSource"}}}}}}},"/domains/{domainId}/dkim-keys":{"get":{"tags":["DkimKey"],"summary":"List DKIM keys","operationId":"listDkimKeys","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string"},"required":false,"name":"selector","in":"query"},{"schema":{"$ref":"#/components/schemas/DnsStatus"},"required":false,"name":"dns_status","in":"query"},{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":50,"default":20},"required":false,"name":"per_page","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DkimKey"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}},"post":{"tags":["DkimKey"],"summary":"Add a DKIM key","operationId":"createDkimKey","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"selector":{"type":"string","minLength":1},"record_type":{"type":"string","enum":["TXT","CNAME"]},"value":{"type":"string","minLength":1},"ttl":{"type":"number","minimum":300,"maximum":86400}},"required":["selector","record_type","value"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DkimKey"}}}}}}},"/domains/{domainId}/dkim-keys/{id}":{"get":{"tags":["DkimKey"],"summary":"Get a DKIM key","operationId":"getDkimKey","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DkimKey"}}}}}},"post":{"tags":["DkimKey"],"summary":"Update a DKIM key","operationId":"updateDkimKey","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"selector":{"type":"string","minLength":1},"record_type":{"type":"string","enum":["TXT","CNAME"]},"value":{"type":"string","minLength":1},"ttl":{"type":"number","minimum":300,"maximum":86400}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DkimKey"}}}}}},"delete":{"tags":["DkimKey"],"summary":"Delete a DKIM key","operationId":"deleteDkimKey","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"object":{"type":"string"},"id":{"type":"string","format":"uuid"}},"required":["deleted","object","id"]}}}}}}},"/domains/{domainId}/reports/summary":{"get":{"tags":["Reports"],"summary":"Get the DMARC summary","operationId":"getDmarcSummary","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"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":false,"description":"Reporting window, ending yesterday. Defaults to last_7_days.","name":"period","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DmarcSummary"}}}}}}},"/domains/{domainId}/reports/senders":{"get":{"tags":["Reports"],"summary":"List DMARC senders","operationId":"listDmarcSenders","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"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":false,"description":"Reporting window, ending yesterday. Defaults to last_7_days.","name":"period","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":20,"default":10,"description":"How many senders to return, highest volume first. Maximum 20."},"required":false,"description":"How many senders to return, highest volume first. Maximum 20.","name":"limit","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DmarcSenders"}}}}}}},"/dns-providers/detect":{"get":{"tags":["DnsProvider"],"summary":"Detect the DNS provider","operationId":"detectDnsProvider","description":"Detect which DNS provider hosts a domain, from its live NS records. Returns a null provider when the nameservers match no known provider.","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"domain","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","nullable":true}},"required":["provider"]}}}}}}},"/domains/{domainId}/dns-snapshots":{"get":{"tags":["DnsSnapshot"],"summary":"List DNS snapshots","operationId":"listDnsSnapshots","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"from_date","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"to_date","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["spf","dmarc","bimi","mx","dkim","ns","mta_sts","tls_rpt"]}},"required":false,"style":"form","explode":true,"name":"record_types","in":"query"},{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":50,"default":20},"required":false,"name":"per_page","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"spf":{"type":"array","items":{"type":"string"}},"dmarc":{"type":"array","items":{"type":"string"}},"bimi":{"type":"array","items":{"type":"string"}},"mx":{"type":"array","items":{"type":"object","properties":{"exchange":{"type":"string"},"priority":{"type":"number"}},"required":["exchange","priority"]}},"dkim":{"type":"array","items":{"type":"object","properties":{"selector":{"type":"string"},"value":{"type":"string"}},"required":["selector","value"]}},"ns":{"type":"array","items":{"type":"string"}},"mta_sts":{"type":"array","items":{"type":"string"}},"tls_rpt":{"type":"array","items":{"type":"string"}},"records_updated":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","format":"date-time"}},"required":["id","spf","dmarc","bimi","mx","dkim","ns","mta_sts","tls_rpt","records_updated","created_at"]}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}}},"/domains/{domainId}/dns-snapshots/{id}":{"get":{"tags":["DnsSnapshot"],"summary":"Get a DNS snapshot","operationId":"getDnsSnapshot","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"array","items":{"type":"string","enum":["diff"]}},"required":false,"style":"form","explode":true,"name":"expand","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DnsSnapshot"}}}}}}},"/groups":{"get":{"tags":["Group"],"summary":"List groups","operationId":"listGroups","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"query","in":"query"},{"schema":{"type":"string","enum":["created_at","name"]},"required":false,"name":"sort","in":"query"},{"schema":{"type":"string","enum":["desc","asc"]},"required":false,"name":"sort_direction","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":100},"required":false,"name":"per_page","in":"query"},{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["domains"]}},"required":false,"style":"form","explode":true,"name":"expand","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Group"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}},"post":{"tags":["Group"],"summary":"Create a group","operationId":"createGroup","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1}},"required":["name"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"external_customer_id":{"type":"string","nullable":true},"external_billing_entity_id":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","name","external_customer_id","external_billing_entity_id","created_at","updated_at"]}}}}}}},"/groups/{id}":{"get":{"tags":["Group"],"summary":"Get a group","operationId":"getGroup","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Group"}}}}}},"post":{"tags":["Group"],"summary":"Update a group","operationId":"updateGroup","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"external_customer_id":{"type":"string","nullable":true},"external_billing_entity_id":{"type":"string","nullable":true},"backfill_tickets":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"external_customer_id":{"type":"string","nullable":true},"external_billing_entity_id":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","name","external_customer_id","external_billing_entity_id","created_at","updated_at"]}}}}}},"delete":{"tags":["Group"],"summary":"Delete a group","operationId":"deleteGroup","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"object":{"type":"string"},"id":{"type":"string","format":"uuid"}},"required":["deleted","object","id"]}}}}}}},"/invitations":{"get":{"tags":["Invitation"],"summary":"List pending invitations","operationId":"listInvitations","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Invitation"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}},"post":{"tags":["Invitation"],"summary":"Invite a teammate","operationId":"createInvitation","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"The address to invite."},"roles":{"type":"array","items":{"type":"string","enum":["admin","editor","viewer"]}},"groups":{"type":"array","items":{"type":"string","format":"uuid"}},"ttl_sec":{"type":"number","minimum":86400,"maximum":2592000,"default":1209600}},"required":["email","roles"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Invitation"}},"required":["data"]}}}},"422":{"description":"invitation_domain_not_allowed: API-key callers only, the invitee email domain does not match any existing member email domain of the organization; contact support to add a new domain, and note that dashboard sessions are not subject to this check. invitation_invitee_exists: the email address already has a Palisade account, so an invitation would send it to a signup screen it cannot complete; use POST /members to add the existing account instead.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/InvitationDomainNotAllowed"},{"$ref":"#/components/schemas/InvitationInviteeExists"}]}}}},"502":{"description":"upstream_failure: the identity provider returned an error while creating the invitation. The invitation may still have been created without its email being sent; list invitations to check before retrying, since a retry creates a second invitation to the same address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationUpstreamFailure"}}}}}}},"/invitations/{id}/resend":{"post":{"tags":["Invitation"],"summary":"Resend an invitation","operationId":"resendInvitation","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"204":{"description":"No Content","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"502":{"description":"upstream_failure: the identity provider returned an error while replacing the invitation. The message says whether the old invitation still stands, was removed, or could not be checked; list invitations before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationUpstreamFailure"}}}}}}},"/invitations/{id}":{"delete":{"tags":["Invitation"],"summary":"Revoke an invitation","operationId":"deleteInvitation","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"object":{"type":"string"},"id":{"type":"string","format":"uuid"}},"required":["deleted","object","id"]}}}}}}},"/tasks":{"get":{"tags":["Task"],"summary":"List tasks","operationId":"listTasks","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"By default, the API returns 'open' and 'pending' tickets. To filter by other statuses, use the 'statuses' query parameter.","parameters":[{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskStatus"},"description":"By default, we return OPEN and PENDING tasks."},"required":false,"style":"form","explode":true,"name":"statuses","in":"query"},{"schema":{"type":"string","enum":["created_at","priority","resolved_at","sequence","status"],"default":"priority","description":"Defaults to 'priority', most urgent first, with ties broken by rollout stage then recency — so among equally urgent tickets, DMARC setup comes before the SPF and DKIM work that depends on its reports. 'sequence' walks the rollout outright: DMARC setup, foundation records, SPF/DKIM alignment, cleanup, enforcement, hardening. 'status' ranks pending tickets first, then open, snoozed, done, dismissed and canceled, breaking ties by priority, sequence, then most recent. 'resolved_at' orders by when a ticket was completed, most recent first when descending; tickets never completed sort last."},"required":false,"name":"sort","in":"query"},{"schema":{"type":"string","enum":["desc","asc"],"default":"asc"},"required":false,"name":"sort_direction","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":50},"required":false,"name":"per_page","in":"query"},{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"domain","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"group","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["domain","source"]}},"required":false,"style":"form","explode":true,"name":"expand","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Task"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}}},"/tasks/{id}":{"get":{"tags":["Task"],"summary":"Get a task","operationId":"getTask","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"array","items":{"type":"string","enum":["domain","instructions","source"]}},"required":false,"style":"form","explode":true,"name":"expand","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}}}},"post":{"tags":["Task"],"summary":"Update a task","operationId":"updateTask","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/TaskStatus"},"ignore_spf":{"type":"boolean","description":"Stops counting the ticket's sender against SPF on its domain. Ignoring both SPF and DKIM for the ticket's sender discards it, which answers the sender's confirm_source ticket: a confirm_source ticket updated so that both are ignored is closed as done unless the status sent is dismissed or canceled, and any other open confirm_source ticket for the same sender closes as well."},"ignore_dkim":{"type":"boolean","description":"Stops counting the ticket's sender against DKIM on its domain. Ignoring both SPF and DKIM for the ticket's sender discards it, which answers the sender's confirm_source ticket: a confirm_source ticket updated so that both are ignored is closed as done unless the status sent is dismissed or canceled, and any other open confirm_source ticket for the same sender closes as well."},"confirm_source":{"type":"boolean","description":"Confirms the ticket's sender for its domain. That answers the sender's confirm_source ticket, so a confirm_source ticket updated with this flag is closed as done unless the status sent is dismissed or canceled, and any other open confirm_source ticket for the same sender closes as well."},"confirm_source_in_use":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"domain_id":{"type":"string","format":"uuid"},"sending_domain":{"type":"string","nullable":true},"source_id":{"type":"string","nullable":true,"format":"uuid"},"source_alias":{"type":"string","nullable":true},"priority":{"type":"number"},"sequence":{"type":"number","description":"Where the task sits in the email-authentication rollout, lower first: 10 DMARC setup, 20 foundation records, 30 SPF/DKIM alignment, 40 cleanup, 50 enforcement, 60 hardening. Separate from priority, which is urgency; sequence orders tasks of equal priority, since an alignment task cannot be worked before DMARC reporting exists to show the failure."},"status":{"$ref":"#/components/schemas/TaskStatus"},"type":{"$ref":"#/components/schemas/TaskType"},"description":{"type":"string"},"snoozed_until":{"type":"string","nullable":true,"format":"date-time"},"marked_pending_at":{"type":"string","nullable":true,"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"resolved_at":{"type":"string","nullable":true,"format":"date-time"},"resolution_reason":{"$ref":"#/components/schemas/TaskResolutionReason"}},"required":["id","domain_id","sending_domain","source_id","source_alias","priority","sequence","status","type","description","snoozed_until","marked_pending_at","created_at","updated_at","resolved_at","resolution_reason"]}}}}}}},"/organizations":{"post":{"tags":["Organization"],"summary":"Create an organization","operationId":"createOrganization","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":3,"maxLength":128},"invite_email":{"type":"string","nullable":true,"format":"email","description":"If provided, an invitation will be sent to this email to join the organization as an admin."},"referer":{"type":"string","nullable":true,"description":"This field should be omitted or kept null when using an API key."},"pricing_type":{"type":"string","enum":["free","it_teams","msp"],"description":"Self-serve plan to provision. Only honored when PLG is enabled; otherwise the default plan is used."},"attribution_id":{"type":"string","format":"uuid","description":"Opaque first-party journey id carried from palisade.email. Ignored for non-self-serve creation."}},"required":["name","invite_email"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedOrganization"}}}}}}},"/organizations/{id}":{"post":{"tags":["Organization"],"summary":"Update an organization","operationId":"updateOrganization","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"is_active":{"type":"boolean","description":"Whether or not the organization should be active."},"name":{"type":"string","minLength":3,"maxLength":128},"logo_url":{"type":"string","format":"uri"},"email":{"type":"string","format":"email","description":"Organization contact email address."},"phone_number":{"type":"string","minLength":1,"maxLength":50,"description":"Organization contact phone number."},"website":{"type":"string","format":"uri","description":"Organization website URL."},"settings":{"type":"object","properties":{"email_notifications":{"type":"object","properties":{"domain_drifted":{"type":"boolean","default":true,"description":"Send notifications when domain DNS records drift from expected configuration."},"domain_failed":{"type":"boolean","default":true,"description":"Send notifications when a domain fails DMARC validation checks."},"domain_setup":{"type":"boolean","default":true,"description":"Send notifications when a domain DMARC configuration is successfully set up."},"tasks_digests":{"type":"boolean","default":true,"description":"Send daily digest of new and pending tasks."},"setup_reminders":{"type":"boolean","default":true,"description":"Send reminders to finish adding and verifying a first domain."},"first_report":{"type":"boolean","default":true,"description":"Send an email when a newly set-up domain's first DMARC data arrives, and its first report a week later."},"invitation_reminders":{"type":"boolean","default":true,"description":"Remind invitees about an unaccepted invitation, and tell the inviter which invitations are about to expire."},"billing_notices":{"type":"boolean","default":true,"description":"Send plan-usage notices, such as passing the Free plan monthly email allowance."},"hosted_spf_live":{"type":"boolean","default":true,"description":"Send a one-time email when a domain's Hosted SPF setup finishes and starts serving."},"delegation_missing":{"type":"boolean","default":true,"description":"Send a notification when a domain never delegated to a record Palisade hosts for it, so the hosted record is not serving. Controlled separately from domain_drifted."}},"description":"Email notification preferences for the organization. Controls which types of emails are sent."}},"additionalProperties":false,"description":"Organization settings including email notification preferences."},"require_mfa":{"type":"boolean","description":"Require every member to complete multi-factor authentication at each sign-in. Only an administrator of this organization can change it, from a signed-in session rather than an API key, and changing it in either direction is refused with `mfa_confirmation_required` until that administrator's own sign-in has completed multi-factor authentication."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}}}}},"delete":{"tags":["Organization"],"summary":"Delete an organization","operationId":"deleteOrganization","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"202":{"description":"Accepted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]},"object":{"type":"string","enum":["organization"]},"id":{"type":"string"}},"required":["deleted","object","id"]}}}}}}},"/organizations/{id}/onboarding/dismiss":{"post":{"tags":["Organization"],"summary":"Dismiss onboarding","operationId":"dismissOrganizationOnboarding","description":"Records that the onboarding wizard has been dismissed or finished, so it is not offered again. Idempotent: a second call keeps the first timestamp. Requires domains:read, the same permission that decides whether the wizard is shown.","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}}}}}},"/usage":{"get":{"tags":["Usage"],"summary":"Get domain usage counts","operationId":"getUsage","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"date"},"required":true,"name":"date_from","in":"query"},{"schema":{"type":"string","format":"date"},"required":true,"name":"date_to","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["domains","organizations","line_items","aligned_messages"]}},"required":false,"style":"form","explode":true,"name":"expand","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"}}}}}}},"/members":{"get":{"tags":["Member"],"summary":"List organization members","operationId":"listMembers","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Member"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}},"post":{"tags":["Member"],"summary":"Add an existing account","operationId":"addMember","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Adds an existing Palisade account to your organization. Use this when the person already has an account: POST /invitations sends them to a signup screen they cannot complete, and returns invitation_invitee_exists for such an address. Returns 201 when the account is added and 200 when it was already a member, in which case the requested roles and groups are still applied.","parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Email address of an existing Palisade account. Use POST /invitations for someone who has no account yet."},"roles":{"type":"array","items":{"$ref":"#/components/schemas/MemberRole"}},"groups":{"type":"array","items":{"type":"string","format":"uuid"}}},"required":["email","roles"],"additionalProperties":false}}}},"responses":{"200":{"description":"Already a member; roles and groups applied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}},"201":{"description":"Added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}},"400":{"description":"No Palisade account exists for this email address. Send an invitation instead.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberAccountNotFound"}}}}}}},"/members/{id}":{"post":{"tags":["Member"],"summary":"Update a member","operationId":"updateMember","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"roles":{"type":"array","items":{"$ref":"#/components/schemas/MemberRole"}},"groups":{"type":"array","items":{"type":"string","format":"uuid"}}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}}}},"delete":{"tags":["Member"],"summary":"Remove a member","operationId":"removeMember","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"object":{"type":"string"},"id":{"type":"string","format":"uuid"}},"required":["deleted","object","id"]}}}}}}},"/members/mfa/statuses":{"get":{"tags":["Member"],"summary":"List member MFA statuses","operationId":"listMemberMfaStatuses","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMembersMfaStatusesResponse"}}}}}}},"/domains/{domainId}/mta-sts":{"get":{"tags":["MtaSts"],"summary":"Get MTA-STS status","operationId":"getMtaSts","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MtaSts"}}}}}},"post":{"tags":["MtaSts"],"summary":"Enable MTA-STS","operationId":"enableMtaSts","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["testing","enforce","none"],"default":"testing"},"max_age":{"type":"number","minimum":86400,"maximum":31557600,"default":86400}},"default":{},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MtaSts"}}}}}},"put":{"tags":["MtaSts"],"summary":"Update the MTA-STS policy","operationId":"updateMtaStsPolicy","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["testing","enforce","none"]},"max_age":{"type":"number","minimum":86400,"maximum":31557600}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MtaSts"}}}}}},"delete":{"tags":["MtaSts"],"summary":"Disable MTA-STS","operationId":"disableMtaSts","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MtaSts"}}}}}}},"/domains/{domainId}/mta-sts/retry":{"post":{"tags":["MtaSts"],"summary":"Retry a failed MTA-STS setup","operationId":"retryMtaSts","description":"Requests a fresh certificate for mta-sts.{domain} after a failed setup. The domain must already have MTA-STS hosting enabled, must currently be in the error state, and must be publishing the mta-sts CNAME, otherwise the retry is refused with an explanation. A setup still in progress is refused rather than restarted. Returns the domain unchanged when its certificate is already active.","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MtaSts"}}}}}}},"/report-data/summary":{"get":{"tags":["Report Data"],"summary":"Get the report summary","operationId":"getReportSummary","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["last_1_day","last_7_days","last_30_days","last_365_days"]},"required":false,"name":"period","in":"query"},{"schema":{"type":"string"},"required":false,"name":"date_from","in":"query"},{"schema":{"type":"string"},"required":false,"name":"date_to","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportSummary"}}}}}}},"/report-data/domains":{"get":{"tags":["Report Data"],"summary":"List domain report scores","operationId":"listReportDomains","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["last_1_day","last_7_days","last_30_days","last_365_days"]},"required":false,"name":"period","in":"query"},{"schema":{"type":"string"},"required":false,"name":"date_from","in":"query"},{"schema":{"type":"string"},"required":false,"name":"date_to","in":"query"},{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":50},"required":false,"name":"per_page","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"group_id","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ReportDomain"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}}},"/report-data/domains/{domainId}/senders":{"get":{"tags":["Report Data"],"summary":"List senders for a domain","operationId":"listReportDomainSenders","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","enum":["last_1_day","last_7_days","last_30_days","last_365_days"]},"required":false,"name":"period","in":"query"},{"schema":{"type":"string"},"required":false,"name":"date_from","in":"query"},{"schema":{"type":"string"},"required":false,"name":"date_to","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDomainSenders"}}}}}}},"/report-data/domains/{domainId}/timeseries":{"get":{"tags":["Report Data"],"summary":"Get daily volume","operationId":"getReportDomainTimeseries","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","enum":["last_1_day","last_7_days","last_30_days","last_365_days"]},"required":false,"name":"period","in":"query"},{"schema":{"type":"string"},"required":false,"name":"date_from","in":"query"},{"schema":{"type":"string"},"required":false,"name":"date_to","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDomainTimeseries"}}}}}}},"/prospecting-reports/{id}/download":{"get":{"tags":["Prospecting Reports"],"summary":"Download the hosted PDF","operationId":"downloadProspectingReport","description":"Serves the report to anyone holding the link — no authentication, since the recipient is a prospect. Reports are available for 90 days from creation; after that this returns 410 with a page explaining that the report expired, or the JSON below when the request asks for it.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The report as a PDF","content":{"application/json":{"schema":{"type":"string","format":"binary"}}}},"410":{"description":"The report has expired and is no longer available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectingReportGone"}}}}}}},"/prospecting-reports":{"get":{"tags":["Prospecting Reports"],"summary":"List prospecting reports","description":"Prospecting reports created through this API, newest first. API keys see every one in the organization. A member limited to groups sees only the reports they created and the reports whose domains are all monitored domains in their groups.","operationId":"listProspectingReports","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":50},"required":false,"name":"per_page","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ProspectingReport"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}},"post":{"tags":["Prospecting Reports"],"summary":"Generate a prospecting report","operationId":"createProspectingReport","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Audits each domain against live DNS, renders the email security report as a PDF, and hosts it. The domains are looked up publicly and do not need to be in your account, which is what makes this usable on a prospect. Up to 10 domains per report. The response carries a url anyone with the link can open, good for 90 days; delete the report to stop it resolving sooner.","parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"generated_for":{"type":"string","minLength":1},"domains":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":10},"language":{"type":"string","enum":["en","fr"],"default":"en"},"include_organization_info":{"type":"boolean","default":true},"include_bimi":{"type":"boolean","default":true},"include_dns_records":{"type":"boolean","default":false},"include_potential_loss":{"type":"boolean","default":true},"theme":{"type":"string","enum":["light","dark"],"default":"light","description":"The skin the report is drawn in: light on paper, or dark on a near-black ground."},"currency":{"type":"string","enum":["USD","CAD","EUR","GBP","AUD"],"default":"USD","description":"The currency every price band in the report is printed in."},"deadline_weeks":{"type":"integer","minimum":1,"maximum":52,"default":6,"description":"How many weeks the plan and closing pages give the reader to fix what the report found."}},"required":["generated_for","domains"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectingReport"}}}}}}},"/prospecting-reports/preview":{"post":{"tags":["Prospecting Reports"],"summary":"Preview a report","operationId":"previewProspectingReport","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Runs the same audit as POST /prospecting-reports and returns the report itself rather than a link to a hosted PDF. `format: \"json\"` returns the report as data -- every score, finding and figure the PDF prints, with the wording resolved for the requested `language` -- so you can render it in your own layout. `format: \"html\"` returns the same report as a complete, self-contained HTML document, styled by Palisade and carrying your organization's name, logo and contact details. Nothing is stored and no link is minted, so use this for a page rendered on demand and POST /prospecting-reports when the recipient needs a PDF they can keep.","parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"generated_for":{"type":"string","minLength":1},"domains":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":10},"language":{"type":"string","enum":["en","fr"],"default":"en"},"include_organization_info":{"type":"boolean","default":true},"include_bimi":{"type":"boolean","default":true},"include_dns_records":{"type":"boolean","default":false},"include_potential_loss":{"type":"boolean","default":true},"theme":{"type":"string","enum":["light","dark"],"default":"light","description":"The skin the report is drawn in: light on paper, or dark on a near-black ground."},"currency":{"type":"string","enum":["USD","CAD","EUR","GBP","AUD"],"default":"USD","description":"The currency every price band in the report is printed in."},"deadline_weeks":{"type":"integer","minimum":1,"maximum":52,"default":6,"description":"How many weeks the plan and closing pages give the reader to fix what the report found."},"format":{"type":"string","enum":["json","html"],"default":"json","description":"json returns the report as data; html returns the same report as a complete, self-contained HTML document."}},"required":["generated_for","domains"]}}}},"responses":{"200":{"description":"The report as data, or as an HTML document when format is \"html\"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectingReportContent"}},"text/html":{"schema":{"type":"string"}}}}}}},"/prospecting-reports/{id}":{"get":{"tags":["Prospecting Reports"],"summary":"Get a prospecting report","operationId":"getProspectingReport","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProspectingReport"}}}}}},"delete":{"tags":["Prospecting Reports"],"summary":"Delete a prospecting report","operationId":"deleteProspectingReport","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Deletes the report and the hosted PDF, so any link already sent out stops resolving.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"object":{"type":"string"},"id":{"type":"string","format":"uuid"}},"required":["deleted","object","id"]}}}}}}},"/scheduled-reports":{"get":{"tags":["Scheduled Reports"],"summary":"List scheduled reports","operationId":"listScheduledReports","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":50},"required":false,"name":"per_page","in":"query"},{"schema":{"type":"string"},"required":false,"name":"customer_name","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ScheduledReport"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}},"post":{"tags":["Scheduled Reports"],"summary":"Create a scheduled report","operationId":"createScheduledReport","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"customer_name":{"type":"string","minLength":1},"domain_ids":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1},"schedule_frequency":{"type":"string","minLength":1,"default":"0 0 1 * *"},"auto_send":{"type":"boolean","default":false},"email_recipients":{"type":"array","items":{"type":"string","format":"email"},"default":[]}},"required":["customer_name","domain_ids"]}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledReport"}}}}}}},"/scheduled-reports/{id}":{"get":{"tags":["Scheduled Reports"],"summary":"Get a scheduled report","operationId":"getScheduledReport","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledReport"}}}}}},"post":{"tags":["Scheduled Reports"],"summary":"Update a scheduled report","operationId":"updateScheduledReport","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"customer_name":{"type":"string","minLength":1},"domain_ids":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1},"schedule_frequency":{"type":"string","minLength":1},"auto_send":{"type":"boolean"},"email_recipients":{"type":"array","items":{"type":"string","format":"email"}},"generated_at":{"type":"string","nullable":true,"format":"date-time"}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledReport"}}}}}},"delete":{"tags":["Scheduled Reports"],"summary":"Delete a scheduled report","operationId":"deleteScheduledReport","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"object":{"type":"string"},"id":{"type":"string","format":"uuid"}},"required":["deleted","object","id"]}}}}}}},"/support-requests":{"post":{"tags":["SupportRequest"],"summary":"Send a support request","operationId":"createSupportRequest","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":4000},"title":{"type":"string","minLength":1,"maxLength":100}},"required":["content","title"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["created"]}},"required":["status"]}}}}}}},"/support/tickets":{"get":{"tags":["SupportTicket"],"summary":"List support tickets","operationId":"listSupportTickets","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Returns paginated list of support tickets. Admins see all org tickets; other roles see only their own tickets. Filter by status or priority using query params.","parameters":[{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupportTicketStatus"}},"required":false,"style":"form","explode":true,"name":"status","in":"query"},{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupportTicketPriority"}},"required":false,"style":"form","explode":true,"name":"priority","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":50,"default":20},"required":false,"name":"per_page","in":"query"},{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SupportTicket"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}},"post":{"tags":["SupportTicket"],"summary":"Open a support ticket","operationId":"createSupportTicket","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Creates a new support ticket with an initial message. Optionally link to a hostname or task. Returns the created ticket ID.","parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","minLength":1,"maxLength":255},"content":{"type":"string","minLength":1},"priority":{"allOf":[{"$ref":"#/components/schemas/SupportTicketPriority"},{"default":"normal"}]},"hostname_id":{"type":"string","format":"uuid"},"attachments":{"type":"array","items":{"type":"object","properties":{"file_name":{"type":"string"},"file_url":{"type":"string","format":"uri"},"file_size":{"type":"number","nullable":true},"mime_type":{"type":"string","nullable":true},"attachment_type":{"$ref":"#/components/schemas/SupportTicketAttachmentType"}},"required":["file_name","file_url","attachment_type"]},"maxItems":3}},"required":["subject","content"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"required":["id"]}},"required":["data"]}}}}}}},"/support/tickets/{id}":{"get":{"tags":["SupportTicket"],"summary":"Get a support ticket","operationId":"getSupportTicket","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Returns a single ticket with its full message thread. Messages are ordered chronologically. Marks all support messages as read for the requesting user.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportTicketDetail"}}}}}},"patch":{"tags":["SupportTicket"],"summary":"Close or reopen a ticket","operationId":"updateSupportTicketStatus","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Updates ticket status. Users can close a resolved ticket or reopen a closed/resolved ticket.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/AllowedUserStatusUpdate"}},"required":["status"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"$ref":"#/components/schemas/SupportTicketStatus"}},"required":["id","status"]}}}}}}},"/support/tickets/{id}/messages":{"post":{"tags":["SupportTicket"],"summary":"Reply to a ticket","operationId":"replyToSupportTicket","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Adds a reply message to an existing ticket. Updates ticket status to PENDING_SUPPORT.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","minLength":1},"attachments":{"type":"array","items":{"type":"object","properties":{"file_name":{"type":"string"},"file_url":{"type":"string","format":"uri"},"file_size":{"type":"number","nullable":true},"mime_type":{"type":"string","nullable":true},"attachment_type":{"$ref":"#/components/schemas/SupportTicketAttachmentType"}},"required":["file_name","file_url","attachment_type"]},"maxItems":3}},"required":["content"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"}},"required":["id"]}}}}}}},"/support/unread-count":{"get":{"tags":["SupportTicket"],"summary":"Get the unread count","operationId":"getSupportUnreadCount","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Returns the count of unread support messages across visible tickets. Lightweight query for badge polling.","parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number"}},"required":["count"]}}}}}}},"/support/presigned-url":{"post":{"tags":["SupportTicket"],"summary":"Get an attachment upload URL","operationId":"createSupportAttachmentUploadUrl","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Generates a presigned URL for uploading support ticket attachments to S3. Supports images and PDFs up to 10MB.","parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"file_name":{"type":"string","minLength":1,"maxLength":255},"mime_type":{"$ref":"#/components/schemas/SupportAttachmentMimeType"}},"required":["file_name","mime_type"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"presigned_url":{"type":"string","format":"uri"},"public_url":{"type":"string","format":"uri"}},"required":["presigned_url","public_url"]}}}}}}},"/domains/{domainId}/tls-reports":{"get":{"tags":["TlsReport"],"summary":"List TLS-RPT reports","operationId":"listTlsReports","description":"List the TLS-RPT reports sending servers have submitted for this domain. Reports only arrive once the domain publishes an MTA-STS policy and a TLS-RPT record.","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":50,"default":20},"required":false,"name":"per_page","in":"query"},{"schema":{"type":"string","format":"date"},"required":false,"name":"date_from","in":"query"},{"schema":{"type":"string","format":"date"},"required":false,"name":"date_to","in":"query"},{"schema":{"type":"string","enum":["day","total_successful","total_failure","organization_name"]},"required":false,"name":"sort_by","in":"query"},{"schema":{"type":"string","enum":["desc","asc"]},"required":false,"name":"sort_order","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"hostname_id":{"type":"string"},"report_id":{"type":"string"},"organization_name":{"type":"string"},"contact_info":{"type":"string","nullable":true},"date_range_start":{"type":"string"},"date_range_end":{"type":"string"},"day":{"type":"string"},"policy_type":{"type":"string"},"policy_domain":{"type":"string"},"mx_host":{"type":"string","nullable":true},"total_successful":{"type":"number"},"total_failure":{"type":"number"},"created_at":{"type":"string"},"failures":{"type":"array","items":{"type":"object","properties":{"result_type":{"type":"string"},"sending_mta_ip":{"type":"string","nullable":true},"receiving_ip":{"type":"string","nullable":true},"receiving_mx_hostname":{"type":"string","nullable":true},"failed_session_count":{"type":"number"},"additional_info":{"type":"string","nullable":true},"failure_reason_code":{"type":"string","nullable":true}},"required":["result_type","sending_mta_ip","receiving_ip","receiving_mx_hostname","failed_session_count","additional_info","failure_reason_code"]}}},"required":["id","hostname_id","report_id","organization_name","contact_info","date_range_start","date_range_end","day","policy_type","policy_domain","mx_host","total_successful","total_failure","created_at","failures"]}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}}},"/domains/{domainId}/tls-reports/chart":{"get":{"tags":["TlsReport"],"summary":"Get daily TLS session counts","operationId":"getTlsReportChart","description":"Successful and failed TLS session counts per day over the requested window, as parallel arrays aligned to `labels`.","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","format":"date"},"required":true,"name":"date_from","in":"query"},{"schema":{"type":"string","format":"date"},"required":true,"name":"date_to","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"labels":{"type":"array","items":{"type":"string"}},"successful":{"type":"array","items":{"type":"number"}},"failure":{"type":"array","items":{"type":"number"}},"totals":{"type":"object","properties":{"successful":{"type":"number"},"failure":{"type":"number"}},"required":["successful","failure"]}},"required":["labels","successful","failure","totals"]}}}}}}},"/domains/{domainId}/tls-reports/summary":{"get":{"tags":["TlsReport"],"summary":"Get the TLS-RPT summary","operationId":"getTlsReportSummary","description":"Aggregate TLS session totals for the requested window, with failures broken down by RFC 8460 result type.","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","format":"date"},"required":true,"name":"date_from","in":"query"},{"schema":{"type":"string","format":"date"},"required":true,"name":"date_to","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"total_successful":{"type":"number"},"total_failure":{"type":"number"},"total_reports":{"type":"number"},"failure_breakdown":{"type":"array","items":{"type":"object","properties":{"result_type":{"type":"string"},"count":{"type":"number"}},"required":["result_type","count"]}}},"required":["total_successful","total_failure","total_reports","failure_breakdown"]}}}}}}},"/domains/{domainId}/tls-reports/{id}":{"get":{"tags":["TlsReport"],"summary":"Get a TLS-RPT report","operationId":"getTlsReport","description":"Retrieve a single TLS-RPT report, including the per-failure detail sending servers reported.","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"hostname_id":{"type":"string"},"report_id":{"type":"string"},"organization_name":{"type":"string"},"contact_info":{"type":"string","nullable":true},"date_range_start":{"type":"string"},"date_range_end":{"type":"string"},"day":{"type":"string"},"policy_type":{"type":"string"},"policy_domain":{"type":"string"},"mx_host":{"type":"string","nullable":true},"total_successful":{"type":"number"},"total_failure":{"type":"number"},"created_at":{"type":"string"},"failures":{"type":"array","items":{"type":"object","properties":{"result_type":{"type":"string"},"sending_mta_ip":{"type":"string","nullable":true},"receiving_ip":{"type":"string","nullable":true},"receiving_mx_hostname":{"type":"string","nullable":true},"failed_session_count":{"type":"number"},"additional_info":{"type":"string","nullable":true},"failure_reason_code":{"type":"string","nullable":true}},"required":["result_type","sending_mta_ip","receiving_ip","receiving_mx_hostname","failed_session_count","additional_info","failure_reason_code"]}}},"required":["id","hostname_id","report_id","organization_name","contact_info","date_range_start","date_range_end","day","policy_type","policy_domain","mx_host","total_successful","total_failure","created_at","failures"]}}}}}}},"/activity-log":{"get":{"tags":["Activity Log"],"summary":"List activity log entries","operationId":"listActivityLogEntries","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"actor_id","in":"query"},{"schema":{"type":"string"},"required":false,"name":"actor_email","in":"query"},{"schema":{"type":"string"},"required":false,"name":"resource_type","in":"query"},{"schema":{"type":"string"},"required":false,"name":"resource_id","in":"query"},{"schema":{"type":"string"},"required":false,"name":"action","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"date_from","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"date_to","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":200,"default":50},"required":false,"name":"per_page","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityLogList"}}}}}}},"/activity-log/actions":{"get":{"tags":["Activity Log"],"summary":"List action types","operationId":"listActivityLogActions","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityLogActions"}}}}}}},"/activity-log/{id}":{"get":{"tags":["Activity Log"],"summary":"Get an activity log entry","operationId":"getActivityLogEntry","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityLogEntry"}}}}}}},"/signup":{"post":{"tags":["Signup"],"summary":"Start a headless signup","operationId":"startSignup","description":"Start a headless signup: sends a 6-digit verification code to the given work email. No account is created until the code is verified.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Email address that will own the new account. A verification code is sent to this inbox; the address becomes the organization admin."},"organization_name":{"type":"string","minLength":3,"maxLength":128},"pricing_type":{"type":"string","enum":["free","it_teams","msp"],"description":"Self-serve plan to provision. Only honored when PLG is enabled; otherwise the default plan is used."},"attribution_id":{"type":"string","format":"uuid","description":"Opaque first-party journey id from palisade.email. Used only for acquisition attribution."}},"required":["email","organization_name"],"additionalProperties":false}}}},"responses":{"202":{"description":"Verification code sent","content":{"application/json":{"schema":{"type":"object","properties":{"signup_id":{"type":"string","format":"uuid"},"expires_in_seconds":{"type":"integer"}},"required":["signup_id","expires_in_seconds"]}}}}}}},"/signup/verify":{"post":{"tags":["Signup"],"summary":"Verify a signup code","operationId":"verifySignup","description":"Verify the emailed code. On success, creates the Auth0 user (email verified), the organization, and returns an org-scoped API key exactly once.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"signup_id":{"type":"string","format":"uuid"},"code":{"type":"string","pattern":"^\\d{6}$"}},"required":["signup_id","code"],"additionalProperties":false}}}},"responses":{"200":{"description":"Account created","content":{"application/json":{"schema":{"type":"object","properties":{"api_key":{"type":"string","description":"Org-scoped API key. Shown exactly once — store it securely."},"organization_id":{"type":"string","format":"uuid"},"organization_reference_id":{"type":"string"},"email":{"type":"string","format":"email"},"next_steps":{"type":"string"}},"required":["api_key","organization_id","organization_reference_id","email","next_steps"]}}}}}}},"/sources":{"get":{"tags":["Source"],"summary":"List sending sources","operationId":"listSources","description":"List the sending sources Palisade knows about, with the SPF and DKIM support details used to build setup instructions. Authentication is optional. `instructions_curation` holds unreviewed AI proposals and is null unless the caller holds sources:update.","security":[{},{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["ACTIVE","PENDING_REVIEW","REJECTED"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","enum":["DMARC_IO","AI_DISCOVERED","MANUAL"]},"required":false,"name":"origin","in":"query"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"external_id":{"type":"string"},"name":{"type":"string"},"homepage":{"type":"string","nullable":true,"format":"uri"},"base_domains":{"type":"array","items":{"type":"string"}},"category":{"type":"string","enum":["SENDING_TOOL","MAILBOX_PROVIDER","VPS_PROVIDER","SECURITY_PROVIDER","EMAIL_RELAY"]},"dmarc_support":{"type":"boolean"},"spf_support":{"type":"string","enum":["NONE","RARE","FULL"]},"spf_support_options":{"type":"object","properties":{"direct":{"type":"object","nullable":true,"properties":{"values":{"type":"array","items":{"type":"string"}},"requires_advanced_config":{"type":"boolean","nullable":true}},"required":["values","requires_advanced_config"]},"subdomain":{"type":"object","nullable":true,"properties":{"is_managed":{"type":"boolean"},"requires_user_selection":{"type":"boolean"},"path":{"type":"string","nullable":true},"value":{"type":"string","nullable":true},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"value":{"type":"string"}},"required":["type"]}}},"required":["is_managed","requires_user_selection","path","value","dns_records"]}},"required":["direct","subdomain"]},"dkim_support":{"type":"string","enum":["NONE","RARE","FULL"]},"parent_source_id":{"type":"string","nullable":true,"format":"uuid"},"display_name":{"type":"string","nullable":true},"envelope_froms":{"type":"array","items":{"type":"string"}},"is_custom":{"type":"boolean"},"documentation":{"type":"object","properties":{"spf":{"type":"string","nullable":true},"dkim":{"type":"string","nullable":true},"combined":{"type":"string","nullable":true}}},"spf_reply_to_target":{"type":"string","nullable":true},"spf_supported_methods":{"type":"array","items":{"type":"string","enum":["DIRECT","SUBDOMAIN","MANAGED_SUBDOMAIN"]}},"forwarding_type":{"type":"string","enum":["NOT_FORWARDER","FORWARDER","FORWARDER_ONLY"]},"dkim_discovery_selectors":{"type":"array","items":{"type":"string"}},"spf_discovery_values":{"type":"array","items":{"type":"string"}},"spf_reply_to_discovery_values":{"type":"array","items":{"type":"string"}},"mx_domains":{"type":"array","items":{"type":"string"}},"dkim_instructions":{"type":"object","nullable":true,"properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"spf_instructions":{"type":"object","nullable":true,"properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"instructions_curation":{"type":"object","nullable":true,"properties":{"dkim":{"type":"object","nullable":true,"properties":{"status":{"type":"string","enum":["PENDING","APPROVED","REJECTED"]},"proposal":{"type":"object","properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"provenance":{"type":"object","properties":{"doc_url":{"type":"string"},"doc_title":{"type":"string","nullable":true},"doc_discovered":{"type":"boolean"},"model":{"type":"string"},"confidence":{"type":"number"},"critic_notes":{"type":"array","items":{"type":"string"}},"generated_at":{"type":"string"}},"required":["doc_url","doc_title","doc_discovered","model","confidence","critic_notes","generated_at"]},"reviewed_at":{"type":"string","nullable":true},"reviewed_by":{"type":"string","nullable":true}},"required":["status","proposal","provenance"]},"spf":{"type":"object","nullable":true,"properties":{"status":{"type":"string","enum":["PENDING","APPROVED","REJECTED"]},"proposal":{"type":"object","properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"provenance":{"type":"object","properties":{"doc_url":{"type":"string"},"doc_title":{"type":"string","nullable":true},"doc_discovered":{"type":"boolean"},"model":{"type":"string"},"confidence":{"type":"number"},"critic_notes":{"type":"array","items":{"type":"string"}},"generated_at":{"type":"string"}},"required":["doc_url","doc_title","doc_discovered","model","confidence","critic_notes","generated_at"]},"reviewed_at":{"type":"string","nullable":true},"reviewed_by":{"type":"string","nullable":true}},"required":["status","proposal","provenance"]}}},"status":{"type":"string","enum":["ACTIVE","PENDING_REVIEW","REJECTED"]},"origin":{"type":"string","enum":["DMARC_IO","AI_DISCOVERED","MANUAL"]},"ai_confidence":{"type":"number","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","external_id","name","homepage","base_domains","category","dmarc_support","spf_support","spf_support_options","dkim_support","envelope_froms","is_custom","documentation","spf_reply_to_target","spf_supported_methods","forwarding_type","dkim_discovery_selectors","spf_discovery_values","spf_reply_to_discovery_values","mx_domains","dkim_instructions","status","origin","ai_confidence","created_at","updated_at"]}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}}},"/sources/spf-index":{"get":{"tags":["Source"],"summary":"Get the SPF include index","operationId":"getSpfIncludeIndex","description":"The compact include-to-sender index used to name the mechanisms in an SPF record, and to offer senders to add. Only sources that carry at least one SPF value appear. `values` holds the mechanisms Palisade prescribes adding; `recognition_values` additionally holds observed values, which name a mechanism already present in a record but are not safe to publish on a user's behalf. Authentication is optional, as on the list endpoint.","security":[{},{"BearerAuth":[]},{"BasicAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"logo_url":{"type":"string","nullable":true},"homepage":{"type":"string","nullable":true,"format":"uri"},"values":{"type":"array","items":{"type":"string"},"description":"SPF mechanisms Palisade prescribes adding for this source. Safe to publish on a user's behalf."},"recognition_values":{"type":"array","items":{"type":"string"},"description":"Every mechanism that identifies this source, including values only observed in the wild. Use these to name a mechanism already present in a record; they are not safe to add to one."},"requires_advanced_config":{"type":"boolean","description":"When true, the mechanism depends on per-customer setup and must not be offered as a one-click add."}},"required":["id","name","logo_url","homepage","values","recognition_values","requires_advanced_config"]}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}}},"/sources/dkim-index":{"get":{"tags":["Source"],"summary":"Get the DKIM selector index","operationId":"getDkimSelectorIndex","description":"The compact index used to name the DKIM keys already published for a domain. A key is matched either by `selectors`, the selector conventions a source publishes under, or by `base_domains`, the zones its CNAME targets sit in — the latter being the only signal that identifies a per-customer selector such as HubSpot's `hs1-<portalId>`. Only sources carrying at least one of the two appear. Authentication is optional, as on the list endpoint.","security":[{},{"BearerAuth":[]},{"BasicAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"logo_url":{"type":"string","nullable":true},"homepage":{"type":"string","nullable":true,"description":"The source's home page. Free text on the record rather than a validated URL - a handful of sources carry a bare hostname such as `samanage.com` - so treat it as a hostname that may or may not carry a scheme."},"selectors":{"type":"array","items":{"type":"string"},"description":"DKIM selectors this source is known to publish under. Matched against the selector of a key already in DNS; they are per-source conventions, not values to publish on a customer's behalf."},"base_domains":{"type":"array","items":{"type":"string"},"description":"Domains this source signs from. A DKIM CNAME whose target sits under one of these belongs to this source, which is the only signal that identifies a per-customer selector such as HubSpot's `hs1-<portal>`."}},"required":["id","name","logo_url","homepage","selectors","base_domains"]}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}}},"/sources/{id}":{"get":{"tags":["Source"],"summary":"Get a sending source","operationId":"getSource","description":"Fetch a single sending source by id. Authentication is optional, and `instructions_curation` is redacted as on the list endpoint.","security":[{},{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"external_id":{"type":"string"},"name":{"type":"string"},"homepage":{"type":"string","nullable":true,"format":"uri"},"base_domains":{"type":"array","items":{"type":"string"}},"category":{"type":"string","enum":["SENDING_TOOL","MAILBOX_PROVIDER","VPS_PROVIDER","SECURITY_PROVIDER","EMAIL_RELAY"]},"dmarc_support":{"type":"boolean"},"spf_support":{"type":"string","enum":["NONE","RARE","FULL"]},"spf_support_options":{"type":"object","properties":{"direct":{"type":"object","nullable":true,"properties":{"values":{"type":"array","items":{"type":"string"}},"requires_advanced_config":{"type":"boolean","nullable":true}},"required":["values","requires_advanced_config"]},"subdomain":{"type":"object","nullable":true,"properties":{"is_managed":{"type":"boolean"},"requires_user_selection":{"type":"boolean"},"path":{"type":"string","nullable":true},"value":{"type":"string","nullable":true},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"value":{"type":"string"}},"required":["type"]}}},"required":["is_managed","requires_user_selection","path","value","dns_records"]}},"required":["direct","subdomain"]},"dkim_support":{"type":"string","enum":["NONE","RARE","FULL"]},"parent_source_id":{"type":"string","nullable":true,"format":"uuid"},"display_name":{"type":"string","nullable":true},"envelope_froms":{"type":"array","items":{"type":"string"}},"is_custom":{"type":"boolean"},"documentation":{"type":"object","properties":{"spf":{"type":"string","nullable":true},"dkim":{"type":"string","nullable":true},"combined":{"type":"string","nullable":true}}},"spf_reply_to_target":{"type":"string","nullable":true},"spf_supported_methods":{"type":"array","items":{"type":"string","enum":["DIRECT","SUBDOMAIN","MANAGED_SUBDOMAIN"]}},"forwarding_type":{"type":"string","enum":["NOT_FORWARDER","FORWARDER","FORWARDER_ONLY"]},"dkim_discovery_selectors":{"type":"array","items":{"type":"string"}},"spf_discovery_values":{"type":"array","items":{"type":"string"}},"spf_reply_to_discovery_values":{"type":"array","items":{"type":"string"}},"mx_domains":{"type":"array","items":{"type":"string"}},"dkim_instructions":{"type":"object","nullable":true,"properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"spf_instructions":{"type":"object","nullable":true,"properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"instructions_curation":{"type":"object","nullable":true,"properties":{"dkim":{"type":"object","nullable":true,"properties":{"status":{"type":"string","enum":["PENDING","APPROVED","REJECTED"]},"proposal":{"type":"object","properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"provenance":{"type":"object","properties":{"doc_url":{"type":"string"},"doc_title":{"type":"string","nullable":true},"doc_discovered":{"type":"boolean"},"model":{"type":"string"},"confidence":{"type":"number"},"critic_notes":{"type":"array","items":{"type":"string"}},"generated_at":{"type":"string"}},"required":["doc_url","doc_title","doc_discovered","model","confidence","critic_notes","generated_at"]},"reviewed_at":{"type":"string","nullable":true},"reviewed_by":{"type":"string","nullable":true}},"required":["status","proposal","provenance"]},"spf":{"type":"object","nullable":true,"properties":{"status":{"type":"string","enum":["PENDING","APPROVED","REJECTED"]},"proposal":{"type":"object","properties":{"steps":{"type":"array","items":{"type":"string"}},"dns_records":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["TXT","CNAME","MX"]},"name":{"type":"string"},"value":{"type":"string"}},"required":["type"]}}},"required":["steps","dns_records"]},"provenance":{"type":"object","properties":{"doc_url":{"type":"string"},"doc_title":{"type":"string","nullable":true},"doc_discovered":{"type":"boolean"},"model":{"type":"string"},"confidence":{"type":"number"},"critic_notes":{"type":"array","items":{"type":"string"}},"generated_at":{"type":"string"}},"required":["doc_url","doc_title","doc_discovered","model","confidence","critic_notes","generated_at"]},"reviewed_at":{"type":"string","nullable":true},"reviewed_by":{"type":"string","nullable":true}},"required":["status","proposal","provenance"]}}},"status":{"type":"string","enum":["ACTIVE","PENDING_REVIEW","REJECTED"]},"origin":{"type":"string","enum":["DMARC_IO","AI_DISCOVERED","MANUAL"]},"ai_confidence":{"type":"number","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","external_id","name","homepage","base_domains","category","dmarc_support","spf_support","spf_support_options","dkim_support","envelope_froms","is_custom","documentation","spf_reply_to_target","spf_supported_methods","forwarding_type","dkim_discovery_selectors","spf_discovery_values","spf_reply_to_discovery_values","mx_domains","dkim_instructions","status","origin","ai_confidence","created_at","updated_at"]}}}}}}},"/domains/{domainId}/spf":{"get":{"tags":["Spf"],"summary":"Get SPF diagnostics","operationId":"getSpfDiagnostics","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"domainId","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpfDiagnostics"}}}}}}},"/tools/audit":{"get":{"tags":["Tool"],"summary":"Audit a domain","operationId":"auditDomain","description":"Run the full email-authentication audit against a domain from live DNS: MX, SPF, DKIM, DMARC, BIMI, MTA-STS and TLS-RPT, each scored and annotated, plus an overall score out of 100. The domain does not have to be one you have added to Palisade. Results are cached for ten minutes per domain. Authentication is optional. Note that this response predates the API-wide snake_case convention and keeps its original camelCase field names (`mtaSts`, `lookupCount`, `dmarcValue`), so it does not read like the rest of the API.","security":[{},{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":3},"required":true,"name":"domain","in":"query"},{"schema":{"type":"string","format":"email"},"required":false,"name":"email","in":"query"},{"schema":{"type":"string","enum":["true","false"],"default":"false"},"required":false,"name":"skip_dkim","in":"query"},{"schema":{"type":"string","enum":["true","false"],"default":"false"},"required":false,"name":"skip_webhooks","in":"query"},{"schema":{"type":"string","enum":["true","false"],"default":"false"},"required":false,"name":"store_scan","in":"query"},{"schema":{"type":"string"},"required":false,"name":"utm_source","in":"query"},{"schema":{"type":"string","format":"uuid","description":"The website visitor journey this audit belongs to, the same id sent to `POST /attribution/touches`. Read only when the request captures a lead, and ignored rather than rejected when it is missing or malformed."},"required":false,"name":"Palisade-Journey-Id","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditResult"}}}}}}},"/tools/activation-scan":{"get":{"tags":["Tool"],"summary":"Read a stored activation scan","operationId":"getActivationScan","description":"Return the most recent audit stored for a domain by `GET /tools/audit?store_scan=true`, so a scan run on the marketing site survives the sign-up round trip without being run again. Reports the same public DNS findings the audit does, and expires 24 hours after the scan. Authentication is not required.","parameters":[{"schema":{"type":"string","maxLength":253},"required":true,"name":"domain","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"schemaVersion":{"type":"number","enum":[1]},"domain":{"type":"string","maxLength":253},"score":{"type":"number","minimum":0,"maximum":100},"issueCount":{"type":"integer","minimum":0,"maximum":7},"findings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^email-security-score:(mx|spf|dkim|dmarc|bimi|mta-sts|tls-rpt)$"},"key":{"type":"string","enum":["mx","spf","dkim","dmarc","bimi","mtaSts","tlsRpt"]},"title":{"type":"string","minLength":1,"maxLength":32},"severity":{"type":"string","enum":["fail","warn","info"]},"detail":{"type":"string","minLength":1,"maxLength":1000},"optional":{"type":"boolean"},"diagnostics":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":2000},"detail":{"type":"string","minLength":1,"maxLength":1000},"selector":{"type":"string","maxLength":253}},"required":["code","detail"]}}},"required":["id","key","title","severity","detail","optional"],"additionalProperties":false},"maxItems":7},"baseline":{"type":"object","properties":{"dmarcValue":{"type":"string","nullable":true,"maxLength":8192},"initialTasks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["SET_UP_MX_RECORD","SET_UP_SPF_RECORD","CLEANUP_SPF_RECORD","REDUCE_SPF_LOOKUPS"]},"priority":{"type":"integer","minimum":1,"maximum":3},"description":{"type":"string","minLength":1,"maxLength":500}},"required":["type","priority","description"],"additionalProperties":false},"maxItems":4}},"required":["dmarcValue","initialTasks"],"additionalProperties":false},"provider":{"type":"object","nullable":true,"properties":{"name":{"type":"string","minLength":1,"maxLength":128},"domain":{"type":"string","maxLength":253}},"required":["name","domain"],"additionalProperties":false},"sourceTool":{"type":"string","enum":["email-security-score"]},"scannedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"}},"required":["schemaVersion","domain","score","issueCount","findings","baseline","provider","sourceTool","scannedAt","expiresAt"],"additionalProperties":false}}}}}}},"/tools/mailbox-provider":{"get":{"tags":["Tool"],"summary":"Identify the mailbox provider","operationId":"identifyMailboxProvider","description":"Identify which mailbox provider hosts a domain's inbound mail, from its live MX records. Returns a null provider when the MX records match no known provider, when the domain has no MX at all, or when a security gateway sits in front and hides the provider behind it. Authentication is optional.","security":[{},{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":3},"required":true,"name":"domain","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MailboxProviderResult"}}}}}}},"/tools/validate-include":{"get":{"tags":["Tool"],"summary":"Validate an SPF include","operationId":"validateSpfInclude","description":"Check that a domain is usable as an SPF `include:` and count the DNS lookups it costs, so you can tell before adding it whether it will push a record past the ten-lookup limit. The count includes the include itself. Callable with an API key or a dashboard session. The browser tool reaches this same endpoint with a captcha token instead, which is why you may see it called without credentials; a captcha is not something an API client can produce, so it is not part of this contract.","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","description":"The domain to test as an SPF `include:` target, e.g. `_spf.google.com`."},"required":true,"description":"The domain to test as an SPF `include:` target, e.g. `_spf.google.com`.","name":"domain","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateIncludeResult"}}}}}}},"/tools/dns-lookup":{"post":{"tags":["Tool"],"summary":"Look up DNS records","operationId":"lookupDnsRecords","description":"Read a host's TXT, MX, NS, A and CNAME records from live DNS in one call. Record types that resolve to nothing are simply absent from the array rather than reported as an error. Authentication is optional.","security":[{},{"BearerAuth":[]},{"BasicAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"host":{"type":"string","description":"The host to resolve."}},"required":["host"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DnsLookupResult"}}}}}}},"/tools/header-analyze":{"post":{"tags":["Tool"],"summary":"Analyze email headers","operationId":"analyzeEmailHeaders","description":"Parse a raw email header block and report what the receiving path concluded: the SPF, DKIM and DMARC verdicts each hop recorded, whether those domains align with the From domain, the DKIM signatures and ARC chain present, and the delivery hops with the delay at each. With `include_dns_validation` left on, the From domain's SPF, DMARC and DKIM records are also read from live DNS so a verdict can be checked against what is published now. Callable with an API key or a dashboard session. The browser tool reaches this same endpoint with a captcha token instead, which is why you may see it called without credentials; a captcha is not something an API client can produce, so it is not part of this contract. The request body is snake_case, but the analysis in the response keeps camelCase field names (`authenticationResults`, `receivedHops`, `dnsValidation`) rather than the API-wide convention.","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"headers":{"type":"string","minLength":10,"maxLength":100000},"include_dns_validation":{"type":"boolean","default":true}},"required":["headers"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeaderAnalysisResult"}}}}}}},"/webhook-endpoints":{"get":{"tags":["Webhooks"],"summary":"List webhook endpoints","operationId":"listWebhookEndpoints","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":50},"required":false,"name":"per_page","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}},"post":{"tags":["Webhooks"],"summary":"Create a webhook endpoint","operationId":"createWebhookEndpoint","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Creates an endpoint. The response is the only time the signing secret is returned in full; store it immediately.","parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"description":{"type":"string","maxLength":500},"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 this endpoint receives. Omit it, or send an empty array, to receive every event including ones added later."}},"required":["url"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}}}}}},"/webhook-endpoints/{id}":{"get":{"tags":["Webhooks"],"summary":"Get a webhook endpoint","operationId":"getWebhookEndpoint","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}}}},"post":{"tags":["Webhooks"],"summary":"Update a webhook endpoint","operationId":"updateWebhookEndpoint","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"description":{"type":"string","nullable":true,"maxLength":500},"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"]}},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}}}},"delete":{"tags":["Webhooks"],"summary":"Delete a webhook endpoint","operationId":"deleteWebhookEndpoint","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"},"object":{"type":"string"},"id":{"type":"string","format":"uuid"}},"required":["deleted","object","id"]}}}}}}},"/webhook-endpoints/{id}/rotate-secret":{"post":{"tags":["Webhooks"],"summary":"Rotate the signing secret","operationId":"rotateWebhookEndpointSecret","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"Issues a new signing secret and returns it once. The previous secret stops being valid immediately, so deliveries signed with it will fail verification.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}}}}}},"/webhook-endpoints/{id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"List webhook deliveries","operationId":"listWebhookDeliveries","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"number","minimum":1,"default":1},"required":false,"name":"page","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":50},"required":false,"name":"per_page","in":"query"},{"schema":{"type":"string","enum":["PENDING","SUCCEEDED","FAILED"]},"required":false,"name":"status","in":"query"},{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"page_info":{"type":"object","properties":{"count":{"type":"number"},"page":{"type":"number"},"per_page":{"type":"number"}},"required":["count","page","per_page"]}},"required":["data","page_info"]}}}}}}},"/webhook-events":{"get":{"tags":["Webhooks"],"summary":"List webhook event types","operationId":"listWebhookEventTypes","security":[{"BearerAuth":[]},{"BasicAuth":[]}],"description":"The catalogue of event types an endpoint can subscribe to.","parameters":[{"schema":{"$ref":"#/components/schemas/ApiVersion"},"required":true,"name":"Palisade-Version","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventDescriptor"}}},"required":["data"]}}}}}}}}}