Take every field ContactLint returns and give it one decision: Accept, Warn, or Block. Fill this in once and keep it next to your form code. The next time someone asks what the form does with an info@ address, the answer is already written down.
GET /openapi.json, says what it tells you and what it does not, and gives the default ContactLint suggests where one exists. Tick one decision per check and write down why.
| Item | Your answer |
|---|---|
| Form or page this sheet covers | |
| Email endpoint | POST https://contactlint.com/v1/email/validate, body {"email": "..."} |
| Phone endpoint | POST https://contactlint.com/v1/phone/validate, body {"phone": "...", "country": "US"} |
Default country value for phone checks | (two-letter code; "US" is the documented example) |
| Where the API call runs | Server sideServerless function Keep the key out of browser code. You are responsible for all use of your key, and it is shown only once at signup. If a key is exposed, email support@contactlint.com so it can be disabled. |
| When the check fires | On submitOn field blurBoth Every call uses one check, so checking on blur can spend more checks per visitor than checking on submit. |
| Auth header you will send | Authorization: Bearer YOUR_KEYx-api-key |
| Key label used at signup (optional) | |
| Owner of this sheet | Date: |
These fields live in the result object returned by POST /v1/email/validate.
syntax_valid (true / false)syntax_valid is false: AcceptWarnBlockmx_found and a_found (true / false each)mx_found is false and a_found is false: AcceptWarnBlockmx_found is false but a_found is true: AcceptWarnBlockmx_found / a_founddeliverable_domaindisposable (true / false)disposable is true: AcceptWarnBlockrole_address (true / false)role_address is true: AcceptWarnBlockfree_provider (true / false)free_provider is true: AcceptWarnBlockverdict shortcut (valid / risky / invalid / unknown)| verdict | Action | Note |
|---|---|---|
valid | AcceptWarnBlock | |
risky | AcceptWarnBlock | |
invalid | AcceptWarnBlock | |
unknown | AcceptWarnBlock |
Results are best effort. They come from public DNS records, open-source lists, and phone metadata at the time of the check, so they can be wrong or out of date. Do not use a result as the only basis for a decision that seriously affects a person.
These fields live in the result object returned by POST /v1/phone/validate. Parsing uses libphonenumber metadata. Carrier lookups are not offered today. Send a country value with every request.
parsed (true / false)parsed is false: AcceptWarnBlockvalid (true / false)valid is false: AcceptWarnBlockpossible (true / false)possible is true and valid is false: AcceptWarnBlockpossible is false: AcceptWarnBlocktype (line type, where the data supports it)type is missing: AcceptWarnBlockGET /openapi.json so your comparison matches what the API returns.regionregion is not on that list: AcceptWarnBlocke164 and the other formatse164NationalInternationalContactLint never connects to the mailbox. It does not confirm that a mailbox exists or that a person will receive mail. If you need that, make it a separate step. Sending a confirmation email to the address is one way.
| Question | Your answer |
|---|---|
| Do you send a confirmation email after signup? | YesNoOnly when verdict is risky or unknown |
| What happens to a record that never confirms? | |
| Do you confirm phone numbers some other way, such as a code the user enters? | YesNo |
| Which decisions on this sheet seriously affect a person, and what second signal do you check before acting? |
Four codes cover it. Decide now whether the form fails open (accepts the input and flags it) or fails closed (asks the user to try again).
| Code | Meaning | Form behavior |
|---|---|---|
400 | Bad input. | |
401 | Missing or invalid key. | Fail open and alertFail closed Alert goes to: |
402 | Free checks used up and no credits. | Fail open and alertFail closed Alert goes to: |
429 | More than 60 requests per minute per key. | Retry after a short wait. Wait length: Max retries: |
| Timeout or network error | No response from the API. | Timeout value: Then: Fail open and flagFail closed |
The 60 per minute limit is per key. If the same key also runs CRM cleanup or bulk jobs, count those requests together with the form and pace them.
Every key gets 100 free checks per calendar month, and unused free checks do not roll over. One check is one API or MCP call to a validation tool. Email and phone checks draw from the same allowance.
| Line | Your number |
|---|---|
| A. Expected form submissions per month | |
| B. Checks per submission (1 for email only, 2 for email and phone, more if you check on blur) | |
| C. Other checks on this key per month (CRM cleanup, agents via MCP) | |
| D. Total checks per month = (A times B) plus C | |
| E. Checks beyond the free 100 = D minus 100 (write 0 if negative) | |
| F. Months you want one purchase to cover | |
| G. Credits to buy = E times F |
| Plan | Checks | Price (USD) | Covers your G? |
|---|---|---|---|
| Credit pack | 5,000 | $9 | |
| Credit pack | 25,000 | $35 | |
| Credit pack | 100,000 | $99 |
Credits are used after the monthly free checks run out, and they do not expire. Unused credits are refundable within 14 days of purchase: email support@contactlint.com. Used credits are not refundable. GET /v1/usage returns free checks used this month and your credit balance.
Alert me when the credit balance drops below: Checked by: Scheduled jobManual
What you can say about the vendor side, taken from the ContactLint privacy policy and terms:
| Item | Your answer |
|---|---|
| Where your privacy policy names ContactLint as a processor | |
| Do you log the full API response, or only the fields you act on? | Full responseSelected fields: |
| Retention for those logs |
ContactLint has two free on-page tools. Put them on the page where this checklist lives, or in your docs or internal wiki, so anyone can try a single value by hand before the code ships.
| Tool | URL |
|---|---|
| Disposable email checker | https://contactlint.com/disposable-email-checker |
| Phone number validator | https://contactlint.com/phone-number-validator |
<section class="cl-tools" aria-label="Free ContactLint checkers">
<h2>Try a value by hand</h2>
<p>Paste one email or phone number into a free checker.</p>
<ul>
<li><a href="https://contactlint.com/disposable-email-checker"
rel="noopener">Disposable email checker</a></li>
<li><a href="https://contactlint.com/phone-number-validator"
rel="noopener">Phone number validator</a></li>
</ul>
<p>Ready to automate it?
<a href="https://contactlint.com/signup">Get a free API key</a>:
100 checks a month, no card required.</p>
</section>
Only use this after you have checked in a browser that the tool page actually renders inside a frame. If it does not, ship the link card from 7.1 instead. Tested and working on:
<iframe
src="https://contactlint.com/disposable-email-checker"
title="ContactLint disposable email checker"
loading="lazy"
width="100%"
height="520"
style="border:1px solid #d0d7de;border-radius:6px;">
<a href="https://contactlint.com/disposable-email-checker">
Open the disposable email checker</a>
</iframe>
<iframe
src="https://contactlint.com/phone-number-validator"
title="ContactLint phone number validator"
loading="lazy"
width="100%"
height="520"
style="border:1px solid #d0d7de;border-radius:6px;">
<a href="https://contactlint.com/phone-number-validator">
Open the phone number validator</a>
</iframe>
Place the frames below the checklist so the sheet comes first. On screens narrower than 768px, show the link card only.
curl -X POST https://contactlint.com/v1/email/validate \
-H "Authorization: Bearer YOUR_KEY" \
-H "content-type: application/json" \
-d '{"email":"someone@example.com"}'
| Step | Done |
|---|---|
| Every check in sections 1 and 2 has one decision ticked | |
| Section 3 confirmation plan is written down | |
| Section 4 fail-open or fail-closed choices are in the code | |
| Section 5 credit purchase or balance alert is set | |
| Key is stored server side, not in the browser bundle | |
| Reviewed by | Date: |