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
| Parameter | Type | Default | Description |
|---|---|---|---|
domain_id | string | Restrict the report to a single domain. | |
days | number | 30 | Trailing 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
| Parameter | Type | Default | Description |
|---|---|---|---|
domain_id | string | Restrict to a single domain. | |
days | number | 30 | Trailing 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
| Parameter | Type | Default | Description |
|---|---|---|---|
from | string | Start date (YYYY-MM-DD) for the snapshot range. | |
to | string | End 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
| Parameter | Type | Default | Description |
|---|---|---|---|
target | string | Filter to a single domain or IP. | |
target_type | string | One 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
| Field | Type | Required | Description |
|---|---|---|---|
target | string | Yes | Domain name or IP address. |
target_type | string | Yes | One 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
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | Must 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
| Parameter | Type | Default | Description |
|---|---|---|---|
threshold | number | 5000 | Override 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
| Parameter | Type | Default | Description |
|---|---|---|---|
domain | string | Filter by the reported domain. | |
limit | number | 25 | Results per page (1-100). |
cursor | string | Pagination 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
| Parameter | Type | Default | Description |
|---|---|---|---|
domain | string | Filter by the reported domain. | |
days | number | 7 | Trailing 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
| Field | Type | Required | Description |
|---|---|---|---|
google_oauth_refresh_token | string | Yes | Refresh token from your own Google Cloud project. |
google_oauth_client_id | string | Yes | OAuth 2.0 client ID. |
google_oauth_client_secret | string | Yes | OAuth 2.0 client secret. |
enabled | boolean | No | Defaults 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
| Parameter | Type | Default | Description |
|---|---|---|---|
return_to | string | Relative 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
| Parameter | Type | Default | Description |
|---|---|---|---|
code | string | Authorization code from Google. | |
state | string | State token issued by /oauth/start. | |
error | string | Set by Google when the user denies consent. | |
scope | string | Granted 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
| Parameter | Type | Default | Description |
|---|---|---|---|
domain_id | string | Filter by domain. | |
from | string | Start date (YYYY-MM-DD). | |
to | string | End 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"
}