Docs
Get a funnel running on your Instagram, then send every lead it finds to your own server or CRM through a signed webhook.
Quickstart
Connect Instagram, pick a niche, go live. You do every step yourself in the dashboard, with no code.
Before you start
- An Instagram professional account, Business or Creator. A personal account can't be connected. You don't need a Facebook Page.
- An invite. Instagram connection is in early access, by invite: ask for yours.
Steps
- Sign up. Create your account with the email the invite came to.
- Connect Instagram. In Settings, open Channels and log in with Instagram. Allow Mastak to see your basic profile and to read and answer your comments and messages.
- Pick your niche. Choose your kind of business and its playbook: how the chat opens, what it asks and when it hands the buyer to you.
- Fill in your business card. Prices, address, opening hours and the answers you give every day. Add PDFs, links or text to the knowledge base. The AI answers only from what is here.
- Turn the funnel on. Choose the posts and the code word. A comment with that word opens a private DM and the AI takes it from there.
- Test it in the simulator. Play the buyer: push on price, ask for a person, ask something the card doesn't cover. Adjust, then let real buyers in.
Want leads in your own tools too? Add a webhook (below) or connect Google Sheets in Settings, Integrations.
Webhooks
Mastak sends a POST with a JSON body to your HTTPS address each time a lead changes. Point it at your own endpoint or your CRM.
Set it up
- Add the address. In Settings, open Integrations, add a webhook and paste your URL. It has to start with
https://. - Copy the secret. Mastak creates a signing secret and shows it once. Store it on your server, for example as
MASTAK_WEBHOOK_SECRET. - Check the connection. The check sends a test event named
webhook.test, signed the same way, so you can see it arrive.
Headers
| Header | What it holds |
|---|---|
Content-Type | application/json |
X-Mastak-Timestamp | When the request was signed, in Unix seconds. |
X-Mastak-Signature | sha256= followed by the hex HMAC SHA-256 of <timestamp>.<body>, keyed with your secret. |
X-Mastak-Event-Id | The event's id. A retry carries the same id, so you can drop duplicates. |
Events
| Event | Sent when |
|---|---|
lead.created | A conversation becomes a lead. |
stage.changed | The lead moves to another stage. stage holds the new one. |
lead.qualified | The lead reaches the qualified stage. |
lead.ready | Beta, requires lead scoring to be enabled for your workspace. The buyer is ready for a person and the chat is handed to you. |
Payload
An example lead.qualified body. The values are made up.
{
"event": "lead.qualified",
"eventId": "3f6c1e2a-8b4d-4c55-9a1e-2d7b0c9e4f10",
"timestamp": "2026-11-07T15:42:10.000Z",
"conversationId": "8d0e5b7c-2f1a-4e6b-9c3d-5a4f7e1b2c90",
"channel": "instagram",
"niche": "med-spa",
"stage": "qualified",
"contact": {
"peerId": "17841400000000000",
"username": "jess.moves",
"name": "Jess",
"phone": null,
"email": "[email protected]"
},
"qualification": {
"service": "Lip filler",
"when": "Next week"
},
"summary": "First visit. Asked about price and downtime. Prefers Thursday after 5 pm.",
"leadStatus": "qualified",
"temperature": "warm",
"handoffReason": null,
"card": {
"need": "Lip filler, first time",
"budget": "Around the listed price",
"timeline": "Next week",
"objections": ["Worried about swelling"],
"manualSteps": ["Confirm Thursday 5:30 pm"],
"buyerType": "b2c",
"readyLead": false,
"contact": "[email protected]"
}
}
| Field | Meaning |
|---|---|
event | One of the four events above. |
eventId | Same value as the X-Mastak-Event-Id header. |
timestamp | When the event happened, ISO 8601. |
conversationId | The chat this lead came from. |
channel | Where the chat runs, instagram. |
niche | The playbook's niche, or null. |
stage | The stage the lead is in now. |
contact | peerId (the buyer's Instagram id), username, name, phone, email. Anything the buyer hasn't shared is null or missing. |
qualification | The answers the funnel collected, as text keyed by question. |
summary | A short summary of the conversation. |
leadStatus | new, engaged, qualified, ready, handed_off, won or lost. |
temperature | cold, warm or hot. |
handoffReason | Why the chat went to a person, or null. |
card | The lead card: need, budget, timeline, objections, manualSteps (what you still do by hand), buyerType, readyLead, contact. |
Optional fields can be missing. Ignore fields you don't recognise: new ones may be added.
Delivery and retries
- Answer with any 2xx status within 10 seconds and the delivery counts.
- On a timeout, a 5xx, a 408 or a 429, Mastak tries again after 1, 2, 4 and 8 minutes, five attempts in all.
- Any other 4xx, a 401 for a bad signature included, stops the retries for that event.
Check the signature
Do this before you trust a request:
- Check the time. Reject the request if
X-Mastak-Timestampis more than 300 seconds away from your clock, in either direction. - Rebuild the signature. HMAC SHA-256 over the timestamp, a dot and the raw body, keyed with your secret, as hex, with
sha256=in front. - Compare safely. Compare it with
X-Mastak-Signaturein constant time. Use the raw body as received: parsing the JSON and printing it again changes the bytes.
Node
// Node 18 or newer, no packages needed
const http = require("node:http");
const crypto = require("node:crypto");
const SECRET = process.env.MASTAK_WEBHOOK_SECRET;
const seen = new Set(); // keep event ids in your database in production
function verify(rawBody, timestamp, signature) {
const now = Math.floor(Date.now() / 1000);
if (!/^\d+$/.test(timestamp || "")) return false;
if (Math.abs(now - Number(timestamp)) > 300) return false;
const expected = "sha256=" + crypto
.createHmac("sha256", SECRET)
.update(timestamp + "." + rawBody)
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signature || "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
http.createServer((req, res) => {
let raw = "";
req.setEncoding("utf8");
req.on("data", (chunk) => (raw += chunk));
req.on("end", () => {
const ok = verify(
raw,
req.headers["x-mastak-timestamp"],
req.headers["x-mastak-signature"]
);
if (!ok) return res.writeHead(401).end();
const id = req.headers["x-mastak-event-id"];
if (id && seen.has(id)) return res.writeHead(200).end(); // a retry
if (id) seen.add(id);
const event = JSON.parse(raw);
console.log(event.event, event.contact.username, event.leadStatus);
res.writeHead(200).end();
});
}).listen(3000);
Python
# Python 3, standard library only
import hashlib
import hmac
import os
import time
SECRET = os.environ["MASTAK_WEBHOOK_SECRET"].encode()
def verify(raw_body: bytes, timestamp: str, signature: str) -> bool:
"""raw_body: the request body exactly as received, before JSON parsing."""
if not timestamp or not timestamp.isdigit():
return False
if abs(time.time() - int(timestamp)) > 300:
return False
signed = timestamp.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(SECRET, signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature or "")
# Flask example:
# ok = verify(request.get_data(),
# request.headers.get("X-Mastak-Timestamp", ""),
# request.headers.get("X-Mastak-Signature", ""))
Instagram rules
Mastak works through Instagram's official messaging API and keeps to the limits Instagram sets for every app that uses it.
- One private reply per comment. A comment can get one private reply, sent within 7 days of the comment.
- 24 hours to answer in Direct. After a buyer's last message you have 24 hours to reply. The dashboard shows how much of the window is left.
- The buyer writes first. A business can message someone only after that person comments or writes to it.
Instagram is a trademark of Meta. Mastak is not affiliated with or endorsed by Meta.
Questions
Is there a public API or SDK?
Not today. Leads leave Mastak through the signed webhook and the Google Sheets integration. An MCP server is on the way: see the MCP page.
Can I send leads to another tool?
Yes, if it accepts an incoming webhook. Paste that address as your webhook URL. Each event arrives as JSON with the fields listed above.
My endpoint was down. Did I lose leads?
Mastak retries a failed delivery after 1, 2, 4 and 8 minutes. Every chat also stays in the dashboard, so nothing is lost from your side even if all five attempts fail.
Why didn't a comment get a DM?
Check the code word on that post first. Then the Instagram rules: one private reply per comment, sent within 7 days of the comment.
Do I need a Facebook Page?
No. You need an Instagram professional account, Business or Creator, and you log in with Instagram.
Who do I ask when something here doesn't work?
Write to [email protected] with the event id or the time of the request.