> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dmand.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Embed Dmand AI Email Enrichment Directly in Your Product

> Learn how to embed Dmand AI's async NPI-to-email enrichment in your platform with webhooks, custom_data matching, idempotency keys, and credit-aware workflows.

You can embed Dmand AI's NPI-to-email enrichment directly in your product so your users get verified provider emails without leaving your app. This guide covers the integration pattern: submit asynchronously, receive results by webhook, and map them back to your records with `custom_data`.

## 1. Submit asynchronously

When a user requests enrichment, submit the NPI (or up to `60` NPIs). The API responds with HTTP 202 immediately.

```bash theme={null}
curl -X POST https://ext-api.dmand.ai/api/v1/email/bulk \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -d '{
    "npis": ["1003158791", "1234567893"],
    "enrichment_email_type": "professional",
    "webhook": {
      "url": "https://your-app.com/webhooks/dmand",
      "mode": "real-time"
    },
    "custom_data": { "tenant_id": "tenant_456", "user_id": "usr_123" }
  }'
```

## 2. Store batch and request IDs

Both single and bulk submits return a `batch_id` and a `requests` array. Persist these so you can poll or reconcile later:

```json theme={null}
{
  "batch_id": "b_123",
  "status": "in-progress",
  "requests": [
    { "npi": "1003158791", "request_id": "r_456", "poll_url": "/api/v1/email/r_456" },
    { "npi": "1234567893", "request_id": "r_457", "poll_url": "/api/v1/email/r_457" }
  ]
}
```

## 3. Handle webhooks

With `mode: "real-time"`, your endpoint receives one POST per NPI as it resolves, so you can update your UI progressively. With `mode: "batch"`, you receive one webhook when the entire batch completes. Your handler should:

1. Parse the JSON payload.
2. Read `custom_data` to find the tenant and user.
3. For each item in `data`, update the record by `npi` using its `status` and email fields.
4. Return a `2xx` status quickly.

```python app.py theme={null}
from flask import Flask, request

app = Flask(__name__)
processed = set()  # use a database table in production

@app.post("/webhooks/dmand")
def dmand_webhook():
    payload = request.get_json()
    tenant_id = (payload.get("custom_data") or {}).get("tenant_id")

    for item in payload["data"]:
        if item["request_id"] in processed:
            continue  # duplicate delivery
        if item["status"] == "completed":
            save_email(tenant_id, item["npi"], item["email"], item["email_status"])
        elif item["status"] in ("not_found", "failed"):
            mark_no_email(tenant_id, item["npi"])
        processed.add(item["request_id"])

    return "", 200
```

Make your handler idempotent: deduplicate by `request_id`. If your endpoint returns a non-2xx response, Dmand retries delivery with backoff. See [Webhooks](/general/webhooks) for the payload format.

## 4. Match results with `custom_data`

`custom_data` accepts any JSON object and comes back unchanged in every webhook and poll response. Use it for your tenant, user, or record IDs instead of matching on names.

## 5. Use `Idempotency-Key` for safe retries

If a submit request fails on the network, retry with the same `Idempotency-Key` header value so the retry does not create a duplicate submission. Use a UUID or a hash of the request body.

## 6. Check credits before large jobs

Before you enqueue a large number of NPIs, verify you have enough credits:

```bash theme={null}
curl https://ext-api.dmand.ai/api/v1/credits \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json theme={null}
{ "balance": 850, "total": 1000, "used": 150 }
```

Credits are held at submit, charged only for emails found, and refunded for `not_found` or `failed` NPIs. Re-requesting the same NPI within `30` days is free (`replayed: true`), so you don't need to deduplicate recent lookups yourself. See [GET /credits](/api-reference/account/credits) and [Credits](/general/credits).

## 7. Keep polling as a fallback

If a webhook does not arrive, poll `GET /api/v1/batch/{batch_id}` or the `poll_url` of each request until no item is `accepted` or `in-progress`.

## Next steps

* Size your traffic with [Volume](/implement-in-product/volume).
* Review [Authentication](/general/authentication) to keep your key server-side.
