VeriDock

Quickstart

Create an API key from the dashboard, then call /v1/verify. That is the whole integration.

curl "https://api.veridock.io/v1/verify?email=jane.doe@acme.com" \
  -H "Authorization: Bearer vd_live_••••••••••••"

Authentication

Every request needs a bearer token in the Authorization header. Keys are prefixed vd_live_ or vd_test_. Test keys return deterministic fixtures and never consume credits, so your CI can run against them.

Authorization: Bearer vd_live_••••••••••••

Each key has its own usage, rate limit, and optional spend cap. Never ship a live key to a browser; use a test key or proxy through your server.

Verify an address

GET /v1/verify?email={email}

ParameterTypeDescription
emailstring, requiredThe address to verify. URL-encode it.
smtpboolean, default trueSet false to skip the live SMTP check for a faster, syntax-and-DNS-only answer.
timeoutinteger, default 8000Maximum milliseconds to wait for the receiving server before returning unknown.
cacheboolean, default trueSet false to force a fresh check and bypass the 30-day result cache.

POST /v1/verify with a JSON body {"email": "…"} is also accepted and behaves identically.

Response fields

200 OKDeliverable
{
  "email": "jane.doe@acme.com",
  "status": "deliverable",
  "reason": "accepted_email",
  "score": 97,
  "checks": {
    "syntax": true,
    "domain": true,
    "mx": true,
    "smtp": true,
    "disposable": false,
    "role_account": false,
    "catch_all": false,
    "free_provider": false
  },
  "domain": {
    "name": "acme.com",
    "mx": ["aspmx.l.google.com"],
    "provider": "google_workspace"
  },
  "suggestion": null,
  "billed": true,
  "duration_ms": 412
}
FieldTypeDescription
emailstringThe normalized (lower-cased, trimmed) address that was verified.
statusenumdeliverable · undeliverable · risky · unknown. Branch on this.
reasonenumThe specific finding behind the status. See the full list below.
scoreinteger0–100 confidence that mail will be delivered. Useful for thresholds on risky results.
checks.syntaxbooleanRFC 5322 syntax passed.
checks.domainbooleanDomain resolves in DNS.
checks.mxbooleanDomain publishes at least one MX record.
checks.smtpbooleanReceiving server accepted the mailbox during the SMTP conversation.
checks.disposablebooleanDomain is a known temporary or throwaway inbox provider.
checks.role_accountbooleanLocal part is a role such as info, sales, or support.
checks.catch_allbooleanDomain accepts every address, so mailbox existence cannot be confirmed.
checks.free_providerbooleanDomain is a public webmail provider such as gmail.com.
domain.mxstring[]Up to three MX hosts, lowest priority first.
domain.providerstring | nullDetected mail provider, e.g. google_workspace or microsoft_365.
suggestionstring | nullA corrected address when a likely typo is detected.
billedbooleanWhether this request consumed a credit. Always false for unknown and for cached results.
duration_msintegerServer-side processing time.

Statuses and reasons

Branch your code on status. Use reason when you want finer rules, for example allowing role accounts for B2B signups but rejecting disposable ones.

DeliverableThe mailbox exists and the receiving server confirmed it will accept mail for this address.
accepted_email

Billing: one credit, cached 30 days.

UndeliverableThe address cannot receive mail. The syntax is wrong, the domain has no mail server, or the mailbox was rejected.
invalid_syntaxinvalid_domainno_mx_recordrejected_emailmailbox_full

Billing: one credit, cached 30 days.

RiskyThe address might receive mail, but sending to it carries risk: disposable inboxes, role accounts, or catch-all domains.
disposablerole_accountcatch_alllow_qualitylow_deliverability

Billing: one credit, cached 30 days.

UnknownThe mail server timed out, greylisted us, or refused the check. We retry automatically before returning this.
timeoutgreylistedunavailable_smtp

Billing: never charged.

Bulk verification

Send an array of addresses or upload a CSV. Jobs run asynchronously, you can poll or receive a webhook, and results are available as CSV, JSON, or newline-delimited JSON filtered by status.

curl -X POST https://api.veridock.io/v1/bulk \
  -H "Authorization: Bearer vd_live_••••••••••••" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "october-newsletter",
    "emails": ["a@acme.com", "b@acme.com", "c@example.org"],
    "webhook_url": "https://example.com/hooks/veridock"
  }'

# → { "id": "job_8f2k1", "status": "queued", "total": 3 }
Job statusMeaning
queuedAccepted and waiting for a worker.
processingRunning. processed increments as results come in.
completedAll rows done. download_url is valid for 7 days.
failedCould not run. The error field explains why. Nothing is billed.

Webhooks

Pass webhook_url when creating a job and we POST the job object to it on completion. Every delivery is signed with your webhook secret in the X-VeriDock-Signature header (HMAC-SHA256 of the raw body). We retry failed deliveries with exponential backoff for 24 hours.

import crypto from "node:crypto";

export function verifySignature(rawBody: string, header: string, secret: string) {
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}

Errors

Errors return a JSON body of the shape {"error": {"code", "message"}}. Failed requests are never billed.

HTTPcodeWhen
400invalid_requestMissing or malformed email parameter.
401unauthorizedMissing, revoked, or malformed API key.
402quota_exceededMonthly credits and spend cap exhausted. Raise the cap or upgrade.
404not_foundUnknown job id.
422unprocessableBulk payload exceeds plan limits or has no valid rows.
429rate_limitedToo many concurrent requests. Honor the Retry-After header.
500internal_errorSomething broke on our side. Retried requests are idempotent and never double-billed.

Rate limits

Limits are on concurrent in-flight requests per key, not requests per minute, because SMTP checks vary in duration. Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and, on 429, Retry-After.

PlanConcurrent requestsBulk rows per job
Free21,000
Starter5100,000
Growth201,000,000
Scale100Unlimited

SDKs and integrations

The API is plain HTTPS and JSON, so any HTTP client works. Official packages wrap it with types and retries.

PlatformInstallNotes
Node.js / TypeScriptnpm i @veridock/sdkTyped responses, automatic retries, edge-runtime compatible.
Pythonpip install veridockSync and async clients.
PHPcomposer require veridock/veridockPSR-18 compatible.
Gogo get github.com/veridock/veridock-goContext-aware client.
Zapier · Make · n8nSearch for VeriDockVerify on new row, new subscriber, or form submission.

Something missing? Tell us and we will add it.