{"openapi":"3.1.0","info":{"title":"ValidReach API","version":"1.0.0","description":"Email verification API. Authenticate with an API key. Each verification that returns a billable result uses 1 credit."},"servers":[{"url":"https://validreach.dev"}],"security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"paths":{"/api/v1/verify/single":{"post":{"operationId":"verifySingle","summary":"Verify one email address","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string"},"check_smtp":{"type":"boolean"},"force_refresh":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Verification result","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"$ref":"#/components/schemas/VerifyResult"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/verify/bulk":{"post":{"operationId":"verifyBulk","summary":"Verify up to 50 addresses synchronously","description":"For larger lists use the file endpoints.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["emails"],"properties":{"emails":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Per-address results and totals","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"type":"object","required":["results","total_emails","valid_emails","invalid_emails","credits_used","process_time"],"properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/VerifyResult"}},"total_emails":{"type":"integer"},"valid_emails":{"type":"integer"},"invalid_emails":{"type":"integer"},"credits_used":{"type":"integer"},"process_time":{"type":"number","description":"Milliseconds"}}}}}}}},"400":{"description":"Invalid request or more than 50 emails","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/verify/disposable":{"get":{"operationId":"checkDisposableGet","summary":"Check whether an address uses a disposable domain","description":"Free lookup against the disposable-domain list. It does not verify the mailbox.","parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Disposable-domain lookup","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"type":"object","required":["email","domain","is_disposable","checked_at"],"properties":{"email":{"type":"string"},"domain":{"type":"string"},"is_disposable":{"type":"boolean"},"checked_at":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"email is required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"checkDisposable","summary":"Check whether an address uses a disposable domain","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string"}}}}}},"responses":{"200":{"description":"Disposable-domain lookup","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"type":"object","required":["email","domain","is_disposable","checked_at"],"properties":{"email":{"type":"string"},"domain":{"type":"string"},"is_disposable":{"type":"boolean"},"checked_at":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"email is required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/verify/file":{"post":{"operationId":"createFileJob","summary":"Upload a list for asynchronous verification","description":"Accepts .csv, .txt, .tsv and .xlsx (first sheet). Legacy .xls is rejected with UNSUPPORTED_FILE_FORMAT. Poll the status endpoint until the job is terminal.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"},"name":{"type":"string"},"email_column":{"type":"string","description":"Header name of the address column. If it is not in the header, no addresses are read; another column is never guessed."},"check_smtp":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Job accepted","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"type":"object","required":["task_id","status","status_url"],"properties":{"task_id":{"type":"string"},"file_name":{"type":"string"},"file_size":{"type":"integer"},"status":{"type":"string","enum":["pending"]},"message":{"type":"string"},"status_url":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Missing file, unsupported format, or empty list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"File too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/verify/file/{taskId}":{"get":{"operationId":"getFileJob","summary":"Get file job status and progress","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}},{"name":"timeout","in":"query","required":false,"description":"Long-poll up to this many seconds (max 300) or until the job is terminal.","schema":{"type":"integer","minimum":0,"maximum":300}}],"responses":{"200":{"description":"Job status","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"$ref":"#/components/schemas/FileJobStatus"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"File job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteFileJobData","summary":"Delete a finished job’s results and uploaded file","description":"Only for finished jobs. Credit usage counts remain for billing history.","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"type":"object","required":["task_id","deleted"],"properties":{"task_id":{"type":"string"},"deleted":{"type":"boolean","const":true}}}}}}}},"400":{"description":"Job is not finished (JOB_NOT_FINISHED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"File job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/verify/file/{taskId}/results":{"get":{"operationId":"getFileJobResults","summary":"Download or page through results","description":"CSV by default (streamed, every matching row). Use format=json for paged results.","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string"}},{"name":"format","in":"query","schema":{"type":"string","enum":["csv","json"],"default":"csv"}},{"name":"valid","in":"query","description":"Include only rows with status valid. Several filters may be combined.","schema":{"type":"boolean"}},{"name":"invalid","in":"query","description":"Include only rows with status invalid. Several filters may be combined.","schema":{"type":"boolean"}},{"name":"unknown","in":"query","description":"Include only rows with status unknown. Several filters may be combined.","schema":{"type":"boolean"}},{"name":"risky","in":"query","description":"Include only rows with status risky. Several filters may be combined.","schema":{"type":"boolean"}},{"name":"disposable","in":"query","description":"Include only rows with status disposable. Several filters may be combined.","schema":{"type":"boolean"}},{"name":"catchall","in":"query","description":"Include only rows with status catchall. Several filters may be combined.","schema":{"type":"boolean"}},{"name":"role","in":"query","description":"Include only rows with status role. Several filters may be combined.","schema":{"type":"boolean"}},{"name":"include_source_row","in":"query","description":"CSV only: append a source_row column.","schema":{"type":"boolean"}},{"name":"include_original","in":"query","description":"CSV only: append source_row and the uploaded row’s own columns as orig_<header> (needs the uploaded file, which expires 30 days after the job finishes).","schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"JSON only. Default 50, max 200.","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"offset","in":"query","description":"JSON only.","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"CSV file, or JSON page when format=json","content":{"text/csv":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"type":"object","required":["items","total","limit","offset"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/VerifyResult"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"File job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks":{"post":{"operationId":"createWebhook","summary":"Register a webhook for file job events","description":"The signing secret is returned once, in this response only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","description":"HTTPS endpoint that accepts POST."},"events":{"type":"array","minItems":1,"items":{"type":"string","enum":["file.completed","file.failed"]}}}}}}},"responses":{"200":{"description":"Webhook created (includes the secret)","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string"}}}]}}}}}},"400":{"description":"Invalid url or events","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"listWebhooks","summary":"List webhooks (secrets are never returned)","responses":{"200":{"description":"Webhooks","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"type":"object","required":["webhooks","total"],"properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"total":{"type":"integer"}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks/{webhookId}":{"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook","parameters":[{"name":"webhookId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"type":"object","required":["message","webhook_id"],"properties":{"message":{"type":"string"},"webhook_id":{"type":"string"}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/credits":{"get":{"operationId":"getCredits","summary":"Get the credit balance for the API key’s account","responses":{"200":{"description":"Balance","content":{"application/json":{"schema":{"type":"object","required":["success","code","message","data"],"properties":{"success":{"type":"boolean","const":true},"code":{"type":"string","example":"0"},"message":{"type":"string","example":"Success"},"data":{"type":"object","required":["credits_balance","credits_consumed","credits_added"],"properties":{"account_id":{"type":"string"},"api_key_id":{"type":"string"},"api_key_name":{"type":"string"},"credits_balance":{"type":"integer"},"credits_consumed":{"type":"integer"},"credits_added":{"type":"integer"},"last_updated":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"fileJobEvent":{"post":{"summary":"Delivered to your endpoint when a file job ends","description":"Headers: X-Webhook-Event, X-Webhook-Timestamp, X-Webhook-Signature. The signature is \"sha256=\" plus the hex HMAC-SHA256 of \"{timestamp}.{raw request body}\" using your webhook secret. Recompute it over the raw body and compare in constant time; reject timestamps outside a tolerance you choose to limit replays. Respond with 2xx; other responses are retried through the queue.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPayload"}}}},"responses":{"200":{"description":"Acknowledged"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"},"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key"}},"schemas":{"VerifyResult":{"type":"object","required":["email","status","is_deliverable","is_disposable","is_catchall","is_role","is_free","domain","check_smtp","reason","credits_used","retryable"],"properties":{"email":{"type":"string"},"status":{"type":"string","enum":["valid","invalid","unknown","risky","disposable","catchall","role"]},"score":{"type":"number"},"is_deliverable":{"type":"boolean"},"is_disposable":{"type":"boolean"},"is_catchall":{"type":"boolean"},"is_role":{"type":"boolean"},"is_free":{"type":"boolean"},"domain":{"type":"string"},"check_smtp":{"type":"boolean"},"reason":{"type":["string","null"]},"domain_suggestion":{"type":"string"},"credits_used":{"type":"integer","enum":[0,1]},"retryable":{"type":"boolean"}}},"FileJobStatus":{"type":"object","required":["task_id","status","progress","total_emails","processed_emails","credits_used","download_url","result_download_available"],"properties":{"task_id":{"type":"string"},"status":{"type":"string","enum":["pending","preparing","processing","completed","completed_with_warning","failed"]},"progress":{"type":"integer","minimum":0,"maximum":100},"total_emails":{"type":"integer"},"processed_emails":{"type":"integer"},"valid_emails":{"type":"integer"},"invalid_emails":{"type":"integer"},"unknown_emails":{"type":"integer"},"role_emails":{"type":"integer"},"catchall_emails":{"type":"integer"},"risky_emails":{"type":"integer"},"disposable_emails":{"type":"integer"},"credits_used":{"type":"integer"},"download_url":{"type":"string"},"result_download_available":{"type":"boolean"},"result_download_unavailable_reason":{"type":"string"},"started_at":{"type":["string","null"],"format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"}}},"Webhook":{"type":"object","required":["id","url","events","is_active","created_at"],"properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["file.completed","file.failed"]}},"is_active":{"type":"boolean"},"last_delivery_status":{"type":["string","null"]},"last_delivery_at":{"type":["string","null"]},"last_error":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"WebhookPayload":{"type":"object","required":["event","timestamp","data"],"properties":{"event":{"type":"string","enum":["file.completed","file.failed"]},"timestamp":{"type":"string","format":"date-time"},"data":{"type":"object","required":["job_id","total_emails","credits_used"],"properties":{"job_id":{"type":"string"},"file_name":{"type":"string"},"total_emails":{"type":"integer"},"valid_emails":{"type":"integer"},"invalid_emails":{"type":"integer"},"role_emails":{"type":"integer"},"catchall_emails":{"type":"integer"},"unknown_emails":{"type":"integer"},"disposable_emails":{"type":"integer"},"risky_emails":{"type":"integer"},"credits_used":{"type":"integer"},"process_time_seconds":{"type":"number"},"download_url":{"type":"string"}}}}},"Error":{"type":"object","required":["success","code","message","error"],"properties":{"success":{"type":"boolean","const":false},"code":{"type":"string","description":"Numeric envelope code: 4000 bad request, 4010 unauthorized, 4020 insufficient credits, 4040 not found, 4130 file too large, 4290 rate limited, 1000 internal"},"message":{"type":"string"},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}