JSON API reference
One endpoint, five functions, authentication by a single header. This is the interface most integrations use, including our own WordPress plugins, our Zapier app and our Make app. Every statement on this page was measured against the live gateway.
Endpoint
POST https://heb.mesereser.com/Services/JsonServices.aspx?f=FunctionName Header: ApiKey: <your key> Content-Type: application/json; charset=utf-8
The function name is a query string parameter, not a path segment. It is not case sensitive. POST is used for writes, and GET also works.
Authentication
A single HTTP header carries the account key. The key is issued per account, inside Meser10 under account settings. There is no OAuth flow, no token exchange and no refresh, and the key stays valid until the account owner replaces it. Send it in the header, never in the query string.
Reading the response
{ "ErrorCode": 0, "Result": "Call successful" }HTTP 200 is returned for every outcome, including a rejected key. A 200 does not mean the call succeeded. ErrorCode is the only truth, and success is ErrorCode 0. It is serialised as a number on some functions and as a string on others, so normalise before comparing:
const ok = String(body.ErrorCode) === "0";
| Code | Meaning | What the caller should do |
|---|---|---|
0 | Success | Continue |
1 | The key is wrong, missing or revoked | Treat as an authentication failure and stop. Do not retry. |
3 | Application error on our side, or a payload the function could not process at all | Retry once. If it persists, send us the tracking id in Result. |
4 | A parameter is missing or invalid, most often a mailing list that does not exist on this account | Show the user which field or list was named in Result. |
6 | Unknown function name | Fix the f= value, or use it deliberately as a key test. See below. |
Result carries a human readable message, and it is returned in Hebrew on most failures. Map on ErrorCode and supply your own text rather than passing Result through to an international audience.
The five functions
CreateContact add or update a contact on a list ChangeContactStatus move a contact between Active, Unsubscribed and Bounced GetContactStatus read a contact's current status SendSingleSmsMessage one SMS, immediately SendSingleEMailMessage one email, immediately
Any other function name answers ErrorCode 6. The wider platform, meaning campaigns, groups, reporting, attachments and user management, lives on the SOAP service and not here.
CreateContact
Subscribes a contact to a named list, or updates them if they already exist. Thirteen fields are accepted, spelled exactly as below. Note the capitalisation of EMail and PhoneNo.
{
"ContactListName": "Newsletter",
"EMail": "person@example.com",
"PhoneNo": "0501234567",
"FirstName": "Dana",
"LastName": "Levi",
"Address": "12 Herzl St",
"City": "Tel Aviv",
"Zipcode": "6100000",
"CustomField1": "", "CustomField2": "", "CustomField3": "",
"CustomField4": "", "CustomField5": ""
}ContactListName is required, plus at least one of EMail or PhoneNo. Omit fields you are not sending rather than sending empty strings.
The list is not created for you. A list that does not exist on the same account as the key answers ErrorCode 4 with Contact list does not exist: <name>. Build the integration so the user names a list they already have, and show that error as "that list was not found on your account" rather than as a generic failure.
ChangeContactStatus
{ "EMail": "person@example.com", "Status": "Unsubscribed" }Identify the contact by either EMail or PhoneNo. Status is one of Active, Unsubscribed or Bounced, and any other value answers ErrorCode 4 with Unknown value of status.
One behaviour to plan for: an address that is not on the account also answers ErrorCode 0. A successful call is not proof that the contact existed, so do not use this function as an existence check. Use GetContactStatus for that.
GetContactStatus
The only read on the gateway, and the one function that is called differently. The address goes on the query string, not in the JSON body.
GET /Services/JsonServices.aspx?f=GetContactStatus&email=person@example.com
ApiKey: <your key>
{ "ErrorCode": 0, "Result": "Call successful", "Status": "פעיל", "StatusID": 10 }Every JSON body spelling answers email parameter is empty. GET, and POST with an empty body, both work as long as the parameter is on the query string. Only email is supported here. A phone parameter is not read, and returns the same empty parameter error.
Status is Hebrew text meant for display. Branch on StatusID, never on Status.
| StatusID | Status | Meaning |
|---|---|---|
0 | Not Found | The address is not on this account |
10 | פעיל | Active |
30 | פעיל | Active, after being reactivated |
40 | דואר חוזר | Bounced |
50 | הוסר | Unsubscribed |
Two ids mean active. A contact created through the API returns 10, and a contact moved back to Active after a bounce or an unsubscribe returns 30. Treat both as mailable, or you will silently drop reactivated contacts.
SendSingleSmsMessage
{ "ToPhone": "0501234567", "MessageBody": "Your order has shipped", "FromName": "MyShop" }
{ "ErrorCode": 0, "Result": "", "MessageID": 0 }One recipient per call, sent immediately. Hebrew text is sent as Unicode, which shortens a single part message from 160 characters to 70. FromName has to be a sender name or number that is already approved on the account. An alphanumeric sender name is limited to 11 characters, may contain only Latin letters, digits and spaces, and has to include at least one letter, so Hebrew text and digit only values are rejected. This is a mobile network rule, not ours, and it is the same with every provider. Until the name is approved the call returns ErrorCode 4 with The SMS sender identity is not verified.
SendSingleEMailMessage
{
"ToEMail": "person@example.com",
"Subject": "Your receipt",
"Body": "<p>Thank you</p>",
"FromName": "MyShop",
"ReplyToEMail": "orders@myshop.example",
"LinkClickCountType": ""
}ReplyToEMail is required, even though it appears as an empty string in older documentation. Send it and the call succeeds. Omit it and you get a null reference error with code 3. We found that while building our own SMTP plugin.
Note what this function does not accept: a From address, only a display name; no CC or BCC; no attachments; and no more than one recipient. Our WordPress mailer checks for all four before calling, and hands the message back to WordPress when it sees one.
Testing a key without side effects
The gateway validates the key before it resolves the function name. That gives a clean connection test: call a function name that does not exist.
POST /Services/JsonServices.aspx?f=ConnectionTest_probe ErrorCode 6 the key is valid, it reached function lookup ErrorCode 1 the key is not valid
Nothing is created, changed or sent. This is the check our own integrations use behind their test connection button, and we recommend it over creating a throwaway contact.
Set a User-Agent header
The API hosts sit behind Cloudflare with Browser Integrity Check enabled, and the User-Agent header is what that check reads. Two otherwise identical requests can get different answers if the User-Agent differs. The signatures that get blocked are the defaults of HTTP libraries that do not set one of their own, for example Java/1.8.0_241, which answers 403, and Python-urllib/3.x, which answers error 1010. A custom value, a browser string, curl, python-requests and no header at all all pass.
# Python (requests)
requests.post(url, json=payload,
headers={"User-Agent": "myapp/1.0", "ApiKey": key})
// Java
c.setRequestProperty("User-Agent", "myapp/1.0");The value itself does not matter. Any string that is not a default library signature is accepted. The symptom is intermittent, because a system often has two code paths to the same endpoint and only one of them sets the header.
Rate limits and blocking
There is no published request rate limit for normal use. Repeated authentication failures block the calling IP address for several hours. Treat ErrorCode 1 as fatal and stop, rather than retrying with the same key. A retry loop against a bad key takes the caller's IP offline for everyone sharing it.
Known gaps
Stated openly so nobody is surprised mid build. Result messages come back in Hebrew, so map on ErrorCode. Date and time fields are returned without timezone information, so treat them as Israel local time until that is corrected. There is no list reading function on the JSON gateway, so a JSON only integration cannot offer a dropdown of the user's lists and has to ask for the list name. And there are no webhooks here, so event driven integrations poll.
OpenAPI description and Postman collection
The five functions are also published as an OpenAPI 3.1 description and as a ready to run Postman collection. Import them into your tooling, generate a client in your own language, or hand them to an AI agent that will write the integration. They cover this JSON gateway only, not the SOAP service.
- OpenAPI 3.1, JSON. Give this URL to automated tooling.
- The same description in YAML, easier to read.
- Postman collection, six requests with tests.
Run Verify the key first: it creates nothing and sends nothing. The other requests act on the live account, two of them deliver a real SMS and a real email, and one unsubscribes an address, so run them one at a time rather than through the Collection Runner.
The SOAP service
Everything the JSON gateway does not cover is on the SOAP service at https://ns.mesereser.com/Services/Services.asmx?wsdl, which exposes 61 operations across campaigns, sending, attachments, groups, bounce and click reporting, coupons and user management. Group and list names, for example, are read with GetGroupsList, which returns ID and Name pairs and has no JSON equivalent. SOAP authenticates with the same key, inside an oLogin element, plus the numeric user id.
Start integrating
Open a free account to get a key, and see the API overview for the SOAP interface and the IP allowlist.
Sign up freeAPI overviewContact usFrequently asked questions
Which host do I call?
heb.mesereser.com. The login hosts are separate and do not serve the API.
How do I authenticate?
An ApiKey header on every request. There is no OAuth flow and no token exchange.
How do I know a call succeeded?
Only by ErrorCode 0. The HTTP status is 200 for every outcome, including a rejected key, so it tells you nothing.
Why does GetContactStatus say the email parameter is empty?
Because it reads the address from the query string, not from the JSON body. Call it as ?f=GetContactStatus&email=… and it answers.
Can I test a key without creating anything?
Yes. Call a function name that does not exist. ErrorCode 6 means the key is valid, ErrorCode 1 means it is not, and nothing is created or sent.
Is there a sandbox?
No separate sandbox. Use a test list and a test contact on your real account.