Documentation

DMPHub REST API

Single endpoint per verification track. Bearer-token auth, JSON in / JSON out. For interactive testing with your real keys, open the API Explorer inside the dashboard.

Authentication

Bearer token in the Authorization header. Generate keys under Settings → API Keys.

Rate limits

Per-key limit visible in the response headers (X-RateLimit-*). Hitting the limit returns HTTP 429.

Versioning

The current API version is /v1. New versions ship under /v2 etc. — old versions keep working for 6 months after a new version ships.

How-to

Verify your first email in 5 steps

From zero to a graded result. Uses the live /v1/verify/email endpoint — the same one your dashboard uses under the hood.

  1. Step 1

    Generate an API key

    In the dashboard, open API Access → API Keys → Create key. Copy the value — it only shows once. Anyone holding it can verify on your behalf.

  2. Step 2

    Send your first request

    The endpoint is a single POST. Auth is a Bearer header. The body is one JSON field. Replace the email and the key with your own.

    curl -X POST https://api.dmphub.com/v1/verify/email \
      -H "Authorization: Bearer dmph_live_…" \
      -H "Content-Type: application/json" \
      -d '{"email":"jane@example.com"}'
  3. Step 3

    Read the result

    A 200 response is a completed verification. The shape is the same JSON the dashboard renders into a result row.

    {
      "email": "jane@example.com",
      "status": "valid",
      "grade": "A",
      "score": 96,
      "checks": { "syntax": true, "mx": true, "smtp": true }
    }
  4. Step 4

    Run a batch from a CSV

    Use the list endpoint for > 1 email. It accepts a JSON array, returns a job_id, then you poll the job URL until it completes.

    curl -X POST https://api.dmphub.com/v1/verify/email/list \
      -H "Authorization: Bearer dmph_live_…" \
      -H "Content-Type: application/json" \
      -d '{"emails":["a@x.com","b@x.com","c@x.com"]}'
  5. Step 5

    Watch the dashboard

    Every API call appears in the dashboard's recent activity feed (color-coded by channel) and counts against your monthly quota. No separate sync step.

Need a working key to test with? Log into the dashboard and open the API Explorer — it has the same endpoints, with a dark-themed code panel and your key auto-populated.

Grading rubric

Every result — quick-verify, batch, API — lands in the same A-F band. The score range below matches what we surface on the at-a-glance table; the meaning column tells you what to do about it. Channels are independent services; one channel’s grade has no influence on another’s.

Email

Combines tier-1 deliverability (syntax, DNS/MX, SMTP) with tier-2 risk signals. Score 0 = best, 100 = worst. The grade is separate from the status: status is valid (passed SMTP), invalid (failed a check), unknown (no SMTP response) or risky (spam trap, screamer, DND, organisation domain, honeypot, role account, disposable or catch-all).

GradeScore bandWhat it means
A+0–9Clean deliverable address with no risk signals.
A10–19Deliverable, very low risk. Safe to send.
B+20–29Deliverable but one weak signal (e.g. catch-all domain).
B30–39Mostly clean, possibly on suppression-adjacent list.
C40–59Mixed signals — review before sending.
D60–79Likely deliverability problems. Use cautiously.
F80–100Undeliverable, suppressed, or hard-failed.
Signals that feed this channel’s score
  • Syntax (RFC 5322)
  • Domain MX records
  • SMTP handshake (skipped in dev / when port 25 is blocked)
  • Bounce DB match (informational)
  • Spam-trap list match
  • Role-account flag (admin@, support@, …)
  • Disposable / temp-mail domain
  • Organisation domain — government, military, education, large corporate (risky)
  • Screamer / DND / honeypot (tenant suppression reasons)

Phone

Combines carrier lookup (Telnyx), HLR active/inactive flag, line type, and DNC registry presence. Phone has no "Valid/Invalid" string — grade IS the verdict.

GradeScore bandWhat it means
A0–19Active mobile or landline, no DNC hit, no disposable signal.
B20–39Active number with one weak signal (e.g. VoIP).
C40–59Mixed signals — could be inactive or DNC-listed.
D60–79Likely VoIP / burner / DNC. Do not call.
F80–100Inactive, disconnected, or unreachable.
Signals that feed this channel’s score
  • Line type (mobile / landline / VoIP)
  • Carrier (best-effort, ~50% accurate)
  • Active / inactive (HLR lookup)
  • DNC (Do-Not-Call) registry
  • Disposable / burner number flag

Address

Driven by our USPS-certified address verification provider. We forward the raw address string verbatim — the provider's verdict is what we surface.

GradeScore bandWhat it means
A0–19Deliverable, occupied address.
B20–39Deliverable but with a minor flag (e.g. vacant lot).
C40–59Partially matchable — verify components before use.
D60–79Known deliverability issue.
F80–100Not a recognized USPS deliverable address.
Signals that feed this channel’s score
  • DPV (Delivery Point Validation) match code
  • Vacancy indicator
  • RDI (Residential Delivery Indicator)
  • Standardized address components (street, city, state, ZIP)

Domain

Trust score from the in-process domain-health pipeline. Combines DNS reachability, TLS certificate validity, SPF/DKIM/DMARC posture, and blocklist presence.

GradeScore bandWhat it means
A80–100Healthy, authenticated, on no blocklist.
B60–79Reachable, minor flag (e.g. SPF missing).
C40–59Reachability OK but authentication incomplete.
D20–39Multiple failures. Treat as risky.
F0–19Unreachable or on blocklists.
Signals that feed this channel’s score
  • DNS resolve
  • TLS certificate validity
  • SPF / DKIM / DMARC
  • Email reachability (MX present)
  • Blocklists

Verification pipeline

What happens between POST /v1/verify/... and the row landing in your results table. Each stage short-circuits on definitive failure, so a 500-syntax rejection never costs you a DNS round trip. Open the interactive flow diagram →

Email pipeline

  1. 1. Syntax

    Parses the address with the same RFC 5322 rules mail servers use. Rejects malformed strings before any network call.

    On failure: Returns invalid. No DNS or SMTP attempt — saves a round trip.

  2. 2. DNS / MX

    Looks up the domain's MX records (and A records as fallback). No MX means the domain can't receive mail.

    On failure: Returns invalid. Skips SMTP — there's nothing to talk to.

  3. 3. SMTP handshake

    Opens a TCP connection to the highest-priority MX host and runs a lightweight RCPT TO probe. No mail is sent. Skipped when SMTP_VERIFICATION_MODE=off or in development environments without port 25.

    On failure: Returns unknown — the mailbox could not be checked — unless a risk signal fired, which makes it risky. A 5xx at RCPT TO returns invalid.

  4. 4. Bounce DB

    Checks our internal bounce + complaint ledger. Hard bounces and abuse complaints push the score into the D/F band.

    On failure: n/a — informational, doesn't reject.

  5. 5. Risk signals

    Spam trap, screamer, DND and honeypot (from your suppression lists and our spam-domain feed), organisation domains (government, military, education, large corporate), role accounts, disposable domains and catch-all domains.

    On failure: Any signal returns risky. Doesn't reject outright.

  6. 6. Risk scoring + grade

    Aggregates the tier-1 + tier-2 signals into a 0-100 risk score and maps to A+/A/B+/B/C/D/F via the rubric on this page. The score and grade are shown alongside the status; they do not decide it.

    On failure: n/a — terminal stage.

Phone pipeline

  1. 1. Normalize

    Strips formatting, prepends +1 if missing for US/CA numbers, and re-validates the E.164 shape.

    On failure: Returns invalid.

  2. 2. Telnyx Number Lookup

    Calls Telnyx's number_lookup API to retrieve carrier, line type, and HLR active/inactive.

    On failure: Marks the row as 'Not checked' — we don't claim active or inactive when the lookup is missing.

  3. 3. DNC registry

    Cross-references the US Do-Not-Call registry (where permitted by jurisdiction).

    On failure: n/a — informational.

  4. 4. Score + grade

    Aggregates line-type, active, DNC, and disposable flags into the A-F phone rubric.

    On failure: n/a — terminal stage.

Address pipeline

  1. 1. Passthrough

    We forward the raw address string to our USPS-certified address provider verbatim — no parsing, no munging.

    On failure: The provider returns 0 candidates → we surface 'Not a recognized USPS deliverable address.'

  2. 2. Cache check

    Before calling the provider, we hash the address and check the in-process cache (10-min TTL). Hits are free.

    On failure: n/a.

  3. 3. Score + grade

    DPV match code + vacancy + RDI are folded into the A-F address rubric.

    On failure: n/a — terminal stage.

Domain pipeline

  1. 1. DNS

    Resolves the A/AAAA records. Unresolvable domains fail immediately.

    On failure: Returns F.

  2. 2. TLS

    Pulls the certificate chain and verifies it chains to a trusted CA.

    On failure: Subtracts from trust score.

  3. 3. SPF / DKIM / DMARC

    Reads the TXT records at _spf, _dmarc, and common DKIM selectors. Missing DMARC is the most common issue.

    On failure: Subtracts from trust score.

  4. 4. Blocklists

    Cross-references the domain against public blocklists (Spamhaus DBL, etc.).

    On failure: Pushes to F.

  5. 5. Score + grade

    Aggregates the above into the A-F domain rubric.

    On failure: n/a — terminal stage.

Endpoints

Five public endpoints ship in v1. Each example below uses {baseUrl} — substitute your dashboard origin (e.g. https://app.dmphub.com).

POST/api/v1/verify/email

Single Verify (Email)

Verify a single email address in real-time. Returns status, risk score, and the tier-1/tier-2 signal breakdown.

Request

200
curl -X POST {baseUrl}/api/v1/verify/email \
  -H "Authorization: Bearer dmph_live_..." \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com"}'

Response

{
  "email": "user@example.com",
  "status": "valid",
  "riskReasons": [],
  "domainStatus": "valid",
  "riskScore": 0.02,
  "catchAll": false,
  "domainHealth": {
    "grade": "A",
    "score": 95,
    "spf": true,
    "dkim": true,
    "dmarc": true
  },
  "provider": "Google Workspace",
  "tier1": {
    "syntax": true,
    "domain": true,
    "mxFound": true,
    "smtp": "250 OK",
    "bounceDb": false,
    "recency": true
  },
  "tier2": {
    "spamTrap": false,
    "roleAccount": false,
    "disposable": false,
    "complaint": false,
    "protected": false,
    "optOut": false,
    "screamer": false,
    "dnd": false,
    "honeypot": false
  },
  "recommendation": "Safe to send",
  "latencyMs": 1842,
  "creditsUsed": 1,
  "verifiedAt": "2026-07-07T12:00:00.000Z"
}
POST/api/v1/verify/list/start

Batch Verify (Email)

Start a batch verification job. Returns the job id; poll GET /api/v1/jobs/{id} for status.

Request

200
curl -X POST {baseUrl}/api/v1/verify/list/start \
  -H "Authorization: Bearer dmph_live_..." \
  -H "Content-Type: application/json" \
  -d '{"jobId":"11111111-1111-1111-1111-111111111111","columnMapping":{"emailColumnIndex":0}}'

Response

{
  "jobId": "11111111-1111-1111-1111-111111111111",
  "chunked": true,
  "chunks": 2
}
GET/api/v1/jobs

List Jobs

Top-level jobs (parentJobId IS NULL) for the calling tenant, with childCount + grade + coverage. Capped at 20.

Request

200
curl {baseUrl}/api/v1/jobs \
  -H "Authorization: Bearer dmph_live_..."

Response

{
  "items": [
    {
      "id": "11111111-1111-1111-1111-111111111111",
      "type": "batch",
      "status": "completed",
      "totalCount": 3000,
      "processedCount": 3000,
      "fileName": "june-batch.csv",
      "s3InputKey": "tenants/acme/jobs/11111111-1111-1111-1111-111111111111/input.csv",
      "s3OutputKey": "tenants/acme/jobs/11111111-1111-1111-1111-111111111111/output.jsonl",
      "parentJobId": null,
      "chunkOffset": null,
      "chunkSize": null,
      "errorMessage": null,
      "createdAt": "2026-06-02T14:00:00.000Z",
      "updatedAt": "2026-06-02T14:08:51.000Z",
      "startedAt": null,
      "completedAt": "2026-06-02T14:08:51.000Z",
      "childCount": 0,
      "grade": "A",
      "coverage": { "processed": 3000, "total": 3000, "pct": 1 },
      "issues": { "count": 0, "types": [] },
      "resultCounts": { "valid": 2431, "invalid": 389, "risky": 180 },
      "phoneResultCounts": {}
    }
  ],
  "total": 1,
  "page": 1,
  "pageSize": 20
}
GET/api/v1/jobs/{id}

Job Detail

Single job with children, result breakdown, grade, coverage, and issue counts.

Request

200
curl {baseUrl}/api/v1/jobs/11111111-1111-1111-1111-111111111111 \
  -H "Authorization: Bearer dmph_live_..."

Response

{
  "id": "11111111-1111-1111-1111-111111111111",
  "type": "batch",
  "status": "completed",
  "totalCount": 3000,
  "processedCount": 3000,
  "fileName": "june-batch.csv",
  "s3InputKey": "tenants/acme/jobs/11111111-1111-1111-1111-111111111111/input.csv",
  "s3OutputKey": "tenants/acme/jobs/11111111-1111-1111-1111-111111111111/output.jsonl",
  "parentJobId": null,
  "errorMessage": null,
  "createdAt": "2026-06-02T14:00:00.000Z",
  "updatedAt": "2026-06-02T14:08:51.000Z",
  "completedAt": "2026-06-02T14:08:51.000Z",
  "children": [],
  "resultCounts": { "valid": 2431, "invalid": 389, "risky": 180 },
  "phoneResultCounts": {},
  "grade": "A",
  "coverage": { "processed": 3000, "total": 3000, "pct": 1 },
  "issues": { "count": 0, "types": [] }
}
GET/api/v1/suppression-lists

List Suppression Lists

All suppression lists for the calling tenant, each with an entries count.

Request

200
curl {baseUrl}/api/v1/suppression-lists \
  -H "Authorization: Bearer dmph_live_..."

Response

[
  {
    "id": "22222222-2222-2222-2222-222222222222",
    "name": "Hard Bounces",
    "entries": 412,
    "status": "active",
    "dateUploaded": "2026-05-12T10:30:00.000Z",
    "lastModified": "2026-06-28T09:14:00.000Z"
  }
]