API v1 JSON Token authentication REST

API Reference

Build integrations against Skysnag with scoped API tokens. Authenticate using the X-Api-Token header — not Bearer auth. All responses are JSON with predictable error shapes.

https://developers.skysnag.com/api/v1
X-Api-Token
sk_snag_…
60 req / min

Quickstart

Three steps to your first authenticated request.

  1. Request API access — Submit from API Access Requests. An administrator enables API access on your account.
  2. Create a scoped token — Open API Tokens, select scopes, and copy the full token immediately (shown once).
  3. Send authenticated requests — Include X-Api-Token: sk_snag_… on every protected call.
cURL — health check (no auth)
curl -s "https://developers.skysnag.com/api/v1/health" \
  -H "Accept: application/json"

Postman collection

Import a ready-made collection and environment to explore every v1 endpoint in Postman.

  1. Download the collectionSkysnag-API-v1.postman_collection.json
  2. Download the environmentSkysnag-API-v1.postman_environment.json (pre-filled with this app's base URL: https://developers.skysnag.com/api/v1)
  3. Import both files in Postman → Import → drag the JSON files or browse to them.
  4. Set your token — In the environment, set api_token to your full token from API Tokens.
  5. Run requests — Start with System → Health, then Authentication → Get Me.

Authentication

Protected endpoints require a personal API token in a dedicated header. Do not use Authorization: Bearer.

HeaderValueWhen
X-Api-Token Full token (sk_snag_…) All protected endpoints
cURL — authenticated request
curl -s "https://developers.skysnag.com/api/v1/auth/me" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token_here"
Try the API in your browser

Paste a token to enable the Try it console on GET endpoints below (read-only). Requests run against your live account at https://developers.skysnag.com/api/v1. The token is stored only in this browser (localStorage) and is never sent anywhere except this API.

No key set

Base URL & headers

All v1 paths are relative to the base URL below.

https://developers.skysnag.com/api/v1
https://api.skysnag.com/v1
HeaderValueNotes
Acceptapplication/jsonAlways
Content-Typeapplication/jsonWhen sending a body
X-Api-TokenYour API tokenProtected endpoints
X-Request-IdOptional UUIDCorrelate logs; echoed in errors

Responses

Success payloads are wrapped in a data envelope.

JSON — success envelope
{
  "data": { ... }
}
JSON — GET /health
{
  "data": {
    "status": "ok",
    "version": "v1"
  }
}

Errors

Errors return a stable code, human message, and request_id for support.

JSON — error envelope
{
  "error": {
    "code": "missing_api_token",
    "message": "API token is required...",
    "request_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}
HTTPCodeDescription
401missing_api_tokenX-Api-Token not provided
401invalid_api_tokenToken unknown, revoked, or expired
403api_access_disabledAccount lacks API access
403api_plan_not_eligibleComply or trial plan — upgrade to Protect or Suite
403insufficient_scopeToken missing required scope
422validation_errorInvalid request body
404not_foundResource not found
429http_errorRate limit exceeded
500internal_errorUnexpected server error

List pagination

List endpoints return pagination metadata alongside data.

ParameterTypeDefaultDescription
pageinteger1Page number (1-based)
limitintegervariesItems per page (max 100)
JSON — paginated response
{
  "data": [ ... ],
  "pagination": {
    "page": 1,
    "limit": 25,
    "total": 120,
    "has_more": true
  }
}

Rate limits

Each token is limited to 60 requests per minute by default.

When exceeded, the API returns HTTP 429. Implement exponential backoff. Contact support for higher limits.

Scopes

Tokens are granted scoped permissions. Endpoints reject requests when the required scope is missing.

API v1 scopes

ScopeDescription
account:readRead account and token metadata
me:readRead current user profile, permissions, domains, and security
me:writeUpdate current user profile
users:readList account users and domain access
users:writeCreate, update, and delete account users
tokens:readList active API tokens
tokens:writeCreate and revoke API tokens
domains:readList and read domains
domains:writeCreate, update, and delete domains

Legacy scopes

Valid on existing tokens for older integrations. Not required for new v1 auth endpoints.

ScopeDescription
email-trust:readRead DMARC/SPF/BIMI trust data
reports:readRead aggregate and compliance reports
integrations:readRead integration settings
integrations:writeManage integrations

API reference

Endpoint documentation for the current v1 surface area.

Health check

GET /health

Public health check. No authentication required.

FieldTypeDescription
data.statusstringok when reachable
data.versionstringAPI version (v1)
http
curl -s "https://developers.skysnag.com/api/v1/health" \
  -H "Accept: application/json"
GET /auth/scopes

Returns assignable scopes grouped by v1 and legacy. No authentication required.

cURL
curl -s "https://developers.skysnag.com/api/v1/auth/scopes" \
  -H "Accept: application/json"
JSON — response
{
  "data": {
    "v1": [{"id": "account:read", "description": "...", "group": "v1"}],
    "legacy": [{"id": "domains:read", "description": "...", "group": "legacy"}]
  }
}
GET /auth/me account:read

Returns the authenticated account and metadata for the token used. Alias of GET /account.

cURL
curl -s "https://developers.skysnag.com/api/v1/auth/me" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token_here"

Current User API

Read and update the authenticated user. Schemas: users, roles, teams, user_regions, domain_accesses, webauthn_credentials, password_securities.

GET /me me:read

Current user profile.

cURL
curl -s "https://developers.skysnag.com/api/v1/me" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token_here"
GET /me/permissions me:read

Effective permissions derived from the user role and account settings.

cURL
curl -s "https://developers.skysnag.com/api/v1/me/permissions" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token_here"
GET /me/domains me:read

Domains visible to the current user. Supports page and limit.

cURL
curl -s "https://developers.skysnag.com/api/v1/me/domains?page=1&limit=25" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token_here"
GET /me/security me:read

MFA and passkey security status for the current user.

cURL
curl -s "https://developers.skysnag.com/api/v1/me/security" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token_here"
PATCH /me me:write

Update the current user profile.

FieldTypeDescription
namestringDisplay name (max 50 characters)
languagestringPreferred language code
cURL
curl -s "https://developers.skysnag.com/api/v1/me" \
  -X PATCH \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "X-Api-Token: sk_snag_your_token_here" \
  -d '{
    "name": "Updated Name"
  }'

User Management API

Manage account team members. Requires the authenticated account to be the team owner. Schemas: users, roles, teams, domain_accesses, password_securities, webauthn_credentials, user_detail_changes_history.

GET /roles users:read

List roles that can be assigned when inviting or updating team members. Owner, Admin, and Editor are excluded.

GET /roles/{role_id} users:read

Get details for a single assignable role.

GET /users users:read

List account users on the current team.

POST /users users:write

Create an account member invitation. Sends an invite email by default.

GET /users/{user_id} users:read

Get user details.

PATCH /users/{user_id} users:write

Update user details.

DELETE /users/{user_id} users:write

Delete a team member.

PATCH /users/{user_id}/status users:write

Toggle user status. Body: {"enabled": true}

PATCH /users/{user_id}/role users:write

Change role. Body: {"role_id": 5}

PATCH /users/{user_id}/enforce-2fa users:write

Toggle 2FA enforcement. Body: {"enabled": true}

POST /users/{user_id}/reinvite-user users:write

Resend the account invitation email to an existing team member.

GET /users/{user_id}/domains users:read

List domain access for a user.

Domain Management API

Manage account domains. Write operations require the team owner account. Schemas: domains, domain_groups, domain_accesses, domain_states, domain_snapshot, parked_domains, license_domain, domains_services, cloudflare_records.

GET /domains domains:read

List domains visible to the account. Supports page and limit.

POST /domains domains:write

Create a domain. Body: {"fqdn": "example.com", "parent_domain_id": null}

POST /domains/bulk domains:write

Bulk add domains. Body: {"domains": ["example.com", "example.org"]}. Returns a job ID.

GET /domains/bulk/{job_id} domains:read

Get bulk add job status and per-domain results.

GET /domains/{domain_id} domains:read

Get domain details including DNS and verification status.

PATCH /domains/{domain_id} domains:write

Edit domain metadata (group, status, onboarding status, parent domain).

DELETE /domains/{domain_id} domains:write

Delete a domain and related hosted records.

POST /domains/{domain_id}/verify domains:write

Verify domain DNS records. Optional body fields: dmarc_record, spf_record, bimi_record, tls_rpt_record, mta_sts_record.

POST /domains/{domain_id}/check-dns domains:write

Run a DNS fetch/check and store the result for retrieval.

GET /domains/{domain_id}/dns-fetch-result domains:read

Get the latest DNS fetch result stored by check-dns (cached for 7 days). The check-dns response body already includes the full result; use this endpoint to retrieve it later without re-running the check.

GET /domains/{domain_id}/status domains:read

Get protocol verification status (DMARC, SPF, MTA-STS, TLS-RPT, BIMI).

GET /domains/{domain_id}/services domains:read

Get attached email sending services.

PATCH /domains/{domain_id}/services domains:write

Replace attached services. Body: {"service_ids": [1, 2]}

Domain Groups API

Organize domains into groups. Groups are owned by the account team; team owner required for write operations.

GET /domain-groups domains:read

List domain groups for the account with domain counts.

POST /domain-groups domains:write

Create a domain group. Body: {"name": "Production", "status": "active"} (status optional).

GET /domain-groups/{group_id} domains:read

Get a single domain group.

PATCH /domain-groups/{group_id} domains:write

Edit a domain group. Body: {"name": "New name", "status": "active"} (both optional).

DELETE /domain-groups/{group_id} domains:write

Delete a domain group. Member domains are ungrouped, not deleted.

GET /domain-groups/{group_id}/domains domains:read

List domains in a group (paginated).

POST /domain-groups/{group_id}/domains domains:write

Add a domain to the group. Body: {"domain_id": 123}.

DELETE /domain-groups/{group_id}/domains/{domain_id} domains:write

Remove a domain from the group (the domain becomes ungrouped).

Hosted DMARC API

Manage Skysnag-hosted DMARC DNS records, enforcement policy, history, and recommendations for a domain. Team owner required for write operations.

GET /domains/{domain_id}/hosted-dmarc domains:read

Returns hosted record name, value, enablement status, and current policy.

POST /domains/{domain_id}/hosted-dmarc domains:write

Provision the Skysnag-hosted DMARC TXT record for the domain.

PATCH /domains/{domain_id}/hosted-dmarc domains:write

Update advanced DMARC tags: p, sp, pct, adkim, aspf, rua, ruf, etc.

DELETE /domains/{domain_id}/hosted-dmarc domains:write

Remove the hosted DMARC record from Skysnag DNS.

GET /domains/{domain_id}/dmarc-policy domains:read

Read the effective DMARC policy and parsed tag settings.

PATCH /domains/{domain_id}/dmarc-policy domains:write

Update enforcement only. Body: {"policy": "quarantine"} or {"p": "reject"}

GET /domains/{domain_id}/dmarc/history domains:read

Activity logs and DMARC snapshots. Supports page and limit.

POST /domains/{domain_id}/dmarc/recommendation domains:read

Suggested enforcement level based on alignment stats and monitoring period.

Hosted SPF API

Manage Skysnag-hosted SPF records, includes, IP authorization actions, and flattening for a domain. Team owner required for write operations.

GET /domains/{domain_id}/hosted-spf domains:read

Returns hosted SPF record name/value, the record to publish at your domain, enablement status, terminal all qualifier, and lookup count.

POST /domains/{domain_id}/hosted-spf domains:write

Provision the Skysnag-hosted SPF record for the domain.

PATCH /domains/{domain_id}/hosted-spf domains:write

Edit advanced SPF settings. Body: {"all": "~all"} (one of -all, ~all, ?all, +all).

DELETE /domains/{domain_id}/hosted-spf domains:write

Disable hosted SPF and remove the record from Skysnag DNS.

GET /domains/{domain_id}/spf/history domains:read

SPF activity logs and record snapshots. Supports page and limit.

GET /domains/{domain_id}/spf/includes domains:read

List SPF includes configured for the domain.

POST /domains/{domain_id}/spf/includes domains:write

Add an SPF include. Body: {"include_content": "_spf.google.com"}

DELETE /domains/{domain_id}/spf/includes/{include_id} domains:write

Remove an SPF include from the hosted record.

GET /domains/{domain_id}/spf/ip-actions domains:read

List SPF IP authorization actions (Allow / Reject).

POST /domains/{domain_id}/spf/ip-actions domains:write

Authorize or block an IP. Body: {"ip": "203.0.113.10", "type": "Allow"} (type = Allow or Reject).

POST /domains/{domain_id}/spf/flatten domains:read

Generate a flattened SPF record by resolving includes into IP ranges. Returns the flattened record, IPv4/IPv6 lists, and DNS lookup count vs. limit.

BIMI / VMC API

Manage BIMI records, SVG logos, VMC certificates, and readiness checks. Team owner required for write operations.

GET /bimi/registrations account:read

List partner BIMI registration requests for the account team.

GET /domains/{domain_id}/bimi domains:read

Returns BIMI record details, DNS targets, logo/VMC status, and SVG inspection results.

POST /domains/{domain_id}/bimi domains:write

Provision hosted BIMI for the domain.

PATCH /domains/{domain_id}/bimi domains:write

Re-sync or edit BIMI. Republishes from uploaded files, or pass record_value for a manual override.

DELETE /domains/{domain_id}/bimi domains:write

Delete BIMI configuration, hosted files, and Route53 record.

POST /domains/{domain_id}/bimi/svg-convert domains:read

Convert and validate SVG for BIMI Tiny PS. Body: {"svg_content": "<svg...>"}

GET /domains/{domain_id}/bimi/vmc domains:read

Full VMC analysis (DNS, certificate chain, validation). Query refresh=true to bypass cache.

POST /domains/{domain_id}/bimi/vmc/inspect domains:read

Inspect a PEM certificate without uploading. Body: {"pem": "-----BEGIN CERTIFICATE-----..."}

GET /domains/{domain_id}/bimi/readiness domains:read

BIMI/VMC readiness checklist with score, protocol status, and per-check pass/fail.

MTA-STS API

Manage hosted MTA-STS and TLS-RPT for a domain. Team owner required for write operations.

GET /domains/{domain_id}/mta-sts domains:read

Returns MTA-STS status, policy mode, customer CNAME targets, hosted DNS records, and sync check metadata.

GET /domains/{domain_id}/mta-sts/web-file domains:read

Returns the MTA-STS policy file content served at https://mta-sts.{domain}/.well-known/mta-sts.txt.

POST /domains/{domain_id}/mta-sts/setup domains:write

Provision hosted MTA-STS and TLS-RPT records. Creates a default policy (mode: none) from live MX records when needed.

PATCH /domains/{domain_id}/mta-sts/policy domains:write

Update the MTA-STS policy. Pass raw policy text, or structured fields: mode, max_age, and optional mx hostnames.

PATCH /domains/{domain_id}/mta-sts/mode domains:write

Update policy mode only. Body: {"mode": "none|testing|enforce"}. MX lines are refreshed from live DNS.

POST /domains/{domain_id}/mta-sts/reset domains:write

Reset policy to defaults: mode: none, current MX records, max_age: 604800.

POST /domains/{domain_id}/mta-sts/check domains:read

Run MTA-STS and TLS-RPT validation via the checker service. Optional body: verify_dns, require_caa, deploy_policy (default true when verifying DNS).

TLS-RPT API

Configure hosted TLS-RPT and query received TLS reports. Team owner required for write operations.

GET /domains/{domain_id}/tls-rpt domains:read

Returns TLS-RPT configuration, DNS targets, verification status, and total report count.

POST /domains/{domain_id}/tls-rpt/setup domains:write

Provision hosted TLS-RPT in Route53 with the Skysnag reporting address.

PATCH /domains/{domain_id}/tls-rpt domains:write

Re-publish the hosted TLS-RPT record. Optional body: record_value (must start with v=TLSRPTv1).

GET /domains/{domain_id}/tls-rpt/reports domains:read

Paginated TLS reports. Filters: policy_domain, policy_mode, start_date, end_date.

GET /domains/{domain_id}/tls-rpt/reports/{report_id} domains:read

Get a single TLS report by database id or report UUID.

GET /domains/{domain_id}/tls-rpt/by-source domains:read

Aggregate TLS sessions grouped by sending MTA IP. Supports the same date and policy filters.

GET /domains/{domain_id}/tls-rpt/by-result domains:read

Aggregate failed sessions grouped by TLS result type.

GET /domains/{domain_id}/tls-rpt/failures domains:read

TLS failure summary with success/fail totals and breakdown by failure reason code.

GET /domains/{domain_id}/tls-rpt/failure-details domains:read

Paginated rows with TLS failures (non-zero failure counts or failure reason codes).

Domain Health &amp; Security Score API

Read-only domain health, email security score, mail volume, sending services, failing sources, and cached dashboard data. All endpoints are scoped to {domain_id} and require domains:read. Volume/source endpoints accept optional start_date/end_date (YYYY-MM-DD, default last 30 days).

Get domain health

get /domains/{domain_id}/health domains:read

Current domain health snapshot: a per-protocol pass/fail map, a qualitative result label, the detected mail provider, and the live DMARC record. Sourced from the latest Domain Guard scan, falling back to live verification flags when no scan exists (source tells you which).

Returns

source (guard_history | live), result, mail_provider, a protocols map of booleans, protocols_passing/total_protocols, the dmarc_record, and scanned_at.
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/health" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "domain_id": 123,
    "fqdn": "example.com",
    "source": "guard_history",
    "result": "Protected",
    "mail_provider": "Google Workspace",
    "protocols": {
      "dmarc": true,
      "spf": true,
      "mta_sts": true,
      "tls_rpt": true,
      "bimi": false
    },
    "protocols_passing": 4,
    "total_protocols": 5,
    "dmarc_record": "v=DMARC1; p=reject; rua=mailto:rua@example.com",
    "scanned_at": "2026-06-16T03:00:00Z"
  }
}

Get health history

get /domains/{domain_id}/health/history domains:read

Paginated history of Domain Guard health snapshots, newest first. Each row captures the protocol pass/fail state and result label at the time of a scan, letting you chart protection over time.

Query Parameters

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD). Defaults to 30 days ago.

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD). Defaults to today.

  • page: optional integer

    Page number, 1-based (default 1).

  • limit: optional integer

    Items per page (1–100, default 25).

Returns

A paginated array of snapshot rows (id, result, mail_provider, protocols map, protocols_passing/total_protocols, scanned_at) plus pagination.
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/health/history?page=1&limit=25" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": [
    {
      "id": 8841,
      "result": "Protected",
      "mail_provider": "Google Workspace",
      "protocols": { "dmarc": true, "spf": true, "mta_sts": true, "tls_rpt": true, "bimi": false },
      "protocols_passing": 4,
      "total_protocols": 5,
      "scanned_at": "2026-06-16T03:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 25,
    "total": 312,
    "has_more": true
  }
}

Get security score

get /domains/{domain_id}/security-score domains:read

The current Email Security Score (0–10) with a human-readable interpretation and message. Computed on demand from cached external checker results, so the first call after a cache miss may be slower. Returns 503 score_unavailable if it cannot be computed.

Returns

score, max_score (10), interpretation (e.g. excellent), a message, and computed_at.
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/security-score" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "domain_id": 123,
    "fqdn": "example.com",
    "score": 9,
    "max_score": 10,
    "interpretation": "excellent",
    "message": "Your domain is well protected against spoofing and phishing.",
    "computed_at": "2026-06-16T12:30:00Z"
  }
}

Get security score history

get /domains/{domain_id}/security-score/history domains:read

A derived security-score series computed from Domain Guard snapshots, where derived_score = passing protocols / 5 × 10. Use this for a trend line when you do not need the full live-checker score.

Query Parameters

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD). Defaults to 30 days ago.

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD). Defaults to today.

  • limit: optional integer

    Maximum rows to return (1–100, default 10).

Returns

max_score, an explanatory note, and a history array of points (derived_score, protocols_passing/total_protocols, result, scanned_at).
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/security-score/history?limit=30" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "domain_id": 123,
    "fqdn": "example.com",
    "max_score": 10,
    "note": "Derived from Domain Guard protocol coverage.",
    "history": [
      {
        "derived_score": 8,
        "protocols_passing": 4,
        "total_protocols": 5,
        "result": "Protected",
        "scanned_at": "2026-06-16T03:00:00Z"
      }
    ]
  }
}

Get mail volume

get /domains/{domain_id}/mail-volume domains:read

Daily mail-volume history derived from DMARC aggregate data: delivered / quarantined / rejected counts and DMARC pass/fail per day, plus rolled-up totals for the whole window.

Query Parameters

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD). Defaults to 30 days ago.

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD). Defaults to today.

Returns

A totals object and a timeline array, each entry keyed by date with volume and pass/fail counts.
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/mail-volume?start_date=2026-05-01&end_date=2026-06-01" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "domain_id": 123,
    "fqdn": "example.com",
    "start_date": "2026-05-01",
    "end_date": "2026-06-01",
    "totals": {
      "total": 18432,
      "delivered": 18010,
      "quarantined": 280,
      "rejected": 142,
      "dmarc_pass": 17980,
      "dmarc_fail": 452
    },
    "timeline": [
      {
        "date": "2026-06-01",
        "total": 612,
        "delivered": 600,
        "quarantined": 8,
        "rejected": 4,
        "dmarc_pass": 600,
        "dmarc_fail": 12
      }
    ]
  }
}

List sending services

get /domains/{domain_id}/sending-services domains:read

Sending sources with volume, DMARC metrics, a compliance percentage, and a status of compliant, partial, or failing. The is_registered_threat flag marks sources known to be malicious.

Query Parameters

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD). Defaults to 30 days ago.

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD). Defaults to today.

  • limit: optional integer

    Maximum rows to return (1–100, default 10).

Returns

An array of services, each with source_name, the metrics object, compliance, status, and is_registered_threat.
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/sending-services?limit=10" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "domain_id": 123,
    "fqdn": "example.com",
    "services": [
      {
        "source_name": "Google",
        "total_count": 12040,
        "dmarc": { "pass": 12010, "fail": 30 },
        "compliance": 99.75,
        "status": "compliant",
        "is_registered_threat": false
      }
    ]
  }
}

List failed sources

get /domains/{domain_id}/failed-sources domains:read

Sources that failed DMARC alignment in the window, ordered by failing volume. These are the highest-priority sources to investigate — either legitimate senders that need authentication fixed, or spoofers.

Query Parameters

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD). Defaults to 30 days ago.

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD). Defaults to today.

  • limit: optional integer

    Maximum rows to return (1–100, default 10).

Returns

An array of failed_sources with the same shape as sending-services, ordered by failing volume descending.
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/failed-sources?limit=10" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "domain_id": 123,
    "fqdn": "example.com",
    "failed_sources": [
      {
        "source_name": "Unknown",
        "total_count": 420,
        "dmarc": { "pass": 0, "fail": 420 },
        "compliance": 0,
        "status": "failing",
        "is_registered_threat": true
      }
    ]
  }
}

Get dashboard cache

get /domains/{domain_id}/dashboard-cache domains:read

Returns the precomputed dashboard snapshot payload for the domain, if one exists. This is the fastest way to render a dashboard without recomputing aggregates. When no snapshot exists, cached is false and data is null.

Query Parameters

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD). Defaults to 30 days ago.

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD). Defaults to today.

Returns

cached (boolean), cached_at, the requested window, and the snapshot data payload (or null).
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/dashboard-cache" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "domain_id": 123,
    "fqdn": "example.com",
    "cached": true,
    "cached_at": "2026-06-16T03:05:00Z",
    "start_date": "2026-05-17",
    "end_date": "2026-06-16",
    "data": { "summary": { "total_count": 18432 } }
  }
}

DNS Timeline API

Read-only DNS connect/disconnect timeline, current records, record-value history, change events, and stored snapshots. All endpoints are scoped to {domain_id} and require domains:read. Filterable by record_type and start_date/end_date (YYYY-MM-DD).

Get DNS timeline

get /domains/{domain_id}/dns/timeline domains:read

A per-protocol status summary (current status, last connected/disconnected timestamps, and connection counts) plus the most recent connect/disconnect events. This is the headline view of how each protocol record has come and gone over time.

Query Parameters

  • record_type: optional string

    Filter events to one of dmarc, spf, mta_sts, tls_rpt, bimi.

  • action: optional string

    Filter to connected or disconnected.

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD). Defaults to 30 days ago.

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD). Defaults to today.

  • limit: optional integer

    Maximum rows to return (1–100, default 10).

Returns

A summary keyed by protocol and an events array (id, record_type, action, record_value, status_before/status_after, occurred_at).
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/dns/timeline?limit=50" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "domain_id": 123,
    "fqdn": "example.com",
    "summary": {
      "dmarc": {
        "current_status": "connected",
        "last_connected": "2026-05-02T10:15:00Z",
        "last_disconnected": null,
        "connection_count": 2,
        "disconnection_count": 1
      }
    },
    "events": [
      {
        "id": 5521,
        "record_type": "dmarc",
        "action": "connected",
        "record_value": "v=DMARC1; p=reject; rua=mailto:rua@example.com",
        "status_before": "disconnected",
        "status_after": "connected",
        "occurred_at": "2026-05-02T10:15:00Z"
      }
    ]
  }
}

Get current DNS

get /domains/{domain_id}/dns/current domains:read

The current state of each protocol record: connection status, verification flag, last connected/disconnected timestamps, and the latest known record value. SPF additionally reports its DNS lookup_count (the 10-lookup limit matters for validity).

Returns

A protocols map keyed by protocol, each with current_status, verified, status, last_connected/last_disconnected, and record_value.
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/dns/current" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "domain_id": 123,
    "fqdn": "example.com",
    "protocols": {
      "dmarc": {
        "current_status": "connected",
        "verified": true,
        "status": "valid",
        "last_connected": "2026-05-02T10:15:00Z",
        "last_disconnected": null,
        "record_value": "v=DMARC1; p=reject; rua=mailto:rua@example.com"
      },
      "spf": {
        "current_status": "connected",
        "verified": true,
        "record_value": "v=spf1 include:_spf.google.com -all",
        "lookup_count": 3
      }
    }
  }
}

Get DNS history

get /domains/{domain_id}/dns/history domains:read

A unified, paginated, chronological history of record values — merging DMARC and SPF snapshots so you can see exactly what each record contained at each point in time. SPF rows include lookup_count.

Query Parameters

  • record_type: optional string

    Limit to dmarc or spf (default both).

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD). Defaults to 30 days ago.

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD). Defaults to today.

  • page: optional integer

    Page number, 1-based (default 1).

  • limit: optional integer

    Items per page (1–100, default 25).

Returns

A paginated array of history rows (record_type, record_value, occurred_at; SPF adds lookup_count) plus pagination.
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/dns/history?record_type=spf&page=1&limit=25" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": [
    {
      "record_type": "spf",
      "record_value": "v=spf1 include:_spf.google.com -all",
      "lookup_count": 3,
      "occurred_at": "2026-05-02T10:15:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 25,
    "total": 312,
    "has_more": true
  }
}

List DNS changes

get /domains/{domain_id}/dns/changes domains:read

A paginated feed of connect/disconnect change events across all protocols, with the status transition (status_beforestatus_after). Use this for an audit-style activity feed of DNS health.

Query Parameters

  • record_type: optional string

    One of dmarc, spf, mta_sts, tls_rpt, bimi.

  • action: optional string

    Filter to connected or disconnected.

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD). Defaults to 30 days ago.

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD). Defaults to today.

  • page: optional integer

    Page number, 1-based (default 1).

  • limit: optional integer

    Items per page (1–100, default 25).

Returns

A paginated array of change events (id, record_type, action, record_value, status_before/status_after, occurred_at) plus pagination.
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/dns/changes?action=disconnected&page=1&limit=25" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": [
    {
      "id": 5520,
      "record_type": "spf",
      "action": "disconnected",
      "record_value": null,
      "status_before": "connected",
      "status_after": "disconnected",
      "occurred_at": "2026-04-28T08:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 25,
    "total": 312,
    "has_more": true
  }
}

List DNS snapshots

get /domains/{domain_id}/dns/snapshots domains:read

The raw stored snapshot rows, grouped by record type. Unlike /dns/history (which merges and paginates), this returns the underlying dmarc and spf snapshot arrays separately.

Query Parameters

  • record_type: optional string

    Limit to dmarc or spf (default both).

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD). Defaults to 30 days ago.

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD). Defaults to today.

  • limit: optional integer

    Maximum rows to return (1–100, default 10).

Returns

A dmarc array and an spf array of snapshot rows (id, record_value, occurred_at; SPF adds lookup_count).
Example
curl -s "https://developers.skysnag.com/api/v1/domains/123/dns/snapshots?limit=50" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "domain_id": 123,
    "fqdn": "example.com",
    "dmarc": [
      {
        "id": 331,
        "record_value": "v=DMARC1; p=reject; rua=mailto:rua@example.com",
        "occurred_at": "2026-05-02T10:15:00Z"
      }
    ],
    "spf": [
      {
        "id": 902,
        "record_value": "v=spf1 include:_spf.google.com -all",
        "lookup_count": 3,
        "occurred_at": "2026-05-02T10:15:00Z"
      }
    ]
  }
}

Audit Logs API

Read-only account API audit logs — every mutating API request (POST/PUT/PATCH/DELETE) plus sensitive reads are recorded with method, path, status, IP, token, and timestamp. Scoped to the token owner's account and require the account:read scope.

The audit log object

Every endpoint in this section returns rows of this shape. action is a stable, human-readable label for the operation; request_id matches the request_id echoed in the response envelope of the original call.

Audit log object
{
  "id": 90122,
  "request_id": "req_8f2c1a4e",
  "action": "domain.hosted_spf.update",
  "method": "PATCH",
  "path": "/api/v1/domains/123/hosted-spf",
  "status_code": 200,
  "ip_address": "203.0.113.24",
  "token_id": 12,
  "token_name": "CI deploy token",
  "user_id": 45,
  "team_id": 9,
  "created_at": "2026-06-16T11:02:33Z"
}

List audit logs

get /audit-logs account:read

Paginated audit logs for the account, newest first. Combine filters to narrow the feed — e.g. all failing writes in a date range.

Query Parameters

  • method: optional string

    HTTP method, e.g. POST.

  • action: optional string

    Exact action label, e.g. domain.hosted_spf.update.

  • path: optional string

    Substring match against the request path.

  • status_code: optional integer

    Exact HTTP status, e.g. 200.

  • status_class: optional string

    Status family: 2xx, 3xx, 4xx, 5xx.

  • ip_address: optional string

    Exact client IP.

  • token_id: optional integer

    Filter to a single API token.

  • q: optional string

    Free-text search across action, path and IP.

  • start_date: optional string · date

    Inclusive window start (YYYY-MM-DD).

  • end_date: optional string · date

    Inclusive window end (YYYY-MM-DD).

  • page: optional integer

    Page number (default 1).

  • limit: optional integer

    Items per page (1–100, default 25).

Returns

A paginated array of audit log objects plus a pagination object.
Example
curl -s "https://developers.skysnag.com/api/v1/audit-logs?page=1&limit=25" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": [
    {
      "id": 90122,
      "request_id": "req_8f2c1a4e",
      "action": "domain.hosted_spf.update",
      "method": "PATCH",
      "path": "/api/v1/domains/123/hosted-spf",
      "status_code": 200,
      "ip_address": "203.0.113.24",
      "token_id": 12,
      "token_name": "CI deploy token",
      "user_id": 45,
      "team_id": 9,
      "created_at": "2026-06-16T11:02:33Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 25,
    "total": 312,
    "has_more": true
  }
}

Get audit log

get /audit-logs/{log_id} account:read

Fetch a single audit log entry by ID, scoped to the account. Returns 404 not_found if the entry does not belong to the authenticated account.

Path parameters

  • log_id: required integer

    The audit log entry ID.

Returns

A single audit log object.
Example
curl -s "https://developers.skysnag.com/api/v1/audit-logs/90122" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token"
Response
{
  "data": {
    "id": 90122,
    "request_id": "req_8f2c1a4e",
    "action": "domain.hosted_spf.update",
    "method": "PATCH",
    "path": "/api/v1/domains/123/hosted-spf",
    "status_code": 200,
    "ip_address": "203.0.113.24",
    "token_id": 12,
    "token_name": "CI deploy token",
    "user_id": 45,
    "team_id": 9,
    "created_at": "2026-06-16T11:02:33Z"
  }
}

Token Management

GET /auth/tokens tokens:read

List active tokens for the account. Supports page and limit.

cURL
curl -s "https://developers.skysnag.com/api/v1/auth/tokens?page=1&limit=25" \
  -H "Accept: application/json" \
  -H "X-Api-Token: sk_snag_your_token_here"
POST /auth/tokens tokens:write

Create a token. Full value returned once in data.plain_text.

FieldTypeDescription
name reqstringToken label
scopes reqstring[]From GET /auth/scopes
expires_in_daysintegerOptional 1&ndash;365 days
cURL
curl -s "https://developers.skysnag.com/api/v1/auth/tokens" \
  -X POST \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "X-Api-Token: sk_snag_your_token_here" \
  -d '{
    "name": "CI token",
    "scopes": ["account:read"],
    "expires_in_days": 90
  }'