General
The mmDial API connects external software to an mmDial account: find and download call recordings, export data, maintain the do-not-call list, and read, create and change leads.
| Address | https://login.mmdial.de/api/index.php |
|---|---|
| Call | The action is always given as action in the query string, for example ?action=dnccheck&number=024192789880. |
| Method | Which values are expected as GET and which as POST is stated with each action. Where it says GET or POST, either works. |
| Response | JSON, encoded as UTF-8. Two exceptions: dncget answers in plain text, and a file request returns the file itself. |
| Authentication | With every call. Without a valid token the API answers 401 Unauthorized – see Token access. |
Not every mmDial interface is listed here. If you need another one, please contact our support – a good deal exists and is simply not written up yet.
About the error responses. These actions are of
different ages and therefore do not answer uniformly: depending on the
action, the status values are ok, found,
exists or notfound, and the error text appears in
msg, message or reason. What an
action sends is stated with that action. When in doubt, rely on the HTTP
status and on status.
Token access
Every call identifies itself with a token. You create the token yourself in the customer administration, under API tokens.
- The token is shown once. Only a checksum is stored – after that it cannot be retrieved again. Write it down right away.
- Every token carries a name so that it is clear later who uses it. We recommend one token per application: then one can be switched off without disturbing the others.
- A token can be restricted to individual actions – one action per line. Empty means all actions are allowed.
- Changes to a token (switching it off, deleting it, changing the restriction) take effect within five minutes at the latest. The API remembers an authentication for that long.
The token in a call
Preferably in the Authorization header:
curl -H "Authorization: Bearer <TOKEN>" \
"https://login.mmdial.de/api/index.php?action=dncget"
Where that is not possible, HTTP basic authentication works as well –
user name xmlrpc_user, password is the token:
curl -u "xmlrpc_user:<TOKEN>" \
"https://login.mmdial.de/api/index.php?action=dncget"
You may append your dialer id to the user name, separated by -
or _, for example xmlrpc_user-1098. This changes
nothing about the check, but the id ends up in the web server's access log and
helps us with troubleshooting. If it does not match the token, the call is
rejected.
Whether the token works
The API answers 401 Unauthorized when the token is unknown,
switched off, or not allowed for this action. On a 401, check both:
the token itself and its action restriction.
The old access method is deprecated. The API used to authenticate with a user account that had admin rights. That method is no longer documented and should no longer be used: a token can be switched off individually and restricted to actions, a user account cannot. Please migrate existing integrations.
authtestGET
The probe action: it answers only when the authentication is valid. No parameters.
curl -H "Authorization: Bearer <TOKEN>" \
"https://login.mmdial.de/api/index.php?action=authtest"
{"status":"ok","login":"token","customer":1098,"time":"2026-10-26 11:04:12"}
| Field | Meaning |
|---|---|
status | always ok – the fact that anything answers here is the information |
login | how you are authenticated: token |
customer | the customer number the token belongs to |
dialerid | only if you appended the id to the user name. Its presence means it arrived and was checked. |
time | the server's time |
A restricted token may do this too.
authtest is exempt from the action restriction –
otherwise you would get a 401 precisely for those tokens where
checking matters most, and would take a valid token for a broken one. For
monitoring, the HTTP status is enough: 200 means the
credentials work.
Fetching filesGET
Recordings and export files are fetched with action=getfile.
The ready-made address is part of the response of whichever action created or
found the file – in the url field. Take it from there instead
of assembling it yourself.
Parameters
| Name | Required | Meaning |
|---|---|---|
type | yes | voices for a recording, temp for an export file |
file | yes | for voices the voiceid from the recording search, for temp the file_id from the export |
Response
The file itself, not JSON. Authentication is required here as well: the address alone does not hand anything out.
curl -H "Authorization: Bearer <TOKEN>" -o recording.mp3 \ "https://login.mmdial.de/api/index.php?action=getfile&type=voices&file=9912837"
Export files are meant to be fetched, not stored – please collect them promptly.
Field names
Three actions work with lead fields:
exportbystatus,
addlead and
editlead. They use the same names.
| Name | Meaning |
|---|---|
title | form of address or title |
phone | phone number. In responses always with the country code in front. |
alt_phone | second phone number |
first_name | first name |
last_name | last name |
company_name | company name |
street | street |
streetnr | house number |
addradd | address addition. In responses the same field is called streetadd. |
plz | postal code |
ort | city |
country | country code, for example DE |
notices | notes, the comment field |
leadid_external | your own lead number |
status | the lead's status (only editlead and responses) |
rotation | yes = the lead is available for dialling again (only editlead and responses) |
xtra_… | your own extra fields, as far as they are set up for the account |
The date of birth. When writing
(addlead, editlead) the field is called
birthdate. The old name bithdate – a typo
from earlier years – stays valid; if you send both, the value of
bithdate is used. In the export the field is called
birthday. The expected format is DD.MM.YYYY.
The export additionally accepts these names, which do not exist when
writing: agentid (agent of the last call),
imported_date (created on), last_edit (last changed),
last_phone (last call). Any other column of the lead table can
also be exported under its own name.
searchvoicebyleadidGET
Finds the recordings belonging to a lead.
Parameters
| Name | Required | Meaning |
|---|---|---|
leadid | yes | the lead number |
Response
| Field | Meaning |
|---|---|
status | ok or error |
msg | only with error, for example no lead found |
voices | list of recordings, most recent first |
recid | number of the recording |
agentid | agent who handled the call |
timestamp | start of the recording |
available | yes if the file can be fetched, otherwise no |
voiceid | number of the file (only with yes) |
url | address to fetch the file (only with yes) |
length | length in seconds (only with yes) |
size | size in bytes (only with yes) |
{
"status": "ok",
"voices": [
{
"recid": "4815162",
"agentid": "1098-204",
"timestamp": "2026-10-20 11:32:07",
"available": "yes",
"voiceid": "9912837",
"url": "https://login.mmdial.de/api/index.php?action=getfile&type=voices&file=9912837",
"length": "184",
"size": "1472000"
}
]
}
About the address. It points at the same host you just
called the API on, and it uses https. Send the same
authentication when fetching the file as with the call itself – the
address alone does not hand out a file. See
Fetching files.
If available is no, the recording has not
finished transferring yet or was deleted after the retention period. The
other values are still there.
searchvoicebyphonenumberGET
Finds recordings by the customer's phone number.
Parameters
| Name | Required | Meaning |
|---|---|---|
phone | yes | the number as it was dialled (national form, with the leading zero) |
date | no | only recordings of that day, YYYY-MM-DD |
timestamp | no | exactly this recording, YYYY-MM-DD HH:MM:SS |
Response
As with searchvoicebyleadid,
and every entry additionally carries leadid – the same number
can appear in several leads.
At most 1000 recordings per call, most recent first. Narrow the range with
date if you expect more.
searchvoicebyagentGET
Finds recordings by agent.
Parameters
| Name | Required | Meaning |
|---|---|---|
agentid | yes | the agent id as it appears in the administration |
date | no | only recordings of that day, YYYY-MM-DD |
timestamp | no | exactly this recording, YYYY-MM-DD HH:MM:SS |
Response
As with
searchvoicebyphonenumber, likewise at most 1000 recordings per
call. For a full day of one agent that is usually not enough – query day
by day in that case.
getfieldsbycampaignGET
Lists the order form fields of a campaign. This tells you the names that
exportbystatus and
editlead expect.
Parameters
| Name | Required | Meaning |
|---|---|---|
campaign_id | yes | the campaign number |
allfields | no | yes also lists the fields for which data has already been stored in this campaign – including fields of forms that have since been replaced. |
Response
{
"fields": [
{ "name": "Kundennummer", "GUID": "41" },
{ "name": "Tarif", "GUID": "41" }
]
}
GUID is the number of the form. A field of the same name can
appear in several forms of one campaign; only name and GUID
together identify it. Entries coming from allfields=yes have no
GUID.
getfieldsbyguidGET
The same for a single form, by its number. Useful when a campaign has several forms and you only need the fields of one.
Parameters
| Name | Required | Meaning |
|---|---|---|
GUID | yes | the number of the form – spelled in capitals |
allfields | no | yes also lists the fields for which data has already been stored under this number |
Response
As with getfieldsbycampaign:
a list fields with name and GUID.
exportbystatusGETPOST
Creates a CSV file with the leads of a campaign and returns the address to fetch it from. You choose the fields yourself.
Parameters
| Name | Required | Meaning |
|---|---|---|
campaign_id | yes | the campaign number |
selectedfields | yes | The fields of the file, separated by commas, in column order. Lead fields with the prefix customerfield_, order form fields with webformfield_<GUID>_. GET or POST. |
list_id | no | only this one list. Without it, all lists of the campaign. |
status | no | only these statuses – repeat the parameter as an array: status[]=NEW&status[]=SALE. Without it, all of them. |
datetype | no | what the time range refers to: last_phone (last call, the default), last_edit (last change) or imported_date (created on) |
fromdate, fromtime | no | start of the range, DD.MM.YYYY and HH:MM:SS. Both together, otherwise neither takes effect. |
todate, totime | no | end of the range, likewise |
encoding | no | character encoding of the file: ISO-8859-1 (the default), Windows-1252 or UTF-8 |
Response
| Field | Meaning |
|---|---|
status | ok or error |
message | only with error, e.g. Keine Kampagne angegeben (no campaign given) or Nichts zu exportieren (nothing to export) |
file_id | id of the generated file |
size | size in bytes |
url | address to fetch it from |
broken_data_count | number of rows that could not be converted into the chosen encoding. Greater than 0 means the file contains a note instead of the data at those places. With encoding=UTF-8 this does not happen. |
curl -H "Authorization: Bearer <TOKEN>" \
"https://login.mmdial.de/api/index.php?action=exportbystatus\
&campaign_id=3000&status[]=SALE&datetype=last_edit\
&fromdate=01.10.2026&fromtime=00:00:00\
&selectedfields=customerfield_leadid_external,customerfield_company_name,customerfield_phone,webformfield_41_Tarif\
&encoding=UTF-8"
{"status":"ok","file_id":"6f1c0b9e4a2d8d31b7a05c9e3d8f7a21",
"size":182344,
"url":"https://login.mmdial.de/api/index.php?action=getfile&type=temp&file=6f1c0b9e4a2d8d31b7a05c9e3d8f7a21",
"broken_data_count":0}
Fetch the file from that address afterwards – again with your token,
and over https:
curl -H "Authorization: Bearer <TOKEN>" -o export.csv \ "https://login.mmdial.de/api/index.php?action=getfile&type=temp&file=<file_id>"
About the file
- Separator
;, fields enclosed in", line endingCRLF. - The first row contains the field names without the prefix.
- Sorted by last call, oldest first.
customerfield_phonecontains the country code and the number in one piece.- Of the order form fields, the latest stored state per form ends up in the file.
- A large export takes time; the API works on it for up to 100 minutes and does not stop if the connection drops. Set your own timeout accordingly.
getcampaignsGET
Lists all campaigns of the account. No parameters.
Response
{
"campaigns": [
{ "id": "3000", "name": "Vertrieb Nord", "callerid": "4924194580649",
"active": "Y", "ki_enabled": "Y" },
{ "id": "3500", "name": "Verkauf", "callerid": "4924194580649",
"active": "N", "ki_enabled": "N" }
]
}
| Field | Meaning |
|---|---|
id | the campaign number you use for all other calls |
name | the name from the administration |
callerid | the campaign's outbound number |
active | Y or N |
ki_enabled | Y if the campaign is dialled by the AI |
getcampaignGET
The settings of one campaign.
Parameters
| Name | Required | Meaning |
|---|---|---|
campaignid | yes | the campaign number. Spelled in lower case here – other actions spell it campaignID. |
Response
{
"status": "ok",
"campaign": {
"name": "Vertrieb Nord",
"callerid": "4924194580649",
"active": "Y",
"ki_enabled": "Y",
"dial_timeout": "30"
}
}
If the campaign does not exist, the answer is
{"status":"notfound"}. dial_timeout is the time in
seconds the dialer lets a call ring.
This action knows further parameters for integrating AI telephony. They belong to the dialer side and are not described here – please ask support if you work on that.
editcampaignGETPOST
Changes a campaign. Only what you send is written.
Parameters
| Name | Method | Meaning |
|---|---|---|
campaignid | GET, required | the campaign number |
name | GET or POST | new name |
callerid | GET or POST | new outbound number |
active | GET or POST | yes switches the campaign on, anything else switches it off |
dial_level | GET or POST | the dial level. It also sets the upper limit for the adaptive level – both receive the same value. |
Response
{ "status": "ok" }
{ "status": "notfound" }
This takes effect on live operation immediately. Switching a campaign on means calls are placed, and the dial level determines how many calls run per agent at the same time. Check your values before you send them.
getcampaignlivedataGET
The live figures of a campaign – meant for your own display next to the user interface.
Parameters
| Name | Required | Meaning |
|---|---|---|
campaignid | yes | the campaign number |
campaign_id | no | the same number once more. Only this parameter fills the campaign field of the response; without it the field stays empty. |
Response
{
"status": "ok",
"data": {
"dial_level": 2.5,
"available_leads": "18342",
"calls_today": "4211",
"average_agents": "12",
"drops": "38",
"dropcount": "0.90",
"dropcount_answers": 3.21,
"campaign": "3000",
"extrastats": {
"calls_hour": "612", "answers_hour": "131",
"drops_hour": "4", "drops_hour_pct": "0.65",
"calls_halfhour": "...", "calls_fivemin": "...", "calls_onemin": "..."
}
}
}
| Field | Meaning |
|---|---|
dial_level | the configured dial level |
available_leads | how many leads are currently dialable |
calls_today | calls today |
average_agents | agents on average over the last minute |
drops | dropped calls today |
dropcount | their share in percent, as the dialer computes it |
dropcount_answers | their share of the answered calls, in percent |
extrastats | the same figures for the last hour, half hour, five minutes and minute |
If there are no figures for the campaign yet today, the answer is
{"status":"notfound"}. The values come from the dialer's live
statistics and are updated once a minute – please do not poll more
often.
getcampaignstatsGET
Two evaluations over a time range: the agents' average wait time and the time spent per status.
Parameters
| Name | Required | Meaning |
|---|---|---|
type | yes | WAITtimeAVG for the average wait time per agent, STATUStime for the total per status. Spelled exactly like that. |
campaign_id | yes | the campaign number. With an underscore here. |
fromdate, todate | no | the time range, as a date or a date with time. Without them: since midnight today. |
agentchosen | no | only these agents, several separated by commas |
only_bigger_zero | no | yes leaves times of 0 out of the calculation |
Response
{ "waittimes": [ { "user": "1098-204", "length_in_sec": "18" },
{ "user": "1098-207", "length_in_sec": "24" } ] }
{ "statustimes": [ { "Status": "PAUSE", "length_in_sec": "3120" },
{ "Status": "READY", "length_in_sec": "8400" } ] }
Times are in seconds, sorted ascending.
If the start lies further back than yesterday, at most one day from that start is evaluated – the end is moved forward accordingly. For longer ranges, query day by day.
dncaddGET
Adds one or more numbers to the account's do-not-call list.
Parameters
| Name | Required | Meaning |
|---|---|---|
number | yes | the phone number. Several separated by commas. |
reason | no | reason for the block. It is recorded with the number and can be seen in the administration later – highly recommended. |
Response
| Field | Meaning |
|---|---|
status | ok, doublet (the number was already listed) or error |
message | only with error |
numbers | the result per number: a list of number (in the spelling the number was stored in) and status |
{
"status": "ok",
"numbers": [
{ "number": "24192789880", "status": "ok" },
{ "number": "4924192789880", "status": "doublet" },
{ "number": "17012345678", "status": "ok" }
]
}
The outer status summarises: ok as soon as at least
one number was newly added, doublet when all of them were already
listed, and error when a real error occurred. On an error,
processing stops at that point; message names the position of the
number (counting from 0), and numbers shows what was stored up to
then. An already listed number is not an error and does not stop the list.
How the number is stored
Before being stored, the number is reduced to its digits, and leading zeros
are dropped: 0241 / 92789880 becomes 24192789880,
while 0049 241 92789880 becomes 4924192789880. These
are therefore different entries.
So always send numbers in the same form in which your leads are stored
– otherwise the number sits in the list without the block taking effect
when dialling. dnccheck applies the same
conversion and shows you what arrived.
dncdelGET
Removes one or more numbers from the do-not-call list.
Parameters
| Name | Required | Meaning |
|---|---|---|
number | yes | the phone number. Several separated by commas. |
reason | no | reason for the release, recorded with the number |
Response
status is ok or error, with
message in the error case. ok means the request was
carried out – including when the number was not in the list at all. Use
dnccheck to find out whether a number is
blocked.
dncgetGET
Returns the whole do-not-call list of the account. No parameters.
Response
Plain text, one number per line, line ending CRLF. Not JSON.
The numbers appear as they were stored – see
how the number is stored, under dncadd.
24192789880 4924192789880 17012345678 ...
dnccheckGET
Looks up a number in the do-not-call list.
Parameters
| Name | Required | Meaning |
|---|---|---|
number | yes | the phone number |
fuzzy | no | yes also searches for partial numbers |
Response
Without fuzzy:
{ "status": "exists" } // the number is blocked
{ "status": "notfound" } // it is not
With fuzzy=yes the action answers without
status and lists every match. It searches for the number itself and
every shorter form of it down to six digits – that way you also find a
blocked area code or a blocked number range:
{ "results": [ "241927", "24192789880" ] }
An empty results list means nothing is blocked.
camp_dncadd, camp_dncdel, camp_dncget, camp_dnccheckGET
Besides the do-not-call list of the whole account there is one per campaign.
These four actions work exactly like
dncadd,
dncdel,
dncget and
dnccheck – same number,
same reason, same fuzzy, same responses. One parameter
is added:
| Name | Required | Meaning |
|---|---|---|
campaignID | yes | the campaign whose list is meant. Capital ID. |
curl -H "Authorization: Bearer <TOKEN>" \ "https://login.mmdial.de/api/index.php?action=camp_dncadd\ &campaignID=3000&number=024192789880&reason=objection+by+email"
The list only takes effect if the campaign uses it.
Whether the campaign's own do-not-call list is checked is a setting of the
campaign itself; without it you can add numbers without anything changing
when dialling. The account-wide list (dncadd) is independent of
that setting.
camp_dncadd does not report the result per
number – the numbers field only exists on
dncadd. If you send several numbers here, processing stops at
one that is already listed.
getleadbyidGET
Reads a lead by its lead number.
Parameters
| Name | Required | Meaning |
|---|---|---|
leadid | yes | the lead number |
getxtratables_fields | no | fields from your extra tables, repeated as an array: getxtratables_fields[]=xtra_table_<table>__<field>. Support can tell you the names. |
Response
| Field | Meaning |
|---|---|
status | found or notfound |
is_archived | true if the lead only exists in the archive. Absent otherwise. |
lead_data | the lead's data, see below |
formdata | the lead's order forms: per form (GUID) the most recently stored state |
xtra_tables_data | only with getxtratables_fields: the requested values, keyed by the requested name |
lead_data contains the field names and in
addition:
lead_id | the lead number |
listid | list the lead belongs to |
agentid | agent of the last call, empty for a call without an agent |
last_contact | last call, in your time zone |
called_count | number of call attempts |
{
"status": "found",
"lead_data": {
"lead_id": "45611659",
"title": "Frau",
"phone": "4924192789880",
"alt_phone": "",
"first_name": "Erika",
"last_name": "Mustermann",
"street": "Theaterstr.",
"streetnr": "70-74",
"streetadd": "",
"plz": "52062",
"ort": "Aachen",
"country": "DE",
"notices": "",
"company_name": "Musterfirma GmbH",
"rotation": "no",
"status": "SALE",
"agentid": "1098-204",
"listid": "202610181",
"last_contact": "2026-10-20 11:32:07",
"called_count": "2",
"leadid_external": "A-55912"
},
"formdata": {
"41": { "Kundennummer": "A-55912", "Tarif": "Komfort" },
"42": { "Zaehlernummer": "12345" }
}
}
Fields that are not set come back as an empty string, not as
null. Your own extra fields appear as
xtra_<name> inside lead_data.
If the lead is not found in the live database, the action continues in the
archive by itself and then sets is_archived.
About formdata. The key is the number of
the form (GUID) as
getfieldsbycampaign reports
it. Per form, only the latest stored state appears there;
older states remain in the history and are not included. If the lead has no
form, formdata is empty.
getleadbyphonenumberGET
Reads leads by phone number. The same number can appear in several leads,
which is why lead_data is a list here.
Parameters
| Name | Required | Meaning |
|---|---|---|
number | yes | the phone number. The spelling is converted automatically. |
archive | no | yes also searches the archive |
Response
{
"status": "found",
"lead_data": [
{ "lead_id": "45611659", "phone": "4924192789880", "...": "..." },
{ "lead_id": "39811204", "phone": "4924192789880", "...": "...",
"archived": true }
],
"formdata": [
{ "41": { "Kundennummer": "A-55912", "Tarif": "Komfort" } }
]
}
The fields of each entry are the same as with
getleadbyid, without
called_count. Entries from the archive carry
archived: true. If nothing is found, the answer is
{"status":"notfound"}.
The form data appears twice. Once with each lead itself, as
formdata inside its entry in lead_data – only
there can you see which state belongs to which lead. And once, as it always has,
as a flat list formdata next to lead_data. If you find
several leads, use the value attached to the lead.
getleadbyexternalidGET
Reads leads by your own lead number
(leadid_external).
In the old documentation this action was called
getleadbypexternalid, with one p too many. The
correct name is getleadbyexternalid.
Parameters
| Name | Required | Meaning |
|---|---|---|
leadid | yes | your own lead number |
archive | no | yes also searches the archive |
Response
As with
getleadbyphonenumber: lead_data is a list, because
your number can appear in several leads – the API does not require it to
be unique.
getleadbyextrafieldGET
Reads leads by one of your extra fields.
Parameters
| Name | Required | Meaning |
|---|---|---|
fieldname | yes | the name of the extra field – without the xtra_ prefix |
fieldvalue | yes | the value to look for. Matching is exact, not partial. |
archive | no | yes also searches the archive |
Response
As with
getleadbyphonenumber: status and
lead_data as a list.
getleadbycustomdataGET
The search with several criteria at once. Everything you send must match.
Parameters
| Name | Required | Meaning |
|---|---|---|
extleadid | at least one criterion | your own lead number |
number | the phone number; the spelling is converted automatically | |
extra_fieldname and extra_fieldvalue | one extra field and its value, the name without xtra_ | |
listid | one list, or several as an array: listid[]=202610181&listid[]=202610182. Several count as "any one of these". | |
campaignID | all lists of this campaign | |
archive | yes also searches the archive |
Response
As with
getleadbyphonenumber. If you send no criterion at all, the
answer is {"status":"noarguments"} – so the action does not
accidentally hand out every lead.
curl -H "Authorization: Bearer <TOKEN>" \ "https://login.mmdial.de/api/index.php?action=getleadbycustomdata\ &campaignID=3000&extra_fieldname=Vertragsnummer&extra_fieldvalue=V-4711"
addlistGET
Creates a new list in a campaign.
Parameters
| Name | Required | Meaning |
|---|---|---|
campaignID | yes | the campaign number. Mind the spelling with capital ID. |
description | yes | name of the list |
Response
| Field | Meaning |
|---|---|
status | ok or error |
listid | number of the new list (only with ok) |
reason | reason for the error (only with error) |
{ "status": "ok", "listid": "202610261" }
The new list is switched off. It is created
inactive so that dialling does not start while you are still
adding leads. You can switch it on in the administration or with
editlist. The list number is made up of
the date and a counter.
getlistsGET
Lists the lists – all of them, or those of one campaign.
Parameters
| Name | Required | Meaning |
|---|---|---|
campaignID | no | only the lists of this campaign. Without it, all lists of the account. |
Response
{
"lists": [
{ "listid": "202610261", "name": "Import 26.10.",
"campaignID": "3000", "status": "inactive" }
]
}
status is active or inactive: a
switched-off list is not dialled.
editlistGETPOST
Changes a list – and switches it on. This is the step after
addlist: a newly created list is switched
off.
Parameters
| Name | Method | Meaning |
|---|---|---|
listid | GET, required | the list number |
description | GET or POST | new name of the list |
campaignID | GET or POST | moves the list into another campaign |
active | GET or POST | yes switches the list on, anything else switches it off |
Response
{ "status": "ok" }
{ "status": "notfound" }
curl -H "Authorization: Bearer <TOKEN>" \ "https://login.mmdial.de/api/index.php?action=editlist\ &listid=202610261&active=yes"
addleadPOST
Creates a lead – one per call. You switch the checks for duplicates and invalid numbers on yourself.
Parameters
| Name | Required | Meaning |
|---|---|---|
campaign_id | yes | the campaign number |
list_id | yes | the list the lead goes into |
country_phone_code | yes | country code of the phone number, for example 49. 00 means the number brings its own country code. |
leaddata | yes | the lead's data as an array: leaddata[first_name], leaddata[phone] and so on. Allowed are the field names without status and rotation, plus your xtra_ fields. |
invalidcheck | no | yes rejects numbers that have been found invalid elsewhere |
doubletcheck_global | no | yes rejects the lead if the number already exists anywhere in the account |
doubletcheck_campaign | no | yes checks within this campaign only. Takes effect only when doubletcheck_global is not yes. |
doubletcheck_search_archived | no | no leaves the archive out of the duplicate check. The default is to include it. |
importstatus | no | status of the new lead. Default NEW. |
importfilter | no | number of an import filter set up in the administration, applied to the lead |
Response
{
"lead_stats": {
"success": 1,
"error": 0,
"sqlerror": 0,
"formaterror": 0,
"doubleterror": 0,
"invaliderror": 0,
"formaterror_types": []
},
"the_new_leadid": "45611660"
}
| Field | Meaning |
|---|---|
success | 1 if the lead was created |
error | 1 if it was not. The counters below say why. |
sqlerror | writing to the database failed |
formaterror | a value has the wrong format; formaterror_types names the field or the kind of error |
doubleterror | duplicate, according to the check you selected |
invaliderror | the number is known to be invalid |
filtererror, filtered | only with importfilter: the filter did not run, or it rejected the lead |
the_new_leadid | lead number of the new lead, or false |
curl -H "Authorization: Bearer <TOKEN>" \ -d "campaign_id=3000" -d "list_id=202610261" \ -d "country_phone_code=49" \ -d "doubletcheck_campaign=yes" \ -d "leaddata[company_name]=Musterfirma GmbH" \ -d "leaddata[first_name]=Erika" \ -d "leaddata[last_name]=Mustermann" \ -d "leaddata[phone]=024192789880" \ -d "leaddata[plz]=52062" \ -d "leaddata[ort]=Aachen" \ -d "leaddata[leadid_external]=A-55912" \ "https://login.mmdial.de/api/index.php?action=addlead"
addlead cannot write order form fields.
Create the lead and send the form fields afterwards with
editlead.
editleadGETPOST
Changes an existing lead. Only what you send is written; everything else stays as it is.
Parameters
| Name | Method | Meaning |
|---|---|---|
leadid | GET, required | the lead number |
campaign_id | GET, required | the campaign number |
leaddata | POST | the fields to change, here including status and rotation. Extra fields as leaddata[extrafields][<name>]. |
formfields | POST | order form fields, named as formfields[webform_<GUID>_<fieldname>]. The names come from getfieldsbycampaign. |
GUID | GET | the form the new state is stored under – required as soon as you send formfields |
Either leaddata or formfields must be present,
otherwise the action answers with an error.
Response
{ "status": "ok" }
{ "status": "error", "reason": "Fehlende Felder" }
What to keep in mind
- Form data is not overwritten. Every call with
formfieldscreates a new state; the old one stays in the history. So send all fields of the form, not only the changed one – otherwise the new state contains only that field. - Empty values are allowed.
leaddata[phone]=clears the phone number. Simply leave out a field that should stay as it is. rotation=yesmakes the lead dialable again,notakes it out.- An archived lead is changed as well.
- Changing the
statuscan trigger the notifications that are set up for the campaign – exactly as when an agent sets the status.
curl -H "Authorization: Bearer <TOKEN>" \ -d "leaddata[status]=SALE" \ -d "leaddata[notices]=Contract received" \ -d "formfields[webform_41_Tarif]=Komfort" \ -d "formfields[webform_41_Kundennummer]=A-55912" \ "https://login.mmdial.de/api/index.php?action=editlead\ &leadid=45611659&campaign_id=3000&GUID=41"
getcallbacksbyleadidGET
Lists the callbacks of a lead.
Parameters
| Name | Required | Meaning |
|---|---|---|
leadid | yes | the lead number |
Response
{
"callbacks": [
{
"id": "88312",
"user": "1098-204",
"global_callback": false,
"status": "aktiv",
"created": "2026-10-20 11:35:02",
"set_time": "2026-10-27 09:00:00",
"campaign_id": "3000",
"comments": "Better call in the morning"
}
]
}
| Field | Meaning |
|---|---|
id | number of the callback |
user | the agent who arranged it |
global_callback | false if only this agent receives the callback, otherwise true |
status | aktiv (active), bearbeitet (handled), tot (dead) or unknown – these values are in German |
created | when it was arranged |
set_time | when the call is due |
comments | the agent's note |
If there is none, callbacks is empty. This action has no
status field of its own.
addcallbackGETPOST
Arranges a callback for a lead.
Parameters
| Name | Method | Meaning |
|---|---|---|
lead_id | GET, required | the lead number. With an underscore here. |
callback | POST, required | when the call is due: YYYY-MM-DD HH:MM:SS |
user | POST | the agent the callback belongs to |
recipient | POST | user: only the named agent receives the callback. ANYONE: every agent of the campaign. The former value USERONLY still works and means the same as user. |
hinweise | POST | the note for the callback |
status | POST | the status the lead should get at the time of the callback. Without it, CALLBK. |
Response
{ "status": "ok" }
{ "status": "error" }
What happens
- An existing active callback of this lead is set to handled – there is only ever one active callback.
- The lead gets the status
CBHOLD. The dialer therefore stops calling it until the callback is due. - The operation is recorded in the administration log.
curl -H "Authorization: Bearer <TOKEN>" \ -d "callback=2026-10-27 09:00:00" \ -d "user=1098-204" \ -d "recipient=user" \ -d "hinweise=Better call in the morning" \ "https://login.mmdial.de/api/index.php?action=addcallback&lead_id=45611659"