| Base URL | https://app.dmhandled.com/api/v1/ |
|---|---|
| Authentication | Authorization: Bearer dmh_cust_… — a key from Settings → API & MCP |
| Format | JSON in (Content-Type: application/json), JSON out |
| Errors | {"error": {"code": "…", "message": "…"}} with a 4xx status:
400 invalid · 401 no or wrong key · 402 not on your plan · 404 not found · 415 send JSON · 429 slow down |
| Limits | 120 writes a minute and 3,000 an hour per key; a 429 carries Retry-After |
curl https://app.dmhandled.com/api/v1/whoami \
-H "Authorization: Bearer $DMH_KEY"A customer is identified by your own id — the {ref} in the path. A consent is
identified by purpose + given_at + source: send it again with
withdrawn_at to withdraw it.
curl -X PUT https://app.dmhandled.com/api/v1/contacts/1042 \
-H "Authorization: Bearer $DMH_KEY" -H "Content-Type: application/json" \
-d '{
"name": "Ana García",
"email": "ana@example.com",
"phone": "+34600111222",
"lang": "es",
"email_confirmed": false,
"phone_confirmed": false,
"consents": [{
"purpose": "reply_whatsapp",
"granted": true,
"text": "I agree that Flamingo Real Estate may reply to my enquiry on WhatsApp.",
"lang": "en",
"given_at": "2026-09-28T10:00:00Z",
"source": "website_form"
}]
}'Returns the customer (201 when new, 200 when updated). Fields you leave out are unchanged; the consent fields marked * are required.
| Field | Meaning |
|---|---|
phone | E.164, e.g. +34600111222 |
dmh_id | For a customer first met in the chat ("ref": null in an event):
their dmh_id attaches your {ref} to that contact |
email_confirmed, phone_confirmed | true when your system has already proven the address or number is theirs. Otherwise the chat checks it before using it. |
consents[].purpose * | reply_email · reply_whatsapp · future_email · future_whatsapp |
consents[].text * | The exact words the person agreed to |
consents[].given_at * | ISO 8601 date-time |
consents[].source * | Where it was given, e.g. website_form |
consents[].withdrawn_at, withdrawn_by | Set when it was withdrawn |
An active reply_whatsapp or future_whatsapp consent with a phone number lets
your team send that person an approved WhatsApp template after the 24-hour window.
{
"ref": "1042", "dmh_id": 311, "name": "Ana García",
"email": "ana@example.com", "email_confirmed": true,
"phone": "+34600111222", "phone_confirmed": false, "lang": "es",
"created_at": "…", "updated_at": "…",
"consents": [{"purpose": "reply_whatsapp", "granted": true, "text": "…", "lang": "en",
"given_at": "2026-09-28T10:00:00+00:00", "source": "website_form",
"withdrawn_at": null, "withdrawn_by": null, "origin": "api", "active": true}]
}origin is api for what you sent and chat for what the chat
collected. A person first met in the chat has "ref": null and a dmh_id.
Erases the customer and their consents. Returns 204.
After a visitor sends your form, your server asks for a pass and puts it on that visitor's page. The chat then knows who it is talking to and can offer back the details you hold. A pass lasts 30 days.
curl -X POST https://app.dmhandled.com/api/v1/contacts/1042/identity \
-H "Authorization: Bearer $DMH_KEY"
# → {"token": "eyJ…", "expires_at": "2026-10-28T10:00:00+00:00"}Put it on the page before the chat script loads, for that visitor only:
<script>
window.DMHandled = Object.assign(window.DMHandled || {}, { identity: "eyJ…" });
</script>Keep your API key on your server; never put it in a web page.
Push what the assistant answers from, by your own id, whenever it changes.
# add or update one piece
curl -X PUT https://app.dmhandled.com/api/v1/knowledge/listing-88 \
-H "Authorization: Bearer $DMH_KEY" -H "Content-Type: application/json" \
-d '{"heading": "Villa Mar", "text": "Villa Mar has three bedrooms, a sea view and a heated pool. Price: 2.1 million euros.", "url": "https://example.com/villa-mar"}'
# remove it
curl -X DELETE https://app.dmhandled.com/api/v1/knowledge/listing-88 -H "Authorization: Bearer $DMH_KEY"
# up to 50 at once
curl -X POST https://app.dmhandled.com/api/v1/knowledge/bulk \
-H "Authorization: Bearer $DMH_KEY" -H "Content-Type: application/json" \
-d '{"items": [
{"external_ref": "listing-88", "heading": "Villa Mar", "text": "Villa Mar has three bedrooms and a sea view."},
{"external_ref": "listing-89", "heading": "Casa Sol", "text": "Casa Sol has four bedrooms and a private pool."}]}'
# what is there
curl "https://app.dmhandled.com/api/v1/knowledge?external_ref=listing-88" -H "Authorization: Bearer $DMH_KEY"Text is 15 to 8,000 characters per piece; an account holds up to 5,000 pieces.
The same events the webhooks deliver, in order — to catch up, or instead of webhooks.
curl "https://app.dmhandled.com/api/v1/events?after=0&limit=100" -H "Authorization: Bearer $DMH_KEY"
# → {"events": [{"id": 57, "event": "consent.granted", "at": "…", "data": {…}}, …],
# "next_after": 57, "more": false}Keep the last id you processed and pass it as after next time. At most 200 per page.
# register (the secret is shown ONCE — store it)
curl -X POST https://app.dmhandled.com/api/v1/webhooks \
-H "Authorization: Bearer $DMH_KEY" -H "Content-Type: application/json" \
-d '{"url": "https://example.com/dmh/webhook", "events": ["contact.confirmed", "consent.granted", "consent.withdrawn", "handover.created"]}'
curl https://app.dmhandled.com/api/v1/webhooks -H "Authorization: Bearer $DMH_KEY"
curl -X DELETE https://app.dmhandled.com/api/v1/webhooks/12 -H "Authorization: Bearer $DMH_KEY"Leave events out to receive everything. Up to 10 endpoints per account; https only.
How to verify what you receive: Webhooks.