# Email Auth # DMARC Reports ## Get DMARC Report Status `client.emailAuth.dmarcReports.get(DMARCReportGetParamsparams, RequestOptionsoptions?): DMARCReportGetResponse` **get** `/zones/{zone_id}/email/auth/dmarc-reports` Retrieves the current DMARC report configuration and status for a zone. Returns the RUA prefix, enabled status, approved sources, and DNS records. ### Parameters - `params: DMARCReportGetParams` - `zone_id: string` Identifier. ### Returns - `DMARCReportGetResponse` Response for GET/PATCH /dmarc-reports - `approved_sources?: Array` List of approved sending sources (omitted when empty) - `created?: string` Deprecated, use created_at - `created_at?: string` Creation timestamp - `domain?: string` The source domain - `ips?: Array` Resolved IP addresses from SPF - `modified?: string` Deprecated, use modified_at - `modified_at?: string` Last modification timestamp - `name?: string` Source name (typically same as domain) - `slug?: string` URL-friendly identifier - `tag?: string` Source UUID - `created?: string` Deprecated, use created_at - `created_at?: string` Creation timestamp - `enabled?: boolean` Whether DMARC reports are enabled - `modified?: string` Deprecated, use modified_at - `modified_at?: string` Last modification timestamp - `records?: Records` Live DNS records for the zone, grouped by type - `bimi_records?: Array` BIMI TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_dkim_records?: Array` CNAME records for DKIM - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_dmarc_records?: Array` CNAME records at _dmarc (problematic) - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_spf_records?: Array` CNAME records for SPF - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `dkim_records?: Array` DKIM TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `dmarc_records?: Array` DMARC TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `spf_records?: Array` SPF TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `rua_prefix?: string` Prefix for DMARC RUA addresses (32-char hex string) - `skip_wizard?: boolean` Whether to skip the setup wizard - `status?: "missing-dmarc-report" | "multiple-dmarc-reports" | "missing-dmarc-rua" | "cname-on-dmarc-record"` DMARC configuration status - `"missing-dmarc-report"` - `"multiple-dmarc-reports"` - `"missing-dmarc-rua"` - `"cname-on-dmarc-record"` - `tag?: string` Use `zone_id` instead - `zone_id?: string` Zone identifier ### Example ```node import Cloudflare from 'cloudflare'; const client = new Cloudflare({ apiToken: process.env['CLOUDFLARE_API_TOKEN'], // This is the default and can be omitted }); const dmarcReport = await client.emailAuth.dmarcReports.get({ zone_id: '023e105f4ecef8ad9ca31a8372d0c353', }); console.log(dmarcReport.zone_id); ``` #### Response ```json { "errors": [ { "code": 1000, "message": "message", "documentation_url": "documentation_url", "source": { "pointer": "pointer" } } ], "messages": [ { "code": 1000, "message": "message", "documentation_url": "documentation_url", "source": { "pointer": "pointer" } } ], "success": true, "result": { "approved_sources": [ { "created": "2024-01-15T10:30:00.12345Z", "created_at": "2024-01-15T10:30:00.12345Z", "domain": "sendgrid.net", "ips": [ "192.168.1.1", "10.0.0.1" ], "modified": "2024-01-15T11:45:00.12345Z", "modified_at": "2024-01-15T11:45:00.12345Z", "name": "SendGrid", "slug": "sendgrid-net", "tag": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415" } ], "created": "2024-01-15T10:30:00.12345Z", "created_at": "2024-01-15T10:30:00.12345Z", "enabled": true, "modified": "2024-01-15T11:45:00.12345Z", "modified_at": "2024-01-15T11:45:00.12345Z", "records": { "bimi_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "cname_dkim_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "cname_dmarc_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "cname_spf_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "dkim_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "dmarc_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "spf_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ] }, "rua_prefix": "9233c80fc89f43e3a7b749605f651868", "skip_wizard": false, "status": "missing-dmarc-report", "tag": "023e105f4ecef8ad9ca31a8372d0c353", "zone_id": "023e105f4ecef8ad9ca31a8372d0c353" } } ``` ## Configure DMARC Reports `client.emailAuth.dmarcReports.edit(DMARCReportEditParamsparams, RequestOptionsoptions?): DMARCReportEditResponse` **patch** `/zones/{zone_id}/email/auth/dmarc-reports` Updates the DMARC report configuration for a zone. At least one of `enabled` or `skip_wizard` must be provided. When enabling, the handler will ensure the DMARC RUA record exists in DNS. ### Parameters - `params: DMARCReportEditParams` - `zone_id: string` Path param: Identifier. - `enabled?: boolean | null` Body param: Enable or disable DMARC reports for this zone - `skip_wizard?: boolean | null` Body param: Skip the DMARC setup wizard ### Returns - `DMARCReportEditResponse` Response for GET/PATCH /dmarc-reports - `approved_sources?: Array` List of approved sending sources (omitted when empty) - `created?: string` Deprecated, use created_at - `created_at?: string` Creation timestamp - `domain?: string` The source domain - `ips?: Array` Resolved IP addresses from SPF - `modified?: string` Deprecated, use modified_at - `modified_at?: string` Last modification timestamp - `name?: string` Source name (typically same as domain) - `slug?: string` URL-friendly identifier - `tag?: string` Source UUID - `created?: string` Deprecated, use created_at - `created_at?: string` Creation timestamp - `enabled?: boolean` Whether DMARC reports are enabled - `modified?: string` Deprecated, use modified_at - `modified_at?: string` Last modification timestamp - `records?: Records` Live DNS records for the zone, grouped by type - `bimi_records?: Array` BIMI TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_dkim_records?: Array` CNAME records for DKIM - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_dmarc_records?: Array` CNAME records at _dmarc (problematic) - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_spf_records?: Array` CNAME records for SPF - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `dkim_records?: Array` DKIM TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `dmarc_records?: Array` DMARC TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `spf_records?: Array` SPF TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `rua_prefix?: string` Prefix for DMARC RUA addresses (32-char hex string) - `skip_wizard?: boolean` Whether to skip the setup wizard - `status?: "missing-dmarc-report" | "multiple-dmarc-reports" | "missing-dmarc-rua" | "cname-on-dmarc-record"` DMARC configuration status - `"missing-dmarc-report"` - `"multiple-dmarc-reports"` - `"missing-dmarc-rua"` - `"cname-on-dmarc-record"` - `tag?: string` Use `zone_id` instead - `zone_id?: string` Zone identifier ### Example ```node import Cloudflare from 'cloudflare'; const client = new Cloudflare({ apiToken: process.env['CLOUDFLARE_API_TOKEN'], // This is the default and can be omitted }); const response = await client.emailAuth.dmarcReports.edit({ zone_id: '023e105f4ecef8ad9ca31a8372d0c353', }); console.log(response.zone_id); ``` #### Response ```json { "errors": [ { "code": 1000, "message": "message", "documentation_url": "documentation_url", "source": { "pointer": "pointer" } } ], "messages": [ { "code": 1000, "message": "message", "documentation_url": "documentation_url", "source": { "pointer": "pointer" } } ], "success": true, "result": { "approved_sources": [ { "created": "2024-01-15T10:30:00.12345Z", "created_at": "2024-01-15T10:30:00.12345Z", "domain": "sendgrid.net", "ips": [ "192.168.1.1", "10.0.0.1" ], "modified": "2024-01-15T11:45:00.12345Z", "modified_at": "2024-01-15T11:45:00.12345Z", "name": "SendGrid", "slug": "sendgrid-net", "tag": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415" } ], "created": "2024-01-15T10:30:00.12345Z", "created_at": "2024-01-15T10:30:00.12345Z", "enabled": true, "modified": "2024-01-15T11:45:00.12345Z", "modified_at": "2024-01-15T11:45:00.12345Z", "records": { "bimi_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "cname_dkim_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "cname_dmarc_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "cname_spf_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "dkim_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "dmarc_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ], "spf_records": [ { "id": "e5bb46707a802688812d5d1c9f7977d4", "content": "\"v=DMARC1; p=none; rua=mailto:rua@dmarc-reports.cloudflare.net\"", "name": "_dmarc.example.com", "ttl": 300, "type": "TXT" } ] }, "rua_prefix": "9233c80fc89f43e3a7b749605f651868", "skip_wizard": false, "status": "missing-dmarc-report", "tag": "023e105f4ecef8ad9ca31a8372d0c353", "zone_id": "023e105f4ecef8ad9ca31a8372d0c353" } } ``` ## Domain Types ### DMARC Report Get Response - `DMARCReportGetResponse` Response for GET/PATCH /dmarc-reports - `approved_sources?: Array` List of approved sending sources (omitted when empty) - `created?: string` Deprecated, use created_at - `created_at?: string` Creation timestamp - `domain?: string` The source domain - `ips?: Array` Resolved IP addresses from SPF - `modified?: string` Deprecated, use modified_at - `modified_at?: string` Last modification timestamp - `name?: string` Source name (typically same as domain) - `slug?: string` URL-friendly identifier - `tag?: string` Source UUID - `created?: string` Deprecated, use created_at - `created_at?: string` Creation timestamp - `enabled?: boolean` Whether DMARC reports are enabled - `modified?: string` Deprecated, use modified_at - `modified_at?: string` Last modification timestamp - `records?: Records` Live DNS records for the zone, grouped by type - `bimi_records?: Array` BIMI TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_dkim_records?: Array` CNAME records for DKIM - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_dmarc_records?: Array` CNAME records at _dmarc (problematic) - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_spf_records?: Array` CNAME records for SPF - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `dkim_records?: Array` DKIM TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `dmarc_records?: Array` DMARC TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `spf_records?: Array` SPF TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `rua_prefix?: string` Prefix for DMARC RUA addresses (32-char hex string) - `skip_wizard?: boolean` Whether to skip the setup wizard - `status?: "missing-dmarc-report" | "multiple-dmarc-reports" | "missing-dmarc-rua" | "cname-on-dmarc-record"` DMARC configuration status - `"missing-dmarc-report"` - `"multiple-dmarc-reports"` - `"missing-dmarc-rua"` - `"cname-on-dmarc-record"` - `tag?: string` Use `zone_id` instead - `zone_id?: string` Zone identifier ### DMARC Report Edit Response - `DMARCReportEditResponse` Response for GET/PATCH /dmarc-reports - `approved_sources?: Array` List of approved sending sources (omitted when empty) - `created?: string` Deprecated, use created_at - `created_at?: string` Creation timestamp - `domain?: string` The source domain - `ips?: Array` Resolved IP addresses from SPF - `modified?: string` Deprecated, use modified_at - `modified_at?: string` Last modification timestamp - `name?: string` Source name (typically same as domain) - `slug?: string` URL-friendly identifier - `tag?: string` Source UUID - `created?: string` Deprecated, use created_at - `created_at?: string` Creation timestamp - `enabled?: boolean` Whether DMARC reports are enabled - `modified?: string` Deprecated, use modified_at - `modified_at?: string` Last modification timestamp - `records?: Records` Live DNS records for the zone, grouped by type - `bimi_records?: Array` BIMI TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_dkim_records?: Array` CNAME records for DKIM - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_dmarc_records?: Array` CNAME records at _dmarc (problematic) - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `cname_spf_records?: Array` CNAME records for SPF - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `dkim_records?: Array` DKIM TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `dmarc_records?: Array` DMARC TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `spf_records?: Array` SPF TXT records - `id?: string` DNS record ID - `content?: string` Record content - `name?: string` DNS record name - `ttl?: number` Time to live in seconds - `type?: string` Record type - `rua_prefix?: string` Prefix for DMARC RUA addresses (32-char hex string) - `skip_wizard?: boolean` Whether to skip the setup wizard - `status?: "missing-dmarc-report" | "multiple-dmarc-reports" | "missing-dmarc-rua" | "cname-on-dmarc-record"` DMARC configuration status - `"missing-dmarc-report"` - `"multiple-dmarc-reports"` - `"missing-dmarc-rua"` - `"cname-on-dmarc-record"` - `tag?: string` Use `zone_id` instead - `zone_id?: string` Zone identifier # SPF # Inspect ## Inspect SPF Record `client.emailAuth.spf.inspect.get(InspectGetParamsparams, RequestOptionsoptions?): InspectGetResponse` **get** `/zones/{zone_id}/email/auth/spf/inspect` Inspects a specific SPF TXT record and returns a parsed tree structure in the spflimit-worker format. The record ID must be provided via the `id` query parameter. Returns a recursive tree showing: - Parsed components with their qualifiers and types - Nested includes recursively resolved within components - Per-component and total lookup counts - Detailed error information with context ### Parameters - `params: InspectGetParams` - `zone_id: string` Path param: Identifier. - `id: string` Query param: DNS record ID (rec_tag) to inspect ### Returns - `InspectGetResponse` Recursive SPF inspection tree - `components: Array` Parsed SPF components (mechanisms) - `domain: string` Domain being inspected - `record: string` Raw SPF record content - `total_lookups: number` Total number of DNS lookups performed across all includes - `errors?: Array` All errors encountered during inspection, collected from the entire tree. This includes errors from nested includes at any depth, providing a quick overview of all issues without needing to traverse the nested structure. Each error includes a `domain` field to identify where it occurred. Empty array if no errors (omitted from JSON when empty). - `code: string` Error code. Known values: - `lookup_failed` — DNS TXT lookup failed - `spf_not_found` — no SPF record found - `invalid_spf` — record does not start with `v=spf1` - `invalid_domain` — PSL validation failed - `loop_detected` — include/redirect cycle detected - `invalid_mechanism` — unrecognised or malformed mechanism - `resource_limit_exceeded` — internal resource protection limits exceeded (recursion depth or query budget) - `max_lookups` — RFC 7208 10-lookup limit exceeded - `domain: string` Domain where the error occurred - `message: string` Human-readable error message - `details?: string` Additional error-specific details (optional). - For `invalid_domain` errors: the invalid domain string - For `invalid_mechanism` errors: the invalid mechanism text (e.g., "invalidmech123") - For `loop_detected` errors: the domain that caused the loop - For other error types: not present ### Example ```node import Cloudflare from 'cloudflare'; const client = new Cloudflare({ apiToken: process.env['CLOUDFLARE_API_TOKEN'], // This is the default and can be omitted }); const inspect = await client.emailAuth.spf.inspect.get({ zone_id: '023e105f4ecef8ad9ca31a8372d0c353', id: 'id', }); console.log(inspect.components); ``` #### Response ```json { "errors": [ { "code": 1000, "message": "message", "documentation_url": "documentation_url", "source": { "pointer": "pointer" } } ], "messages": [ { "code": 1000, "message": "message", "documentation_url": "documentation_url", "source": { "pointer": "pointer" } } ], "success": true, "result": { "components": [ {} ], "domain": "example.com", "record": "v=spf1 ip4:203.0.113.1 include:spf.example.com -all", "total_lookups": 2, "errors": [ { "code": "max_lookups", "domain": "example.com", "message": "RFC 7208 10-lookup limit exceeded", "details": "invalid" } ] } } ``` ## Domain Types ### Inspect Get Response - `InspectGetResponse` Recursive SPF inspection tree - `components: Array` Parsed SPF components (mechanisms) - `domain: string` Domain being inspected - `record: string` Raw SPF record content - `total_lookups: number` Total number of DNS lookups performed across all includes - `errors?: Array` All errors encountered during inspection, collected from the entire tree. This includes errors from nested includes at any depth, providing a quick overview of all issues without needing to traverse the nested structure. Each error includes a `domain` field to identify where it occurred. Empty array if no errors (omitted from JSON when empty). - `code: string` Error code. Known values: - `lookup_failed` — DNS TXT lookup failed - `spf_not_found` — no SPF record found - `invalid_spf` — record does not start with `v=spf1` - `invalid_domain` — PSL validation failed - `loop_detected` — include/redirect cycle detected - `invalid_mechanism` — unrecognised or malformed mechanism - `resource_limit_exceeded` — internal resource protection limits exceeded (recursion depth or query budget) - `max_lookups` — RFC 7208 10-lookup limit exceeded - `domain: string` Domain where the error occurred - `message: string` Human-readable error message - `details?: string` Additional error-specific details (optional). - For `invalid_domain` errors: the invalid domain string - For `invalid_mechanism` errors: the invalid mechanism text (e.g., "invalidmech123") - For `loop_detected` errors: the domain that caused the loop - For other error types: not present