Sendry Docs
API Reference

Deliverability

Query sending reputation, blocklist status, bulk sender readiness, DMARC reports, and Gmail Postmaster metrics.

The deliverability endpoints surface the signals that decide whether your mail reaches the inbox: reputation score and history, blocklist checks and alerts, Gmail/Microsoft bulk sender readiness, ingested DMARC aggregate reports, and Gmail Postmaster Tools metrics.

This page covers the Deliverability tag, grouped into the core deliverability endpoints, the DMARC analyzer, and Gmail Postmaster Tools.


Deliverability

Get deliverability report

GET /v1/deliverability/report

Requires read_only scope.

Query parameters

ParameterTypeDefaultDescription
domain_idstringRestrict the report to a single domain.
daysnumber30Trailing window in days (7-90).

Response

{
  "period": { "from": "2025-02-10", "to": "2025-03-12", "days": 30 },
  "metrics": {
    "total_sent": 125400,
    "total_delivered": 123980,
    "total_bounced": 1420,
    "total_complained": 38,
    "delivery_rate": 0.9887,
    "bounce_rate": 0.0113,
    "complaint_rate": 0.0003,
    "open_rate": 0.421,
    "click_rate": 0.087
  },
  "reputation": { "score": 94, "rating": "excellent", "trend": "stable" },
  "blocklist_status": { "total_lists_checked": 32, "active_listings": 0, "clean": true },
  "inbox_placement_estimate": { "inbox_pct": 96, "spam_pct": 2, "missing_pct": 2 },
  "recommendations": [],
  "authentication": { "spf": true, "dkim": true, "dmarc": true, "bimi": "not_configured" }
}

Get reputation overview

GET /v1/deliverability/reputation

Requires read_only scope.

Query parameters

ParameterTypeDefaultDescription
domain_idstringRestrict to a single domain.
daysnumber30Trailing window in days (1-90).

Response

{
  "current": {
    "score": 94,
    "rating": "excellent",
    "factors": {
      "bounceRate": 0.0113,
      "complaintRate": 0.0003,
      "deliveryRate": 0.9887,
      "engagementRate": 0.42
    },
    "recommendations": []
  },
  "history": [
    {
      "date": "2025-03-12",
      "total_sent": 4200,
      "total_delivered": 4160,
      "total_bounced": 40,
      "total_complained": 1,
      "total_opened": 1750,
      "total_clicked": 360,
      "delivery_rate": 0.9905,
      "bounce_rate": 0.0095,
      "complaint_rate": 0.0002,
      "open_rate": 0.4207,
      "click_rate": 0.0865,
      "reputation_score": 94
    }
  ],
  "domains": [
    {
      "domain_id": "dom_abc123",
      "domain_name": "mail.acme.com",
      "score": 94,
      "rating": "excellent"
    }
  ]
}

Get domain reputation history

GET /v1/deliverability/reputation/:domainId/history

Requires read_only scope.

Query parameters

ParameterTypeDefaultDescription
fromstringStart date (YYYY-MM-DD) for the snapshot range.
tostringEnd date (YYYY-MM-DD) for the snapshot range.

Response

{
  "domain_id": "dom_abc123",
  "domain_name": "mail.acme.com",
  "snapshots": [
    {
      "date": "2025-03-12",
      "total_sent": 4200,
      "total_delivered": 4160,
      "total_bounced": 40,
      "total_complained": 1,
      "total_opened": 1750,
      "total_clicked": 360,
      "delivery_rate": 0.9905,
      "bounce_rate": 0.0095,
      "complaint_rate": 0.0002,
      "open_rate": 0.4207,
      "click_rate": 0.0865,
      "reputation_score": 94
    }
  ]
}

Get blocklist status

GET /v1/deliverability/blocklist

Requires read_only scope. Returns the latest check per target and provider, plus any active alerts.

Query parameters

ParameterTypeDefaultDescription
targetstringFilter to a single domain or IP.
target_typestringOne of domain or ip.

Response

{
  "checks": [
    {
      "id": "blc_abc123",
      "target": "mail.acme.com",
      "target_type": "domain",
      "provider": "spamhaus_dbl",
      "listed": false,
      "listing_reason": null,
      "response_time_ms": 142,
      "checked_at": "2025-03-12T09:00:00Z"
    }
  ],
  "alerts": [
    {
      "id": "bla_abc123",
      "target": "198.51.100.24",
      "target_type": "ip",
      "provider": "spamhaus_zen",
      "status": "active",
      "listed_at": "2025-03-10T14:20:00Z",
      "resolved_at": null
    }
  ],
  "summary": { "total_targets": 2, "listed_count": 1, "clean_count": 1 }
}

Run blocklist check

POST /v1/deliverability/blocklist/check

Requires full_access scope. Runs an on-demand check against all supported blocklists and persists the results.

Request body

FieldTypeRequiredDescription
targetstringYesDomain name or IP address.
target_typestringYesOne of domain or ip.

Example request

{
  "target": "mail.acme.com",
  "target_type": "domain"
}

Response

Returns a bare array of check results.

[
  {
    "id": "blc_abc123",
    "target": "mail.acme.com",
    "target_type": "domain",
    "provider": "spamhaus_dbl",
    "listed": false,
    "listing_reason": null,
    "response_time_ms": 138,
    "checked_at": "2025-03-12T09:00:00Z"
  }
]

Dismiss a blocklist alert

PATCH /v1/deliverability/blocklist/alerts/:id

Requires full_access scope. Marks an active alert as dismissed. The endpoint returns 404 if the alert does not exist or is already resolved.

Request body

FieldTypeRequiredDescription
statusstringYesMust be dismissed.

Example request

{
  "status": "dismissed"
}

Response

{
  "id": "bla_abc123",
  "target": "198.51.100.24",
  "target_type": "ip",
  "provider": "spamhaus_zen",
  "status": "dismissed",
  "listed_at": "2025-03-10T14:20:00Z",
  "dismissed_at": "2025-03-12T09:05:00Z"
}

Get bulk sender readiness

GET /v1/deliverability/bulk-sender-readiness

Requires read_only scope. Reports trailing 24-hour send volume per verified domain, split by mailbox provider, against the 5,000-a-day bulk sender threshold Gmail and Microsoft enforce.

Query parameters

ParameterTypeDefaultDescription
thresholdnumber5000Override the threshold used for the assessment (1-10,000,000).

Response

{
  "threshold": 5000,
  "warn_at": 4000,
  "window_hours": 24,
  "measured_from": "2025-03-11T09:00:00Z",
  "summary": { "ok": 2, "approaching": 1, "at_risk": 0 },
  "domains": [
    {
      "domain_id": "dom_abc123",
      "domain_name": "mail.acme.com",
      "status": "approaching",
      "ready": false,
      "spf_verified": true,
      "dkim_verified": true,
      "dmarc_verified": false,
      "has_dmarc_record": false,
      "total_volume": 4310,
      "providers": [
        {
          "provider": "google",
          "provider_label": "Gmail",
          "volume": 4102,
          "status": "approaching",
          "threshold": 5000,
          "warn_at": 4000,
          "reasons": [
            "mail.acme.com sent 4,102 messages to Gmail in the last 24 hours, 82% of the 5,000/day bulk sender threshold.",
            "No DMARC record for mail.acme.com. Bulk senders must publish DMARC with at least p=none; without it Gmail and Microsoft reject the mail outright."
          ]
        }
      ]
    }
  ]
}

DMARC

Upload a DMARC aggregate report

POST /v1/dmarc/reports

Requires full_access scope. Accepts a multipart .xml or gzipped .xml.gz attachment (10 MB max). Parsing and persistence run synchronously; a duplicate upload with the same (reporter, report_id) is a no-op.

Request body

Multipart form data with a single file field.

curl -X POST https://api.sendry.online/v1/dmarc/reports \
  -H "Authorization: Bearer $SENDRY_API_KEY" \
  -F "file=@google.com!example.com!1715731200!1715817600.xml.gz"

Response

{
  "report_id": "dmarc_report_5n3Q…",
  "reporter": "google.com",
  "domain": "example.com",
  "date_begin": "2025-05-14T00:00:00.000Z",
  "date_end": "2025-05-15T00:00:00.000Z",
  "record_count": 12,
  "duplicate": false
}

List DMARC reports

GET /v1/dmarc/reports

Requires read_only scope.

Query parameters

ParameterTypeDefaultDescription
domainstringFilter by the reported domain.
limitnumber25Results per page (1-100).
cursorstringPagination cursor from the previous response's next_cursor.

Response

{
  "data": [
    {
      "id": "dmarc_report_5n3Q…",
      "domain": "example.com",
      "reporter": "google.com",
      "report_id": "google.com!example.com!1715731200!1715817600",
      "date_begin": "2025-05-14T00:00:00.000Z",
      "date_end": "2025-05-15T00:00:00.000Z",
      "record_count": 12,
      "created_at": "2025-05-15T02:14:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Get DMARC summary

GET /v1/dmarc/summary

Requires read_only scope. Aggregates per source IP across all ingested reports for the org, returning the top 200 source IPs by message volume.

Query parameters

ParameterTypeDefaultDescription
domainstringFilter by the reported domain.
daysnumber7Trailing window in days (1-90).

Response

{
  "domain": "example.com",
  "days": 7,
  "range_begin": "2025-05-08T00:00:00.000Z",
  "range_end": "2025-05-15T00:00:00.000Z",
  "totals": { "messages": 1840, "pass": 1795, "fail": 45, "pass_rate": 0.9755 },
  "buckets": [
    {
      "source_ip": "203.0.113.14",
      "domain": "example.com",
      "total": 640,
      "pass": 638,
      "fail": 2,
      "dkim_pass": 638,
      "spf_pass": 631,
      "dispositions": { "none": 638, "quarantine": 2 },
      "last_seen": "2025-05-15T00:00:00.000Z"
    }
  ]
}

Postmaster

Get Postmaster credentials

GET /v1/postmaster/credentials

Requires read_only scope. Returns the stored credential record for the org. Returns 404 if no credentials are configured.

Response

{
  "id": "pmc_abc123",
  "enabled": true,
  "last_synced_at": "2025-03-12T02:00:00Z",
  "created_at": "2025-03-01T10:00:00Z",
  "updated_at": "2025-03-12T02:00:00Z"
}

Save Postmaster credentials

POST /v1/postmaster/credentials

Requires full_access scope. Saves or replaces manually-pasted OAuth credentials for the org. Use this when the in-product OAuth flow is unavailable.

Request body

FieldTypeRequiredDescription
google_oauth_refresh_tokenstringYesRefresh token from your own Google Cloud project.
google_oauth_client_idstringYesOAuth 2.0 client ID.
google_oauth_client_secretstringYesOAuth 2.0 client secret.
enabledbooleanNoDefaults to true on create, unchanged on update.

Response

{
  "id": "pmc_abc123",
  "enabled": true,
  "last_synced_at": null,
  "created_at": "2025-03-12T09:00:00Z",
  "updated_at": "2025-03-12T09:00:00Z"
}

Delete Postmaster credentials

DELETE /v1/postmaster/credentials

Requires full_access scope. Removes the credential record for the org. Returns 404 if none exists.

Response

{
  "deleted": true
}

Start Postmaster OAuth flow

GET /v1/postmaster/oauth/start

Requires full_access scope. Returns a Google consent URL bound to a short-lived state token (10 minutes) and the state value. Redirect the browser to url to begin consent.

Query parameters

ParameterTypeDefaultDescription
return_tostringRelative path to send the user back to after the callback.

Response

{
  "url": "https://accounts.google.com/o/oauth2/v2/auth?client_id=...&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fpostmaster.readonly&access_type=offline&prompt=consent&state=...",
  "state": "V1StGXR8_Z5jdHi6B-myT"
}

Complete Postmaster OAuth flow

GET /v1/postmaster/oauth/callback

This is the redirect target Google sends the browser to after consent; it is not called directly. It reads code and state from the query string, exchanges the code, stores the refresh token against the org bound to the state token, and responds with a 302 redirect back to the dashboard (?connected=1). On user denial it redirects back with an ?error= parameter.

Query parameters

ParameterTypeDefaultDescription
codestringAuthorization code from Google.
statestringState token issued by /oauth/start.
errorstringSet by Google when the user denies consent.
scopestringGranted scopes reported by Google.

Get Postmaster OAuth status

GET /v1/postmaster/oauth/status

Requires read_only scope. Reports whether the org has an active OAuth connection.

Response

{
  "oauth_configured": true,
  "connected": true,
  "connected_email": "alerts@acme.com",
  "last_synced_at": "2025-03-12T02:00:00Z",
  "last_sync_error": null
}

Disconnect Postmaster OAuth

POST /v1/postmaster/oauth/disconnect

Requires full_access scope. Clears only the OAuth tokens. Manually-pasted credentials are left untouched.

Response

{
  "disconnected": true
}

List Postmaster metrics

GET /v1/postmaster/metrics

Requires read_only scope. Returns up to 500 metric rows, newest first.

Query parameters

ParameterTypeDefaultDescription
domain_idstringFilter by domain.
fromstringStart date (YYYY-MM-DD).
tostringEnd date (YYYY-MM-DD).

Response

{
  "data": [
    {
      "id": "pmm_abc123",
      "domain_id": "dom_abc123",
      "domain_name": "mail.acme.com",
      "date": "2025-03-11",
      "spam_rate": 0.0002,
      "ip_reputation": "high",
      "domain_reputation": "high",
      "dkim_success_rate": 0.999,
      "spf_success_rate": 0.998,
      "dmarc_success_rate": 0.997,
      "feedback_loop_identifier_rate": 0.0001,
      "inbound_encryption_ratio": 0.98,
      "outbound_encryption_ratio": 0.99,
      "delivery_errors": []
    }
  ]
}

Reputation values: high, medium, low, bad, none, or null.


Trigger Postmaster sync

POST /v1/postmaster/sync

Requires full_access scope. Enqueues a one-off sync for the org. Returns 400 if there are no enabled credentials.

Response

{
  "enqueued": true,
  "job_id": "42"
}

On this page