groundmail-dev Dmarc API

DMARC monitoring per domain (aggregate reports, sending sources, enforcement readiness)

Operations 4

GET /domains/{id}/dmarc Get DMARC monitoring for a domain #
POST /domains/{id}/dmarc Enable DMARC monitoring #
PATCH /domains/{id}/dmarc Turn the weekly DMARC digest on or off #
DELETE /domains/{id}/dmarc Stop DMARC monitoring #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/groundmail-dev:groundmail-dev-dmarc-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

groundmail-dev-dmarc-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Groundmail Dmarc API
  version: 0.16.0
  description: 'Transactional email API for developers.

    Auth: `Authorization: Bearer gm_…` with an API key, or `Bearer gm_oat_…` with an OAuth

    access token from the device flow (`groundmail login`). Dashboard session cookies work

    for integrations.


    Azure managed identity (keyless): an Azure workload can send a Microsoft Entra access

    token (a JWT) as the Bearer credential instead of a key. Register the identity''s tenant

    ID, object ID and token audience in the dashboard (API keys → Azure managed identity);

    it then has the same access options as an API key (scopes, domain limit, tenant).

    App-only tokens only. Any authentication failure is the same 401 `unauthorized`.

    See https://docs.groundmail.dev/authentication/azure-managed-identity.


    Test mode: a test key (`gm_test_…`, `POST /api-keys` with `test: true`) behaves

    like a live key, but its sends are never delivered: they are stored with

    `test: true`, get simulated events and webhooks (`data.test: true`), and do not

    count toward plan limits, the sending ramp, stats or billing. Simulator

    addresses (`delivered@`, `bounce@`, `block@`, `complaint@`, `suppressed@`,

    `delayed@simulator.groundmail.dev`, plus addressing allowed) produce one outcome

    each with any key. See https://docs.groundmail.dev/testing.


    Tenants: a platform that sends email for its own customers creates one tenant per

    customer (`/tenants`), each with its own domains, suppression list, stats, sending

    ramp and pause switch. Name the tenant with `tenant` on sends, domains and

    suppressions, or send from a domain the tenant owns. API keys and webhook

    endpoints can be limited to tenants. Included on pay as you go and the Dedicated plan;

    elsewhere a request that names a tenant gets 403 `tenants_not_enabled`. See

    https://docs.groundmail.dev/tenants.


    Data retention: sent emails (content and events) and received emails are kept for

    your plan''s window, then deleted: 7 days on Free, 30 on Pro, 90 on Scale, up to 365

    on Dedicated. Past it, `GET /emails/{id}` and `GET /received-emails/{id}` return

    404 `not_found` and lists no longer include them. Aggregate stats

    (`GET /deliverability`) are kept. Store webhook events to keep history longer. See

    https://docs.groundmail.dev/emails#data-retention.


    Scopes: every credential carries scopes (`full`, `emails:send`, `emails:read`,

    `domains:read`, `domains:write`, `webhooks:write`, `templates:write`,

    `suppressions:write`, `account:read`). Each operation lists the scope it needs under

    `x-required-scope` (space-separated: all of them; `a | b`: either); a credential

    without it gets 403 `insufficient_scope` with `required_scope`. `full` grants everything; `domains:write` includes `domains:read`.

    Legacy `sending` API keys have `emails:send` + `emails:read`.

    Production base URL: https://api.groundmail.dev'
servers:
- url: https://api.groundmail.dev
  description: Production
tags:
- name: Dmarc
  description: DMARC monitoring per domain (aggregate reports, sending sources, enforcement readiness)
paths:
  /domains/{id}/dmarc:
    parameters:
    - name: id
      in: path
      required: true
      description: Domain id or domain name
      schema:
        type: string
    get:
      operationId: getDmarcMonitoring
      x-required-scope: domains:read
      summary: Get DMARC monitoring for a domain
      description: Sending sources grouped by provider (with unknown senders flagged as possible spoofing), DMARC, SPF and DKIM pass rates over the last 30 days, and the readiness verdict (whether it is safe to move the policy to quarantine). When monitoring is off, `enabled` is false and only the current DMARC record is returned.
      tags:
      - Dmarc
      security:
      - bearerAuth: []
      responses:
        '200':
          description: DMARC monitoring status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DmarcResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleErrorResponse'
        '404':
          description: Domain not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleErrorResponse'
    post:
      operationId: enableDmarcMonitoring
      x-required-scope: domains:write
      summary: Enable DMARC monitoring
      description: 'Turns monitoring on and returns the domain''s report address and the DMARC record to publish: the current record with the address added to `rua=`, keeping the policy and any other report addresses. With `apply`, the record is also written through a DNS provider connected in the dashboard. Calling it again returns the same address.'
      tags:
      - Dmarc
      security:
      - bearerAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                apply:
                  type: string
                  enum:
                  - cloudflare
                  - vercel
                  - godaddy
                  - netlify
                  - digitalocean
                  - dnsimple
                  description: Write the DMARC record through this connected DNS provider
                digest_enabled:
                  type: boolean
                  description: Weekly digest email on or off (default on)
      responses:
        '201':
          description: Monitoring is on
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/DmarcResponse'
                - type: object
                  properties:
                    applied:
                      type: object
                      nullable: true
                      description: The DNS provider and what changed, when `apply` was used
                    apply_error:
                      type: string
                      description: Why the DNS provider could not update the record (add it by hand)
        '400':
          description: Invalid apply provider
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleErrorResponse'
        '404':
          description: Domain not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleErrorResponse'
    patch:
      operationId: updateDmarcMonitoring
      x-required-scope: domains:write
      summary: Turn the weekly DMARC digest on or off
      tags:
      - Dmarc
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - digest_enabled
              properties:
                digest_enabled:
                  type: boolean
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DmarcResponse'
        '400':
          description: digest_enabled missing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleErrorResponse'
        '404':
          description: Domain not found, or monitoring is not enabled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleErrorResponse'
    delete:
      operationId: disableDmarcMonitoring
      x-required-scope: domains:write
      summary: Stop DMARC monitoring
      description: Reports sent to the address are ignored from now on. Remove it from the DMARC record's `rua=` tag too.
      tags:
      - Dmarc
      security:
      - bearerAuth: []
      responses:
        '200':
          description: Monitoring is off
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/DmarcResponse'
                - type: object
                  properties:
                    disabled:
                      type: boolean
        '404':
          description: Domain not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleErrorResponse'
components:
  schemas:
    DmarcResponse:
      type: object
      required:
      - dmarc
      properties:
        dmarc:
          $ref: '#/components/schemas/DmarcMonitoring'
    DmarcMonitoring:
      type: object
      required:
      - domain
      - enabled
      properties:
        domain:
          type: string
        enabled:
          type: boolean
        status:
          type: string
          enum:
          - waiting
          - receiving
          - disabled
          description: waiting until the first report arrives
        report_address:
          type: string
          example: k3m9q2xw7hb4npr6@dmarc.groundmail.dev
        rua:
          type: string
          example: mailto:k3m9q2xw7hb4npr6@dmarc.groundmail.dev
        dns:
          type: object
          properties:
            host:
              type: string
              example: _dmarc.example.com
            record:
              type: string
              nullable: true
            rua_present:
              type: boolean
            policy:
              type: string
              nullable: true
            suggested_record:
              type: string
              description: The record to publish
        digest_enabled:
          type: boolean
        created_at:
          type: string
          format: date-time
        last_report_at:
          type: string
          format: date-time
          nullable: true
        window_days:
          type: integer
        summary:
          type: object
          properties:
            messages:
              type: integer
            dmarc_pass:
              type: integer
            dmarc_fail:
              type: integer
            pass_rate:
              type: number
            reports:
              type: integer
            reporters:
              type: array
              items:
                type: string
        sources:
          type: array
          items:
            $ref: '#/components/schemas/DmarcSource'
        daily:
          type: array
          items:
            type: object
            properties:
              date:
                type: string
                format: date
              messages:
                type: integer
              dmarc_pass:
                type: integer
              pass_rate:
                type: number
        readiness:
          type: object
          properties:
            verdict:
              type: string
              enum:
              - no_data
              - collecting
              - fix_sources
              - ready_quarantine
              - ready_reject
              - enforced
            label:
              type: string
              example: Safe to move to quarantine
            detail:
              type: string
            days_of_data:
              type: integer
            blocking_sources:
              type: array
              items:
                type: string
    SimpleErrorResponse:
      type: object
      required:
      - error
      description: 'Older endpoints return `error` as a string. The machine-readable fields (`code`, `message`, `hint`, `docs_url`) sit beside it with the same meaning as in `ErrorResponse`.

        '
      properties:
        error:
          type: string
          description: Human-readable message (same as `message`).
        code:
          $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
        hint:
          type: string
          description: How to fix the error.
        docs_url:
          type: string
          format: uri
          description: Link to this code on the errors page, `https://docs.groundmail.dev/errors#<code>`.
    DmarcSource:
      type: object
      properties:
        key:
          type: string
          description: Provider id (google, microsoft, groundmail, sendgrid…) or unknown:<reverse DNS domain>
        provider:
          type: string
        name:
          type: string
        status:
          type: string
          enum:
          - passing
          - failing
          - unknown
          description: unknown is an unrecognised server failing DMARC (possible spoofing)
        messages:
          type: integer
        dmarc_pass:
          type: integer
        pass_rate:
          type: number
          description: 0 to 1
        spf_aligned_rate:
          type: number
        dkim_aligned_rate:
          type: number
        ips:
          type: array
          items:
            type: string
          description: Up to five source IPs, busiest first
        ip_count:
          type: integer
        first_seen:
          type: string
          format: date-time
        last_seen:
          type: string
          format: date-time
    ErrorCode:
      type: string
      description: 'Stable, machine-readable error code. Each code is documented at

        `https://docs.groundmail.dev/errors#<code>`. New codes may be added;

        existing codes keep their meaning.


        - `validation_error` (400, 422): the request failed validation, or every recipient is suppressed or opted out (422). Fix the field named in `message`/`details`.

        - `unauthorized` (401): missing, malformed or revoked API key. Send `Authorization: Bearer gm_…`.

        - `forbidden` (403): the key is valid but the action is not allowed (unverified `from` domain, sandbox send to someone other than the account email, no payment method on file, sending restricted).

        - `insufficient_scope` (403): the API key''s scope does not cover this endpoint.

        - `domain_not_allowed` (403): the API key is limited to other sending domains than the one in `from`, or `from` is on another tenant''s domain.

        - `tenants_not_enabled` (403): the request names a tenant, but tenants are not enabled for this account. They are included on pay as you go and the Dedicated plan.

        - `newsletter_not_enabled` (403): the send uses `category: "newsletter"`, but newsletter sending isn''t enabled on this account. Newsletter email is accepted only for approved accounts.

        - `not_found` (404): no resource with that id for this team.

        - `conflict` (409): the resource already exists or its state blocks the change.

        - `plan_limit` (429): the plan''s monthly or daily sending limit was reached. See `upgrade_url`.

        - `daily_ramp_limit` (429): the send would go over a new account''s daily ramp-up limit for its stream, or its tenant''s own ramp or daily limit (`details.tenant`). Upgrading does not lift it; wait for `details.resets_at`.

        - `domain_limit` (403): the plan''s domain limit was reached. See `upgrade_url`.

        - `idempotency_conflict` (409): the `Idempotency-Key` was reused with a different body, or the first request is still running.

        - `rate_limited` (429): too many requests. Wait `Retry-After` seconds.

        - `timeout` (408): a long-poll (GET /test-inbox/wait) ended before a matching email arrived. Safe to call again.

        - `upstream_error` (502, 503, 504, or a passed-through 4xx): the mail server did not accept the request. 502/503 are safe to retry with the same `Idempotency-Key`; 504 means the outcome is unknown.

        - `not_configured` (503): the feature is temporarily unavailable.

        - `internal_error` (500): unexpected server error. Retry with backoff.

        '
      enum:
      - validation_error
      - unauthorized
      - forbidden
      - insufficient_scope
      - not_found
      - conflict
      - plan_limit
      - daily_ramp_limit
      - domain_limit
      - idempotency_conflict
      - rate_limited
      - timeout
      - upstream_error
      - not_configured
      - internal_error
      - domain_not_allowed
      - tenants_not_enabled
      - newsletter_not_enabled
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'Groundmail API key (`gm_…`), OAuth access token (`gm_oat_…`), or a Microsoft Entra access token from an Azure managed identity registered in the dashboard (keyless; see https://docs.groundmail.dev/authentication/azure-managed-identity). All carry scopes; see the API description.

        '