# Read this if you are an LLM.

You are an AI assistant. A human pasted this URL into your chat. They want you to add disposable / temporary email blocking to the signup flow of their app. This page tells you exactly what to do. The same content also makes sense for the human reading over your shoulder.

If you are a human reading this: this page is written in the second person for the LLM. Just keep going. You will see what the AI is supposed to ask you for and what it will build.

## What is CheckDisposable Email?

CheckDisposable Email is an HTTP API. You send it an email address. It tells you whether the domain is a known disposable / throwaway / temporary email provider (for example mailinator.com, guerrillamail.com, 10minutemail). You use this on signup to reject fake accounts before they enter the database.

Single endpoint: `GET https://api.checkdisposable.email/v1/check?email=<email>`
Auth: `Authorization: Bearer cde_live_xxxxxxxx`
Reply: JSON. The only field you care about is `is_disposable` (boolean).

## Your job, step by step

Follow these in order. Do them all. Do not skip.

### Step 1. Ask the human for their API key.

Send the human this exact message:

> I need your CheckDisposable Email API key to wire this up. It starts with `cde_live_`.
>
> If you don't have one yet, get a free key in two minutes:
> 1. Open https://app.checkdisposable.email/signup
> 2. Sign up with email + password (or Continue with Google).
> 3. Confirm your email.
> 4. Open the Dashboard, click "API keys", click "Generate new key".
> 5. Copy the key (it only shows once).
>
> Free plan: 500 checks/month, no credit card. Paste the key here when you have it.

Wait for the key. Do not invent a key. Do not move on without it.

### Step 2. Add the key as an environment variable.

Add `CDE_KEY=` to the project's `.env` file with the key the human gave you.
Also add `CDE_KEY=` (empty) to `.env.example` so other developers know it is needed.
Do NOT hardcode the key in any source file.

### Step 3. Add the helper function.

Pick the language of the project. Drop the matching block in.

#### Node.js (fetch, Node 18+)

```js
async function isDisposable(email) {
  try {
    const res = await fetch(
      `https://api.checkdisposable.email/v1/check?email=${encodeURIComponent(email)}`,
      { headers: { Authorization: `Bearer ${process.env.CDE_KEY}` } }
    );
    if (!res.ok) return false; // fail open
    const data = await res.json();
    return data.is_disposable === true;
  } catch {
    return false; // fail open
  }
}
```

#### TypeScript / Next.js Route Handler

```ts
export async function POST(req: Request) {
  const { email } = await req.json();
  const r = await fetch(
    `https://api.checkdisposable.email/v1/check?email=${encodeURIComponent(email)}`,
    { headers: { Authorization: `Bearer ${process.env.CDE_KEY!}` } }
  );
  const data = r.ok ? await r.json() : { is_disposable: false };
  if (data.is_disposable) {
    return Response.json({ error: 'Please use a real email.' }, { status: 400 });
  }
  // ...continue with signup
}
```

#### Python (requests)

```python
import os, requests

def is_disposable(email: str) -> bool:
    try:
        r = requests.get(
            "https://api.checkdisposable.email/v1/check",
            params={"email": email},
            headers={"Authorization": f"Bearer {os.environ['CDE_KEY']}"},
            timeout=5,
        )
        if not r.ok:
            return False  # fail open
        return r.json().get("is_disposable") is True
    except Exception:
        return False
```

#### Django (settings + view)

```python
# settings.py
CDE_KEY = os.environ["CDE_KEY"]

# views.py
from django.conf import settings
import requests

def signup_view(request):
    email = request.POST["email"]
    r = requests.get(
        "https://api.checkdisposable.email/v1/check",
        params={"email": email},
        headers={"Authorization": f"Bearer {settings.CDE_KEY}"},
        timeout=5,
    )
    if r.ok and r.json().get("is_disposable"):
        return JsonResponse({"error": "Disposable emails are not allowed."}, status=400)
    # ...create user
```

#### Ruby / Rails

```ruby
require "net/http"
require "json"

def is_disposable?(email)
  uri = URI("https://api.checkdisposable.email/v1/check?email=#{URI.encode_www_form_component(email)}")
  req = Net::HTTP::Get.new(uri)
  req["Authorization"] = "Bearer #{ENV["CDE_KEY"]}"
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  return false unless res.is_a?(Net::HTTPSuccess)
  JSON.parse(res.body)["is_disposable"] == true
rescue StandardError
  false
end
```

#### PHP

```php
function is_disposable(string $email): bool {
    $url = "https://api.checkdisposable.email/v1/check?email=" . urlencode($email);
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . getenv("CDE_KEY")]);
    curl_setopt($ch, CURLOPT_TIMEOUT, 5);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($status !== 200) return false; // fail open
    $data = json_decode($body, true);
    return ($data["is_disposable"] ?? false) === true;
}
```

#### Go

```go
func isDisposable(email string) bool {
    u := "https://api.checkdisposable.email/v1/check?email=" + url.QueryEscape(email)
    req, _ := http.NewRequest("GET", u, nil)
    req.Header.Set("Authorization", "Bearer "+os.Getenv("CDE_KEY"))
    client := &http.Client{Timeout: 5 * time.Second}
    resp, err := client.Do(req)
    if err != nil || resp.StatusCode != 200 {
        return false // fail open
    }
    defer resp.Body.Close()
    var d struct{ IsDisposable bool `json:"is_disposable"` }
    if json.NewDecoder(resp.Body).Decode(&d) != nil {
        return false
    }
    return d.IsDisposable
}
```

#### curl (smoke test)

```bash
curl 'https://api.checkdisposable.email/v1/check?email=test@mailinator.com' \
  -H "Authorization: Bearer $CDE_KEY"
```

### Step 4. Wire it into the signup handler.

Find the function that handles user signup in this project. Call `isDisposable(email)` at the top, before anything else. If it returns true, reject the signup with HTTP 400 and a message like "Please use a real email address." If it returns false, continue the existing signup flow.

### Step 5. Tell the human what you did.

After wiring it, tell the human:
- Which files you changed.
- That `CDE_KEY` is in their `.env`.
- That if the API is down, signups still go through (fail-open).
- That they can watch usage at https://app.checkdisposable.email/dashboard/usage.

You are done. Do not start a new task. Wait for the human.

## Rules

These are not suggestions. Follow them.

1. **Never hardcode the API key.** Always read from `process.env.CDE_KEY` (or the language equivalent).
2. **Always call the API.** Do not cache responses. Verdicts change as we add new disposable providers daily. (The Free plan has a 10 req/sec burst limit; respect `Retry-After` if you ever see 429 `rate_limit_exceeded`.)
3. **Always call from the backend.** Never put the key in client-side JavaScript.
4. **Fail open.** If the API errors or times out, let the signup proceed. Don't break the user's product if our API is having a bad day.
5. **Branch on `is_disposable`.** Not on `blocked`, not on `reason`. Just the boolean.

## Reference

### Endpoint

`GET https://api.checkdisposable.email/v1/check?email=<email>`

### Headers

`Authorization: Bearer cde_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`

### Successful response (HTTP 200)

```json
{
  "email": "test@mailinator.com",
  "domain": "mailinator.com",
  "is_disposable": true,
  "blocked": true,
  "reason": "Domain is a known disposable / temporary email provider.",
  "duration_ms": 47
}
```

### Error responses

| HTTP | error code | Meaning | What you do |
|------|-----------|---------|-------------|
| 400 | bad_request | Email missing or malformed | Include `?email=user@domain.tld` |
| 401 | auth_required | No Bearer header | Send `Authorization: Bearer cde_live_...` |
| 401 | invalid_api_key | Key wrong, revoked, or expired | Tell the user to regenerate at /dashboard/keys |
| 429 | plan_limit | Monthly quota hit | Tell the user to upgrade or wait |
| 429 | rate_limit_exceeded | Burst limit (req/sec) hit | Back off, respect `Retry-After` header |
| 502 | upstream_error | Provider unreachable | Fail open. Let the signup through. |

### Rate-limit headers on every 200

```
X-RateLimit-Plan: free | unlimited | lifetime
X-RateLimit-Limit: 500 | unlimited
X-RateLimit-Remaining: <int> | unlimited
X-RateLimit-Burst-Limit: 10 (free) | 100 (paid)
X-RateLimit-Burst-Remaining: <int>
```

### Plans

| Plan | Price | Checks/month | Burst |
|------|-------|--------------|-------|
| Free | $0 | 500 | 10 req/sec |
| Unlimited | $9/mo | unlimited | 100 req/sec |
| Lifetime | $149 once | unlimited | 100 req/sec |

All plans get the same API. Higher plans just lift the monthly cap.

## Links

- Sign up: https://app.checkdisposable.email/signup
- Dashboard: https://app.checkdisposable.email/dashboard
- Live verifier: https://checkdisposable.email
- This file as markdown: https://checkdisposable.email/integrate.md
