---
title: Migrate from Email & Domain APIs
description: How to move from the /email and /domain endpoints to the Gates Decision API.
---

The **Gates Decision API** replaces manual logic built around the `/email` and `/domain` endpoints. Instead of pulling raw signals and writing your own logic, you define reusable rules directly in the dashboard and let the Gate decide.

This guide shows how to migrate existing integrations in a few steps.

## 1. Create a Gate

Start by creating a new Gate for the flow you want to protect.  
See [Quickstart](/docs/gates/quickstart) for setup.

## 2. Rebuild your checks as rules

Take the logic you currently apply after calling `/email` or `/domain`, for example:

- `if disposable → block`
- `if spam → block`
- etc...

Then recreate it as **rules** inside your Gate.  
Each rule can check one or more fields (like `domain.mx`, `email.role_account`) and choose an action: **allow**, **block**, or **challenge**.  
See [Rules & Conditions](/docs/gates/rules-conditions) for field references.

## 3. Update your API integration

Replace your `GET /email` or `GET /domain` calls with a `POST` to your Gate’s Decision endpoint:

```bash
curl -X POST "https://api.usercheck.com/v0/gates/{gate_id}/decisions" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@example.com" }'
````

Swap `"email"` for `"domain"` if that’s your input.

The response contains three key objects:

```json
{
  "decision": {
    "action": "block",
    "matched_rule": { "name": "Block domains with no MX records" }
  },
  "signals": {
    "email": { "mx": false, "disposable": false },
    "domain": { "mx": false, "spam": false }
  }
}
```

* **decision.action** — the Gate’s decision (`allow`, `block`, or `challenge`)
* **decision.matched_rule** — which rule triggered, including its name and optional message
* **signals** — the same diagnostic data you’d get from `/email` or `/domain`

> The Gate already applies your rules and returns a verdict. Treat `signals` as informational only.

## 4. Example migration

**Before: manual logic**

```python
response = requests.get(
    f"https://api.usercheck.com/email/{email}",
    headers={"Authorization": f"Bearer {API_KEY}"},
)
data = response.json()

if data.get("disposable"):
    raise SignUpError("Disposable emails are not allowed.")

if data.get("relay_domain"):
    raise SignUpError("Please use your real email address to register.")

normalized_email = data.get("normalized_email", email)

# Proceed with signup
```

**After: Gate decision**

```python
response = requests.post(
    f"https://api.usercheck.com/v0/gates/{GATE_ID}/decisions",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={"email": email},
)
data = response.json()
decision = data.get("decision", {})

if decision.get("action") == "block":
    message = decision.get("matched_rule", {}).get("message")
    raise SignUpError(message)

if decision.get("action") == "challenge":
    tagAccountAsHighRisk()

normalized_email = data.get("signals", {}).get("email", {}).get("normalized", email)

# Proceed with signup
```

## 5. Using rule messages in your UI

Each decision includes an optional `matched_rule.message` field when a rule triggers.  
If you want to display context to your users, you can surface this message in your own interface.

Typical usage:

1. Inspect `decision.action` (`allow`, `block`, or `challenge`)
2. Optionally read `decision.matched_rule.message` for user-facing context 

How you handle that information is entirely up to your application.

## 6. Need help?

For complex migrations or multi-condition logic, contact [support@usercheck.com](mailto:support@usercheck.com) and we’ll walk through your setup.
