Developers

Your Instagram contacts, in your own code

A small, predictable REST API: read your contacts, tag them from your store or CRM, send a DM when something happens, record the sales your DMs bring in, and check which automations are live.

Included on every plan · JSON over HTTPS

REST API · API key in Settings → API
  • GET/api/v1/contacts?tag=vip
    List contacts, filtered by tag
  • POST/api/v1/contacts/:id/tags
    Add or remove tags
  • POST/api/v1/contacts/:id/send
    Send a DM inside the 24-hour window
POST /api/v1/contacts/:id/tags
{ "tags": ["vip"], "mode": "add" }
What you can build

Connect MuChat to the rest of your stack

Sync your list

Page through every contact, or just the ones with a tag, into your CRM or spreadsheet.

Tag from anywhere

Mark buyers as “customer” when an order lands, so your flows and segments treat them differently.

Send a DM on an event

Confirm a booking or a shipment in the DM thread they already started — inside the 24-hour window.

Keys you control

Read-only or read-write keys, named for what uses them, and revocable in one click.

Quickstart

Your first call in two minutes

  1. 1. Create a key

    In MuChat, open Settings → API, name the key after what will use it, and choose its scopes: read to list, write to tag and send. The token starts with mu_live_ and is shown once — we only keep a hash, so store it somewhere safe. You can have up to 10 active keys.

  2. 2. Call the API

    Send the token as a bearer token. Every key belongs to one MuChat account and only ever sees that account’s data.

    curl https://muchat.app/api/v1/contacts?tag=vip&limit=10 \
      -H "Authorization: Bearer mu_live_…"
  3. 3. Page through results

    List responses return nextCursor. Pass it back as cursor until it comes back null.

Reference

Endpoints

Base URL https://muchat.app/api/v1. Requests and responses are JSON; timestamps are ISO 8601 in UTC.

GET/contactsscope: read

List contacts in a stable order, optionally only those with a tag.

tag
Only contacts carrying this tag (exact name).
limit
1–100, default 25.
cursor
The nextCursor from the previous page.
Response
{
  "data": [ { "id": "ck…", "username": "maya.jewels", "tags": ["vip"], "canMessage": true, … } ],
  "nextCursor": "ck…"   // null on the last page
}
POST/contacts/:id/tagsscope: write

Add tags to a contact, or remove them.

Request body
{ "tags": ["vip", "wholesale"], "mode": "add" }   // mode: "add" (default) or "remove"
Response
{ "data": { "id": "ck…", "tags": ["vip", "wholesale"], … } }
  • 1–30 tags per request, each up to 60 characters.
  • A contact keeps at most 30 tags; extra ones are dropped when adding.
POST/contacts/:id/sendscope: write

Send a text DM to one contact from your connected Instagram account.

Request body
{ "text": "Your order has shipped 📦" }   // 1–1000 characters
Response
{ "data": { "id": "msg…", "status": "sent", "externalId": "…", "createdAt": "2026-09-24T15:02:11.000Z" } }
  • Instagram only lets businesses message someone within 24 hours of their last message. Outside it you get a 409 — check canMessage on the contact first.
  • Text only; buttons and links are sent by automations.
POST/conversionsscope: write

Record a conversion — a purchase, booking or sign-up — for one of your conversion events.

Request body
{ "key": "purchase", "contactId": "ck…", "value": 1999 }   // value in minor units: 1999 = $19.99
Response
{ "data": { "id": "cv…", "eventKey": "purchase", "contactId": "ck…", "automationId": "ck…", "value": 1999, "currency": "USD", "source": "api", … } }   // 201
  • key is the event’s key from Settings → Automation → Conversion events. An unknown key is a 404; an archived event is a 410.
  • Identify the person with contactId, or igsid if you only know their Instagram-scoped id. Both are optional.
  • value is a whole number of minor units in the event’s currency; leave it out to use the event’s default. Decimals and negatives are rejected.
  • Pass automationId to credit an automation yourself. Otherwise the conversion is credited to the last automation run or sequence that touched the contact in the 7 days before.
GET/conversionsscope: read

List recorded conversions, newest first.

key
Only this conversion event.
contactId
Only this contact.
limit
1–100, default 25.
cursor
The nextCursor from the previous page.
Response
{ "data": [ { "id": "cv…", "eventKey": "purchase", "value": 1999, "currency": "USD", … } ], "nextCursor": null }
GET/automationsscope: read

List your automations, most recently updated first.

status
DRAFT, LIVE or STOPPED.
limit
1–100, default 50.
Response
{ "data": [ { "id": "ck…", "name": "Auto-DM links from comments", "status": "LIVE", "builder": "flow", "triggerType": "COMMENT", "publishedAt": "…", … } ] }
Webhooks

Let MuChat call you

Add an endpoint under Settings → Webhooks and MuChat POSTs a signed JSON event the moment something happens — ready for your own server, Zapier, Make or anything else that accepts a webhook.

contact.created
The first time someone DMs, comments or taps a button — before any automation runs.
data: contactId, igsid, username, name, channelId, tags
conversion.recorded
A conversion is recorded — from the API, a flow step or a contact’s profile.
data: conversionId, eventKey, eventName, value, currency, contactId, automationId, sequenceId, source
automation.run
An automation starts for a contact.
data: runId, automationId, automationName, status, contactId, trigger
inbox.message
Every inbound message that lands in the inbox, including paused conversations.
data: messageId, contactId, igsid, username, direction, kind, text
  • Up to 10 endpoints per workspace, each subscribed to the events you pick. A Send test button checks your endpoint.
  • HTTPS only. Private, loopback and internal addresses are refused, and redirects aren’t followed.
  • Answer with any 2xx within 10 seconds. Failed deliveries are retried up to 5 times in all, backing off from 1 minute to 2 hours.
  • After 10 failed attempts in a row the endpoint is switched off; the delivery log shows what went wrong.
Every delivery
POST https://your-endpoint.example
X-MuChat-Event: conversion.recorded
X-MuChat-Delivery: <delivery id>
X-MuChat-Version: 1
X-MuChat-Signature: sha256=<hex HMAC of the raw body>

{
  "id": "…",
  "event": "conversion.recorded",
  "version": 1,
  "createdAt": "2026-09-24T15:02:11.000Z",
  "workspaceId": "…",
  "data": { "eventKey": "purchase", "value": 1999, "currency": "USD", … }
}
Verify the signature (Node.js)
import { createHmac, timingSafeEqual } from "node:crypto";

// secret: the whsec_… signing secret, shown once when you create the endpoint
function isFromMuChat(rawBody, header, secret) {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  return header?.length === expected.length &&
    timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}

The contact object

Only these fields are ever returned — new internal columns stay private by default.

id
MuChat contact id — use it in the other endpoints.
igsid
The person’s Instagram-scoped id for your account.
username / name
Instagram username and display name, when known.
profilePicUrl
Profile picture URL, when known.
tags
Tag names on the contact.
customFields
Your custom field values, keyed by field key.
subscribed
The contact’s subscribed flag.
source
Where they came from: comment, DM, story, import or manual.
assignedToId / inboxStatus
Who the conversation is assigned to, and whether it’s open or closed.
lastInteractionAt / lastInboundAt
Last activity, and when they last messaged you.
canMessage
true while you’re inside Instagram’s 24-hour window.
createdAt
When they became a contact.

Errors and limits

Errors come back as { "error": "…" } with one of these status codes.

400
Bad query parameters or JSON body — the message says what’s wrong.
401
Missing, malformed, invalid or revoked API key.
403
The key doesn’t have the scope this endpoint needs.
404
No contact, automation or conversion event with that id or key in your account.
410
That conversion event is archived and no longer records conversions.
409
Can’t send: outside the 24-hour window, or no Instagram account connected.
429
Rate limit reached. Wait for Retry-After seconds.
500
Something went wrong on our side.
502
Instagram refused the message. The response includes the messageId we logged.

Each key is rate-limited per minute. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; back off when Remaining reaches zero.

Get a key and start building

Create a free account, connect Instagram, and make your first call from Settings → API.