Omnichannel API · Version 1.0
Omnichannel API
Connect your own system to Mimin so it can receive customer messages from WhatsApp and reply to them automatically.
Receive
Mimin notifies your system the moment a customer sends a message.
Reply
Send text, images or documents back into the same conversation.
Organise
Create customers, open new chatrooms and look up past conversations.
Base URL
Every endpoint in this guide starts with this address:
https://mimin-api.mimin.io/mimin-backend/api/v1
Before you start
You need three things. If you are missing any of them, ask your Mimin contact.
- A Mimin account — the e-mail and password you use to log in. The API uses the same login.
- A WhatsApp inbox — a WhatsApp Business number already connected to your Mimin account.
- A webhook URL (only if you want to receive messages) — a public
https://address on your server, registered with the Mimin team.
Everything at a glance
| Type | Endpoint | What it does |
|---|---|---|
| POST | /auth/login | Exchange your e-mail and password for an access token. |
| POST | /customer/create | Register a new customer (name and phone number). |
| GET | /customer/get | Get your customers, page by page, with optional search. |
| POST | /message/create | Start a new conversation (chatroom) with a customer. |
| GET | /message/get | Get your conversations (chatrooms), with filters and pagination. |
| POST | /message/send/{messageId} | Send a reply (text and/or media) into an existing chatroom. |
| WEBHOOK | Your webhook URL | Mimin calls you when a customer sends a message. |
The collection contains every request in this guide with placeholder values only. Import it into Postman, run Login with your own account, and the token is filled in for the other requests automatically.
How it works
A conversation between a customer and your business is called a chatroom. Every chatroom has an ID. To reply, you only need two things: a token and that chatroom ID.
- Customer
Sends a WhatsApp message to your business number.
- Mimin
Forwards the message to your webhook URL, together with the chatroom ID.
- Your system
Decides what to answer and calls Send message with that chatroom ID.
- Mimin
Delivers your reply to the customer on WhatsApp.
Quick start: reply to a customer
This walkthrough takes about five minutes and uses Postman, a free tool for sending API requests. No code needed. Once it works here, copy the same requests into your own system using the code samples further down.
-
A customer sends you a message
Using any phone, send a WhatsApp message to your business number — for example
try to test the webhook incoming chat
. -
Find the chatroom ID in your webhook
Within a second or two, Mimin sends the message to your webhook URL. Look at the very end of the data you receive:
message_idis the chatroom ID. Copy it.
The data Mimin sends to your webhook. The highlighted message_idis the chatroom ID. Sample values shown — yours will be different. -
Log in to get your token
In Postman, open Login, type your Mimin e-mail and password in the body, and press Send. The response contains your
token.
The Login request and its response. The highlighted tokenis your access token. Sample values shown — yours will be different. -
Send your reply
Open Send message. In the Params tab, paste the chatroom ID from step 2 as the value of
messageId.
The chatroom ID goes into the messageIdpath variable, which becomes part of the URL. Sample values shown — yours will be different.Then open the Body tab, write your message in
text, and press Send. A200 OKmeans Mimin accepted your message.
The reply text goes in the body. The response confirms the message with status pendingwhile it is delivered. Sample values shown — yours will be different. -
The customer receives it
Check the phone: your reply has arrived in the same WhatsApp chat.
The customer’s message (right) and the reply sent through the API (highlighted). The demo text is Indonesian for “Hello, this is a WhatsApp reply from the API”.
That is the whole loop. The rest of this guide explains each request in detail.
Authentication
Mimin needs to know who is calling. You prove it with an access token — a long string of letters and numbers that you get by logging in.
- Call Login with your e-mail and password.
- Save the
tokenfrom the response. - Send that token in the
Authorizationheader of every other request, with the wordBearerand a space in front of it. - When the token expires, log in again to get a new one.
Authorization: Bearer <your_token>
# Example
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.EXAMPLE-PAYLOAD.EXAMPLE-SIGNATURE
Requests and responses
- Format. You send JSON and you get JSON back. Add the header
Content-Type: application/jsonwhenever your request has a body. - Response shape. Responses generally contain
status,messageanddata, but the exact fields differ per endpoint. Read only the fields you need and ignore the rest, so your integration keeps working when new fields are added. - Phone numbers. International format with no plus sign, spaces or dashes — for example
6281234567890. - IDs. Customers, inboxes, chatrooms and messages each have an ID that looks like
665f1a2b3c4d5e6f7a8b9c0d(24 letters and numbers).
API reference
Every request, one by one. Pick your language in any code sample and the whole page follows.
Login
https://mimin-api.mimin.io/mimin-backend/api/v1/auth/loginExchange your Mimin e-mail and password for an access token. You need this token for every other request, so this is always the first call you make.
No token needed. This is the one endpoint you call without an Authorization header.
Headers
| Header | Value | Why |
|---|---|---|
Content-Type | application/json | Tells the server the body is JSON. |
Request body
| Field | Type | Required? | What it is |
|---|---|---|---|
email | string | Required | The e-mail address of your Mimin account. |
password | string | Required | The password of your Mimin account. |
Example request
curl -X POST "https://mimin-api.mimin.io/mimin-backend/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "you@yourcompany.com",
"password": "your-password"
}'
// Keep your e-mail and password in environment variables, never in the code itself.
const response = await fetch("https://mimin-api.mimin.io/mimin-backend/api/v1/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: process.env.MIMIN_EMAIL,
password: process.env.MIMIN_PASSWORD,
}),
});
const { token } = await response.json();
// Use `token` in the Authorization header of every other request.
import os
import requests
# Keep your e-mail and password in environment variables, never in the code itself.
response = requests.post(
"https://mimin-api.mimin.io/mimin-backend/api/v1/auth/login",
json={
"email": os.environ["MIMIN_EMAIL"],
"password": os.environ["MIMIN_PASSWORD"],
},
)
token = response.json()["token"]
# Use `token` in the Authorization header of every other request.
<?php
// Keep your e-mail and password in environment variables, never in the code itself.
$ch = curl_init("https://mimin-api.mimin.io/mimin-backend/api/v1/auth/login");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"email" => getenv("MIMIN_EMAIL"),
"password" => getenv("MIMIN_PASSWORD"),
]),
]);
$response = curl_exec($ch);
curl_close($ch);
$token = json_decode($response, true)["token"];
// Use $token in the Authorization header of every other request.
Response
You get back your account details and a token. Save the token — it is your key for all other endpoints.
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.EXAMPLE-PAYLOAD.EXAMPLE-SIGNATURE",
"user": {
"_id": "665f1a2b3c4d5e6f7a8b9c05",
"name": "Your Name",
"email": "you@yourcompany.com",
"phone": "6281100000002"
// … more account fields
}
}
Create customer
https://mimin-api.mimin.io/mimin-backend/api/v1/customer/createAdd a new customer to your Mimin account. You will get back a customer ID, which you can later use to find that customer's conversations.
Token required. Send your access token in the Authorization header.
Headers
| Header | Value | Why |
|---|---|---|
Authorization | Bearer <your_token> | The access token you got from Login. |
Content-Type | application/json | Tells the server the body is JSON. |
Request body
| Field | Type | Required? | What it is |
|---|---|---|---|
name | string | Required | The customer's name. |
phone | string | Required | The customer's WhatsApp number in international format without the plus sign, for example 6281234567890. |
Example request
curl -X POST "https://mimin-api.mimin.io/mimin-backend/api/v1/customer/create" \
-H "Authorization: Bearer $MIMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Budi Santoso",
"phone": "6281234567890"
}'
const response = await fetch("https://mimin-api.mimin.io/mimin-backend/api/v1/customer/create", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.MIMIN_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "Budi Santoso",
"phone": "6281234567890"
}),
});
const data = await response.json();
console.log(data);
import os
import requests
response = requests.post(
"https://mimin-api.mimin.io/mimin-backend/api/v1/customer/create",
headers={"Authorization": f"Bearer {os.environ['MIMIN_TOKEN']}"},
json={
"name": "Budi Santoso",
"phone": "6281234567890",
},
)
print(response.json())
<?php
$ch = curl_init("https://mimin-api.mimin.io/mimin-backend/api/v1/customer/create");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("MIMIN_TOKEN"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"name" => "Budi Santoso",
"phone" => "6281234567890",
]),
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Response
Returns the customer that was just created, including its customer ID in the _id field. Keep this ID — use it as the customer filter in List conversations.
List customers
https://mimin-api.mimin.io/mimin-backend/api/v1/customer/getGet the customers saved in your Mimin account. Results come back one page at a time, and you can sort them or search by keyword.
Token required. Send your access token in the Authorization header.
Headers
| Header | Value | Why |
|---|---|---|
Authorization | Bearer <your_token> | The access token you got from Login. |
Query parameters
| Parameter | Type | Required? | What it is |
|---|---|---|---|
order | string | Optional | Which field to sort by, for example _id. |
sort | number | Optional | Sort direction: 1 = ascending (A→Z, oldest first), -1 = descending. |
page | number | Optional | Which page of results you want. Starts at 1. |
limit | number | Optional | How many customers per page, for example 10. |
value | string | Optional | Search keyword, such as a customer's name or phone number. Leave it empty to list everyone. |
Example request
curl -X GET "https://mimin-api.mimin.io/mimin-backend/api/v1/customer/get?order=_id&sort=1&page=1&limit=10&value=" \
-H "Authorization: Bearer $MIMIN_TOKEN"
const params = new URLSearchParams({
"order": "_id",
"sort": "1",
"page": "1",
"limit": "10",
"value": ""
});
const response = await fetch(`https://mimin-api.mimin.io/mimin-backend/api/v1/customer/get?${params}`, {
method: "GET",
headers: {
"Authorization": `Bearer ${process.env.MIMIN_TOKEN}`,
},
});
const data = await response.json();
console.log(data);
import os
import requests
response = requests.get(
"https://mimin-api.mimin.io/mimin-backend/api/v1/customer/get",
headers={"Authorization": f"Bearer {os.environ['MIMIN_TOKEN']}"},
params={
"order": "_id",
"sort": 1,
"page": 1,
"limit": 10,
"value": "",
},
)
print(response.json())
<?php
$query = http_build_query([
"order" => "_id",
"sort" => 1,
"page" => 1,
"limit" => 10,
"value" => "",
]);
$ch = curl_init("https://mimin-api.mimin.io/mimin-backend/api/v1/customer/get" . "?" . $query);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("MIMIN_TOKEN"),
],
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Response
Returns the list of customers that match, plus the total number of records and pages so you can build pagination on your side.
Create chatroom
https://mimin-api.mimin.io/mimin-backend/api/v1/message/createStart a new conversation with a customer on WhatsApp. Use this when you want to open the conversation, rather than waiting for the customer to message first.
Token required. Send your access token in the Authorization header.
Headers
| Header | Value | Why |
|---|---|---|
Authorization | Bearer <your_token> | The access token you got from Login. |
Content-Type | application/json | Tells the server the body is JSON. |
Request body
| Field | Type | Required? | What it is |
|---|---|---|---|
name | string | Required | The customer's name. |
phone | string | Required | The customer's WhatsApp number, international format without +, for example 6281234567890. |
message_inbox | string | Required | The ID of the inbox (your WhatsApp Business number) the chatroom should be created in. |
message_label | array | Optional | Labels to attach to the chatroom. Send an empty list [] if you do not use labels. |
priority | string | Optional | Priority of the chatroom. Can be left as an empty string. |
notes | string | Optional | A free-text note about the chatroom. Can be left as an empty string. |
Example request
curl -X POST "https://mimin-api.mimin.io/mimin-backend/api/v1/message/create" \
-H "Authorization: Bearer $MIMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Budi Santoso",
"phone": "6281234567890",
"priority": "",
"message_label": [],
"message_inbox": "665f1a2b3c4d5e6f7a8b9c02",
"notes": ""
}'
const response = await fetch("https://mimin-api.mimin.io/mimin-backend/api/v1/message/create", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.MIMIN_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "Budi Santoso",
"phone": "6281234567890",
"priority": "",
"message_label": [],
"message_inbox": "665f1a2b3c4d5e6f7a8b9c02",
"notes": ""
}),
});
const data = await response.json();
console.log(data);
import os
import requests
response = requests.post(
"https://mimin-api.mimin.io/mimin-backend/api/v1/message/create",
headers={"Authorization": f"Bearer {os.environ['MIMIN_TOKEN']}"},
json={
"name": "Budi Santoso",
"phone": "6281234567890",
"priority": "",
"message_label": [],
"message_inbox": "665f1a2b3c4d5e6f7a8b9c02",
"notes": "",
},
)
print(response.json())
<?php
$ch = curl_init("https://mimin-api.mimin.io/mimin-backend/api/v1/message/create");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("MIMIN_TOKEN"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"name" => "Budi Santoso",
"phone" => "6281234567890",
"priority" => "",
"message_label" => [],
"message_inbox" => "665f1a2b3c4d5e6f7a8b9c02",
"notes" => "",
]),
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Response
Returns the details of the new chatroom, including its chatroom ID. That ID is the messageId you pass to Send message.
List conversations
https://mimin-api.mimin.io/mimin-backend/api/v1/message/getGet your conversations (chatrooms). You can narrow the list by date, status, agent, customer, inbox or channel, and read it page by page.
Token required. Send your access token in the Authorization header.
Headers
| Header | Value | Why |
|---|---|---|
Authorization | Bearer <your_token> | The access token you got from Login. |
Query parameters
Every filter is optional. Leave out the ones you do not need.
| Parameter | Type | Required? | What it is |
|---|---|---|---|
limit | number | Optional | How many conversations per page, for example 10. |
page | number | Optional | Which page of results you want. Starts at 1. |
sort | number | Optional | Sort by time: 1 = oldest first, -1 = newest first. |
start | date | Optional | Start of a date range, written as YYYY-MM-DD, for example 2026-10-01. |
end | date | Optional | End of a date range, written as YYYY-MM-DD, for example 2026-10-07. |
status | string | Optional | Conversation status, for example pending. |
customer | string | Optional | A customer ID — returns only the conversations of that customer. |
inbox | string | Optional | An inbox ID — returns only conversations on that WhatsApp Business number. |
user | string | Optional | ID of the agent (user) handling the conversation. |
resolved_by | string | Optional | ID of the agent who resolved the conversation. |
conversation_type | string | Optional | The channel, for example whatsapp. |
filter | string | Optional | An additional filter, when one has been agreed with the Mimin team. |
Example request
curl -X GET "https://mimin-api.mimin.io/mimin-backend/api/v1/message/get?limit=10&page=1&sort=-1&customer=665f1a2b3c4d5e6f7a8b9c01" \
-H "Authorization: Bearer $MIMIN_TOKEN"
const params = new URLSearchParams({
"limit": "10",
"page": "1",
"sort": "-1",
"customer": "665f1a2b3c4d5e6f7a8b9c01"
});
const response = await fetch(`https://mimin-api.mimin.io/mimin-backend/api/v1/message/get?${params}`, {
method: "GET",
headers: {
"Authorization": `Bearer ${process.env.MIMIN_TOKEN}`,
},
});
const data = await response.json();
console.log(data);
import os
import requests
response = requests.get(
"https://mimin-api.mimin.io/mimin-backend/api/v1/message/get",
headers={"Authorization": f"Bearer {os.environ['MIMIN_TOKEN']}"},
params={
"limit": 10,
"page": 1,
"sort": -1,
"customer": "665f1a2b3c4d5e6f7a8b9c01",
},
)
print(response.json())
<?php
$query = http_build_query([
"limit" => 10,
"page" => 1,
"sort" => -1,
"customer" => "665f1a2b3c4d5e6f7a8b9c01",
]);
$ch = curl_init("https://mimin-api.mimin.io/mimin-backend/api/v1/message/get" . "?" . $query);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("MIMIN_TOKEN"),
],
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Response
Returns the list of conversations with a summary of the latest message, the status of each conversation, and pagination information.
Send message
https://mimin-api.mimin.io/mimin-backend/api/v1/message/send/{messageId}Send a reply into an existing chatroom. The customer receives it on WhatsApp. You can send plain text, or text with an attachment such as an image or a document.
Token required. Send your access token in the Authorization header.
Headers
| Header | Value | Why |
|---|---|---|
Authorization | Bearer <your_token> | The access token you got from Login. |
Content-Type | application/json | Tells the server the body is JSON. |
URL parameters
| Parameter | Type | Required? | What it is |
|---|---|---|---|
messageId | string | Required | The chatroom ID you are replying in, for example 665f1a2b3c4d5e6f7a8b9c0d. It goes in the URL, not in the body. You get it from the message_id field of a webhook, from Create chatroom or from List conversations. |
Request body
| Field | Type | Required? | What it is |
|---|---|---|---|
text | string | Required | The message to send. |
private | boolean | Required | false = send to the customer. true = save as an internal note that only your team can see. |
context_id | string | Optional | Leave as an empty string "" unless the Mimin team tells you otherwise. |
additional_data | object | Optional | Leave as an empty object {} unless the Mimin team tells you otherwise. |
media | array | Optional | Attachments. Leave it out (or send []) for a text-only message. See the attachment example below. |
Example request
curl -X POST "https://mimin-api.mimin.io/mimin-backend/api/v1/message/send/665f1a2b3c4d5e6f7a8b9c0d" \
-H "Authorization: Bearer $MIMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "Hello, this is a reply from the API",
"private": false,
"context_id": "",
"additional_data": {}
}'
const response = await fetch("https://mimin-api.mimin.io/mimin-backend/api/v1/message/send/665f1a2b3c4d5e6f7a8b9c0d", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.MIMIN_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"text": "Hello, this is a reply from the API",
"private": false,
"context_id": "",
"additional_data": {}
}),
});
const data = await response.json();
console.log(data);
import os
import requests
response = requests.post(
"https://mimin-api.mimin.io/mimin-backend/api/v1/message/send/665f1a2b3c4d5e6f7a8b9c0d",
headers={"Authorization": f"Bearer {os.environ['MIMIN_TOKEN']}"},
json={
"text": "Hello, this is a reply from the API",
"private": False,
"context_id": "",
"additional_data": {},
},
)
print(response.json())
<?php
$ch = curl_init("https://mimin-api.mimin.io/mimin-backend/api/v1/message/send/665f1a2b3c4d5e6f7a8b9c0d");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("MIMIN_TOKEN"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"text" => "Hello, this is a reply from the API",
"private" => false,
"context_id" => "",
"additional_data" => new stdClass(),
]),
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Sending an attachment. Add a media list. Each item needs a type (such as image), a file name, and the file itself as data — a Base64 data URI.
{
"text": "Here is the picture you asked for",
"private": false,
"context_id": "",
"additional_data": {},
"media": [
{
"type": "image",
"name": "sample-image.jpeg",
"data": "data:image/jpeg;base64,<base64_encoded_file>"
}
]
}
Response
Returns the message that was sent, inside a data list.
{
"data": [
{
"_id": "665f1a2b3c4d5e6f7a8b9c03",
"message_id": "665f1a2b3c4d5e6f7a8b9c0d",
"send_message_id": "wamid.EXAMPLEzYxWvUtSrQpOnMlKjIhGfEdCbA9876543210",
"api_type": "waba",
"type": "outgoing",
"status": "pending",
"text": "Hello, this is a reply from the API",
"context_id": null
// … more fields
}
]
}
| Field | What it is |
|---|---|
_id | ID of the message you just sent. |
message_id | ID of the chatroom the message belongs to (the same value you put in the URL). |
send_message_id | The ID WhatsApp gave this message. It starts with wamid. |
type | outgoing — a message from you to the customer. |
status | Delivery status. pending means Mimin accepted the message and is handing it to WhatsApp. |
Webhook
How Mimin tells your system that a customer has written to you.
Incoming message notification
https://your-server.example.com/your-webhook-pathA webhook works the other way round from the endpoints above: instead of you calling Mimin, Mimin calls you. Each time a customer sends a WhatsApp message, Mimin immediately sends the message as a POST request to the webhook URL you registered.
Typical uses: chatbots and auto-replies, saving conversations into your CRM, or alerting your team.
What you receive
{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "6281100000001"
},
"contacts": [
{
"profile": {
"name": "Budi"
},
"wa_id": "6281234567890",
"user_id": "ID.1000000000000001"
}
],
"messages": [
{
"from": "6281234567890",
"from_user_id": "ID.1000000000000001",
"id": "wamid.EXAMPLEaBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789",
"timestamp": "1790854599",
"text": {
"body": "Hi, is my order ready?"
},
"type": "text"
}
]
},
"field": "messages",
"message_id": "665f1a2b3c4d5e6f7a8b9c0d"
}
Fields
| Field | Type | What it is |
|---|---|---|
message_id | string | The chatroom ID. Use it as messageId in Send message to reply. |
field | string | The kind of notification. Always messages for an incoming customer message. |
value.messages[].type | string | The kind of message: text, image, document, audio, video, and so on. |
value.messages[].text.body | string | What the customer wrote. Only present when type is text. |
value.messages[].from | string | The customer's WhatsApp number (same as wa_id). |
value.messages[].id | string | The ID WhatsApp gave this message. It starts with wamid. and is unique per message. |
value.messages[].timestamp | string | When the message was received, as a Unix timestamp in seconds. |
value.messages[].from_user_id | string | The sender's unique ID in Meta's system. |
value.contacts[].profile.name | string | The customer's WhatsApp display name. |
value.contacts[].wa_id | string | The customer's WhatsApp number, international format without +. |
value.contacts[].user_id | string | The sender's unique ID in Meta's system. |
value.metadata.display_phone_number | string | Your WhatsApp Business number that received the message. |
value.messaging_product | string | Always whatsapp. |
Example receiver
A minimal webhook that acknowledges the request and reads the message:
// Node.js + Express
import express from "express";
const app = express();
app.use(express.json());
app.post("/mimin/webhook", (req, res) => {
// 1. Answer straight away so Mimin knows you received it.
res.sendStatus(200);
// 2. Only handle incoming customer messages.
const payload = req.body;
if (payload.field !== "messages") return;
const chatroomId = payload.message_id;
const message = payload.value.messages[0];
if (message.type === "text") {
console.log(`New message in ${chatroomId}: ${message.text.body}`);
// 3. Do your own work here (save it, call Send message, …).
}
});
app.listen(3000);
# Python + Flask
from flask import Flask, request
app = Flask(__name__)
@app.post("/mimin/webhook")
def mimin_webhook():
payload = request.get_json()
# Only handle incoming customer messages.
if payload.get("field") == "messages":
chatroom_id = payload["message_id"]
message = payload["value"]["messages"][0]
if message["type"] == "text":
print(f"New message in {chatroom_id}: {message['text']['body']}")
# Hand the work to a background job here (save it, call Send message, …).
# Answer quickly so Mimin knows you received it.
return "", 200
<?php
// webhook.php
$payload = json_decode(file_get_contents("php://input"), true);
// Answer straight away so Mimin knows you received it.
http_response_code(200);
// Only handle incoming customer messages.
if (($payload["field"] ?? "") === "messages") {
$chatroomId = $payload["message_id"];
$message = $payload["value"]["messages"][0];
if ($message["type"] === "text") {
error_log("New message in " . $chatroomId . ": " . $message["text"]["body"]);
// Do your own work here (save it, call Send message, …).
}
}
Rules for a healthy webhook
- Answer fast. Reply with
200 OKas soon as you receive the request, then do your own processing afterwards. Slow answers can cause timeouts on the Mimin side. - Check
field. Handle the payload as an incoming message only whenfieldismessages. - Expect other notifications. Your URL can also receive status updates about a conversation (see below). Ignore the ones you do not need.
- Be ready for repeats. If the same
value.messages[].idever arrives twice, process it once.
Other notifications you may see
Besides incoming messages, Mimin can notify you when a conversation changes. These have an event field instead of field. The exact content depends on how your webhook was configured:
{
"event": "MESSAGE_UPDATED",
"customer": {
"id": "665f1a2b3c4d5e6f7a8b9c01",
"name": "Budi",
"phone": "6281234567890"
},
"chatbot_active": true,
"message": {
"id": "665f1a2b3c4d5e6f7a8b9c0d",
"message_inbox": {
"id": "665f1a2b3c4d5e6f7a8b9c02",
"name": "Your Inbox",
"type": "waba"
},
"status": "open",
"priority": "low"
// … more fields
}
}
Status codes
Every response comes with a status code that tells you whether the request worked.
| Code | Meaning | What to do |
|---|---|---|
| 200 / 201 | Success. | Nothing — read the response. |
| 400 | The request is not valid, for example the JSON body is malformed. | Check the body and parameters against this guide. |
| 401 | The token is missing, wrong or expired. | Call Login again and retry with the new token. |
| 403 | Your account is not allowed to access this resource. | Ask your Mimin contact to check your permissions. |
| 404 | The endpoint or the item was not found. | Check the URL and the ID you sent. |
| 500 | Something went wrong on the Mimin side. | Try again shortly. If it keeps happening, contact the Mimin technical team. |
Security checklist
Your Mimin login gives access to your customers' conversations. Protect it like you would protect your bank login.
- Call the API from your server only. Never put your e-mail, password or token in a website, a mobile app, or anything a customer can open.
- Keep secrets out of your code. Store them in environment variables or a secrets manager, and never commit them to a repository.
- Use a dedicated account for the integration, with a long, unique password that is not shared with people.
- Do not log secrets. Make sure passwords and tokens never end up in log files, error messages or screenshots.
- Change the password immediately if you think it or a token has leaked, and tell your Mimin contact.
- Handle customer data with care. Names, phone numbers and messages are personal data — store only what you need and follow the privacy rules that apply to you.
Glossary
- Access token
- A temporary key you get from Login. It proves who you are on every request.
- Chatroom (conversation)
- The thread of messages between one customer and your business.
- Chatroom ID
- The ID of a chatroom. Written
messageIdin the Send message URL andmessage_idin webhooks. - Customer ID
- The ID of a customer record, found in the
_idfield. - Inbox
- A channel connected to Mimin — for this API, one WhatsApp Business number. Each inbox has an ID.
- WABA
- WhatsApp Business Account: the official WhatsApp account of your business.
- Webhook
- A URL on your server that Mimin calls when something happens, such as a new customer message.
- wamid
- The ID WhatsApp assigns to each individual message.
Need help?
If something in this guide is unclear or a request does not behave as described, contact the Mimin technical team through your usual Mimin contact, or reach us from the main website.