Developer pages
Zinvee InboxGuide

Connect your tools to Zinvee Inbox

Let another system (your CRM, a website form, an automation tool) add and tag contacts, move them along the pipeline, read conversations and send WhatsApp messages and templates, and hear the moment a message arrives.

Base URL https://api.dm.diginetacademy.in/api/v1/public. Every endpoint is in the API reference.

Quick start

  1. In Zinvee Inbox, open Settings → API & webhooks and create a key with only the scopes the tool needs. The key is shown once.
  2. Check it works:
    Is this key alive?
    export INBOX_KEY="ik_live_…"
    
    curl https://api.dm.diginetacademy.in/api/v1/public/me \
      -H "Authorization: Bearer $INBOX_KEY"
  3. Add a contact with tags, or update the one with that phone:
    Create or update a contact
    curl -X POST https://api.dm.diginetacademy.in/api/v1/public/contacts \
      -H "Authorization: Bearer $INBOX_KEY" \
      -H "Content-Type: application/json" \
      -d '{"phone": "919876543210", "name": "Asha Menon", "tags": ["hot-lead"]}'
  4. Send an approved template, which WhatsApp delivers at any time:
    Send a template
    curl -X POST https://api.dm.diginetacademy.in/api/v1/public/messages \
      -H "Authorization: Bearer $INBOX_KEY" \
      -H "Content-Type: application/json" \
      -d '{"phone": "919876543210", "kind": "template", "template_name": "welcome_msg", "template_language": "en", "name_param": true}'

Keys and scopes

Send the key on every request as Authorization: Bearer ik_live_…. Keys are shown once and stored only as a SHA-256 hash, and they open only the public API. A key holds one or more scopes; a request outside them gets 403 forbidden naming the scope it needed. GET /me needs no scope.

ScopeUnlocks
contacts:readGET /contacts, GET /contacts/{phone}, GET /stages
contacts:writePOST /contacts, POST /contacts/{phone}/tags, POST /contacts/{phone}/stage
conversations:readGET /conversations, GET /conversations/{phone}/messages
messages:sendPOST /messages
campaigns:readGET /campaigns, GET /campaigns/{id}
flows:readGET /flows

Phones and bodies

  • Bodies are JSON both ways.
  • Phones are digits with the country code and nothing else: 919876543210. Anything you send is cleaned up the way the inbox does it, so +91 98765 43210 is the same person.
  • Someone who has only written on Instagram or the website chat has no phone. They appear in GET /contacts, but routes under /contacts/{phone} can’t reach them.
  • A text message goes only inside WhatsApp’s 24-hour customer-service window, which starts at the contact’s last message to you. Outside it, send a template.

Errors

Every error is {"error": "<a sentence>", "code": "<a word>"}, sometimes with a field or two more. Branch on code.

StatusCode
400invalid_* for a bad input: invalid_phone, invalid_body, invalid_tags, invalid_stage and so on.
401unauthorized: no key, or not a valid one.
403forbidden: the key lacks the scope, which the body names in scope.
404not_found
409window_closed: a text message outside WhatsApp’s 24-hour window. Send a template instead.
429rate_limited, with retry_after and a Retry-After header.
500server_error
502send_failed: Meta refused the message. Carries meta_code, transient and message_id.

Rate limit

Each key may make 300 requests a minute. Over that, the answer is 429 with a Retry-After header and retry_after in the body:

429
{"error": "This key may make 300 requests a minute; try again in 12s", "code": "rate_limited", "retry_after": 12}

Webhooks

Under Settings → API & webhooks → Add endpoint, give a URL and pick events. You get a secret once; keep it on the receiving side. Each event is a POST to your URL:

A delivery
Content-Type: application/json
X-Event: message.received
X-Delivery-Id: 1842
X-Signature: 3f0e…   (hex HMAC-SHA256 of the raw body, keyed by the secret)

{"id": 1842, "event": "message.received", "created_at": "2026-11-14T08:00:00.412Z", "data": {…}}
  • Answer any 2xx within 10 seconds. Anything else (a 4xx, a 5xx, a timeout, a redirect) is retried: 5 attempts in all, starting 30 seconds later and backing off.
  • 20 failed deliveries in a row switch the endpoint off; it says so under the endpoint, and switching it back on resets the count.
  • Deliveries can arrive out of order and, rarely, twice. Use id to de-duplicate.
  • The Test button sends {"event": "ping"} whatever the endpoint subscribed to.

Check the signature over the raw body before trusting a delivery:

Verify a delivery (Node.js)
import crypto from "node:crypto";
import express from "express";

const SECRET = process.env.INBOX_WEBHOOK_SECRET; // from the endpoint dialog
const app = express();

// The body must be the raw bytes: parse it only after checking.
app.post("/hooks/inbox", express.raw({ type: "application/json" }), (req, res) => {
  const expected = crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
  const given = String(req.header("x-signature") ?? "");
  const ok = given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
  if (!ok) return res.status(401).end();

  const delivery = JSON.parse(req.body.toString("utf8"));
  // delivery.id, delivery.event, delivery.created_at, delivery.data
  res.status(204).end(); // answer first, work after
});

The events and what each carries are in the API reference.