Developer pages
Zinvee InboxAPI

Inbox API reference

Base URL https://api.dm.diginetacademy.in/api/v1/public. Send a key as Authorization: Bearer ik_live_…; the guide covers keys, scopes, errors, limits and webhooks.

Key

Is this key alive, and what may it do.

Who is this key

get/me
Any valid key

Needs no scope. A quick check that the key works, and which scopes it holds.

Response 200

Request
curl "https://api.dm.diginetacademy.in/api/v1/public/me" \
  -H "Authorization: Bearer $INBOX_KEY"
Response 200
{ "key_id": "…", "scopes": ["contacts:read", "messages:send"] }

Contacts

The people in the inbox, found by phone.

List contacts

get/contacts
Scope contacts:read

Sorted by most recent activity. next_cursor is null on the last page.

stage is the name of the pipeline stage they are in (null when they are not in the pipeline); deal_value is whole rupees, or null.

phone is null for someone who has only written on Instagram or the website chat; their Instagram handles are in instagram. Routes under /contacts/{phone} find people by phone, so such a contact is reached through this list.

Query

  • qstring

    A name or phone fragment.

  • tagstring

    A tag name or id.

  • limitinteger

    1 to 200, default 50.

  • cursorinteger

    An offset: send the next_cursor you were given.

Response 200

Request
curl "https://api.dm.diginetacademy.in/api/v1/public/contacts" \
  -H "Authorization: Bearer $INBOX_KEY"
Response 200
{
  "contacts": [ { "id": "6f1c…", "phone": "919876543210", "name": "Asha Menon", "tags": ["hot-lead"], "…": "…" } ],
  "total": 7,
  "next_cursor": 2
}

One contact

get/contacts/{phone}
Scope contacts:read

One contact by phone.

Path

  • phonestringrequired

    Digits with the country code.

Response 200

Errors: 400 invalid_phone; 404 not_found

Request
curl "https://api.dm.diginetacademy.in/api/v1/public/contacts/919876543210" \
  -H "Authorization: Bearer $INBOX_KEY"
Response 200
{
  "contact": {
    "id": "6f1c…", "phone": "919876543210", "name": "Asha Menon",
    "email": "[email protected]", "city": "Kochi", "source": "inbound",
    "tags": ["hot-lead"], "stage": "Interested", "deal_value": 25000,
    "instagram": [{ "username": "asha.designs", "account": "Main page" }],
    "fields": { "course": "Digital Marketing" },
    "first_seen_at": "2026-11-01T10:12:00.000Z",
    "last_inbound_at": "2026-11-14T08:00:00.000Z",
    "last_outbound_at": "2026-11-14T08:02:11.000Z",
    "created_at": "2026-11-01T10:12:00.000Z",
    "updated_at": "2026-11-14T08:02:11.000Z"
  }
}

Create or update a contact

post/contacts
Scope contacts:write

Creates the contact, or updates the one with that phone. A known name is never replaced by nothing.

Answers 201 with "created": true for a new contact and 200 with "created": false for an existing one. A contact.created webhook fires for a new one, and contact.tagged for each tag that was not there before.

Body

  • phonestringrequired

    Any common format: +91 98765 43210 is the same person as 919876543210.

  • namestring

    Up to 100 characters.

  • emailstring

    An email address.

  • citystring

    Up to 100 characters.

  • tagsstring[]

    Tag names; a new name creates the tag.

  • fieldsobject

    Custom field values; each key must be a field that exists (Settings → Fields & tags). null clears one.

  • stagestring

    A stage’s name, any case.

Response 201

Errors: 400 invalid_phone, invalid_email, invalid_tags, invalid_fields, invalid_stage

Request
curl -X POST "https://api.dm.diginetacademy.in/api/v1/public/contacts" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone": "+91 98765 43210", "name": "Asha Menon", "tags": ["hot-lead", "nov-webinar"], "fields": {"course": "Digital Marketing"}, "stage": "Interested"}'
Response 201
{ "contact": { "…": "…" }, "created": true }

Add tags

post/contacts/{phone}/tags
Scope contacts:write

added counts the tags that were new on the contact.

Path

  • phonestringrequired

    Digits with the country code.

Body

  • tagsstring[]required

    At least one tag name.

Response 200

Errors: 400 invalid_tags; 404 not_found

Request
curl -X POST "https://api.dm.diginetacademy.in/api/v1/public/contacts/919876543210/tags" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tags": ["paid"]}'
Response 200
{ "contact": { "…": "…" }, "added": 1 }

Move to a stage

post/contacts/{phone}/stage
Scope contacts:write

Moves the contact to a pipeline stage, by its name. changed is false when they were there already.

The move is written in the contact’s history (by “the API”), sends contact.stage_changed, and starts the flows waiting on that stage.

Path

  • phonestringrequired

    Digits with the country code.

Body

  • stagestringrequired

    A stage’s name; GET /stages lists them.

Response 200

Errors: 400 invalid_stage; 404 not_found

Request
curl -X POST "https://api.dm.diginetacademy.in/api/v1/public/contacts/919876543210/stage" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stage": "Demo booked"}'
Response 200
{ "contact": { "…": "…" }, "changed": true }

List pipeline stages

get/stages
Scope contacts:read

The pipeline’s stages, in order. outcome is open, won or lost.

Response 200

Request
curl "https://api.dm.diginetacademy.in/api/v1/public/stages" \
  -H "Authorization: Bearer $INBOX_KEY"
Response 200
{
  "stages": [
    { "id": "…", "name": "New lead", "outcome": "open", "position": 0 },
    { "id": "…", "name": "Won", "outcome": "won", "position": 4 }
  ]
}

Conversations

Threads and their messages.

List conversations

get/conversations
Scope conversations:read

Newest activity first.

Query

  • statusstring

    open, pending, closed or all (default all).

  • assigneestring

    A member id, or unassigned.

  • qstring

    A name or phone fragment.

  • limitinteger

    1 to 500, default 100.

Response 200

Errors: 400 invalid_status

Request
curl "https://api.dm.diginetacademy.in/api/v1/public/conversations" \
  -H "Authorization: Bearer $INBOX_KEY"
Response 200
{
  "conversations": [
    {
      "phone": "919876543210", "name": "Asha Menon", "contact_id": "6f1c…",
      "status": "open", "assigned_user_id": 3, "assigned_name": "Rahul",
      "unread": 2, "needs_reply": true,
      "last_at": "2026-11-14T08:00:00.000Z", "last_direction": "in",
      "last_inbound_at": "2026-11-14T08:00:00.000Z",
      "preview": "How much are the fees?", "tags": ["hot-lead"]
    }
  ]
}

Messages in a conversation

get/conversations/{phone}/messages
Scope conversations:read

Oldest first. origin says who sent an outbound message: agent, flow, campaign, api, ai or router.

Path

  • phonestringrequired

    Digits with the country code.

Query

  • limitinteger

    1 to 500, default 100.

Response 200

Request
curl "https://api.dm.diginetacademy.in/api/v1/public/conversations/919876543210/messages" \
  -H "Authorization: Bearer $INBOX_KEY"
Response 200
{
  "phone": "919876543210",
  "messages": [
    {
      "id": "b2…", "direction": "in", "status": "received", "kind": "text",
      "text": "How much are the fees?", "template_name": null,
      "attachment_url": null, "attachment_type": null,
      "origin": null, "user_id": null, "wa_message_id": "wamid.HBg…",
      "error": null, "sent_at": null, "created_at": "2026-11-14T08:00:00.000Z"
    }
  ]
}

Messages

Send on WhatsApp.

Send a message

post/messages
Scope messages:send

text works only inside WhatsApp’s 24-hour customer-service window, which starts at the contact’s last message to you. Outside it the answer is 409 window_closed (with last_inbound_at), and a template is the only thing WhatsApp will carry.

template sends a template Meta has approved, at any time. name_param: true fills {{1}} with the contact’s name as the inbox knows it (or “there” when there is none); a string fills it with that string.

A sent message appears in the inbox with origin api and raises a message.sent webhook with by: "api". Sending through the API does not assign the conversation to anyone and does not pause the flows.

When Meta refuses, the answer is 502 send_failed with Meta’s meta_code, whether it is transient, and the message_id; the attempt is kept in the thread, marked failed.

Body

  • phonestringrequired

    Digits with the country code.

  • kindstringrequired

    text or template.

  • textstring

    For text.

  • template_namestring

    For template: an approved template.

  • template_languagestring

    For template, e.g. en.

  • name_paramboolean | string

    For template: what fills {{1}}.

Response 201

Errors: 409 window_closed; 502 send_failed

Request
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}'
Response 201
{ "message": { "id": "b4…", "phone": "919876543210", "kind": "template", "status": "sent", "wa_message_id": "wamid.HBj…", "created_at": "…" } }

Campaigns and flows

Broadcasts and automations, read-only.

List campaigns

get/campaigns
Scope campaigns:read

status is draft, scheduled, sending, paused, done or cancelled.

Response 200

Request
curl "https://api.dm.diginetacademy.in/api/v1/public/campaigns" \
  -H "Authorization: Bearer $INBOX_KEY"
Response 200
{
  "campaigns": [
    {
      "id": "c9…", "name": "November webinar", "template_name": "webinar_invite", "template_language": "en",
      "status": "sending", "scheduled_at": "2026-11-14T04:30:00.000Z", "started_at": "2026-11-14T04:30:02.000Z", "finished_at": null,
      "rate_per_minute": 20, "total": 1200,
      "counts": { "sent": 400, "delivered": 380, "read": 122, "failed": 3, "pending": 800 },
      "created_at": "…", "updated_at": "…"
    }
  ]
}

One campaign

get/campaigns/{id}
Scope campaigns:read

The same fields as the list, as { "campaign": { … } }.

Path

  • idstringrequired

    Campaign id.

Response 200

Errors: 404 not_found

Request
curl "https://api.dm.diginetacademy.in/api/v1/public/campaigns/c9…" \
  -H "Authorization: Bearer $INBOX_KEY"
Response 200
{ "campaign": { "id": "c9…", "…": "…" } }

List flows

get/flows
Scope flows:read

kind is lead (started by a lead), reply (answers keywords), contact (on a new contact) or tag (when a tag is added; trigger.tag).

channels says where the flow runs: any of whatsapp, instagram, web (the website chat) and messenger (waiting on Meta’s review). A lead flow runs on WhatsApp only.

Response 200

Request
curl "https://api.dm.diginetacademy.in/api/v1/public/flows" \
  -H "Authorization: Bearer $INBOX_KEY"
Response 200
{
  "flows": [
    { "id": "f1…", "key": "welcome", "name": "Welcome flow", "kind": "lead", "description": null,
      "is_active": true, "archived_at": null, "trigger": {}, "channels": ["whatsapp"], "steps": 6, "updated_at": "…" }
  ]
}

Webhook events

What a webhook endpoint receives in data. Every delivery is { id, event, created_at, data }; see Webhooks for signatures and retries.

  • message.received

    { phone, wamid, text, kind, contact: { id, name } }. kind is text, image, button, interactive, audio, document and so on; text is what a person would read (the text, a button’s label, a caption).

  • message.sent

    { phone, message_id, wa_message_id, kind, by, user_id }. by is agent, api or ai; user_id is the teammate for agent, else null.

  • conversation.assigned

    { phone, assigned_user_id, by }. assigned_user_id is null when unassigned.

  • conversation.closed

    { phone, by }

  • contact.created

    { contact: { … } }, with tags as { id, name, color }. phone is null for someone who first wrote on Instagram or the website chat.

  • contact.tagged

    { contact_id, phone, tag }. One delivery per tag, only for tags that were not on the contact before.

  • contact.stage_changed

    { contact, from_stage_id, to_stage_id, by }. by is person, flow, source, api or ai; to_stage_id is null when they were taken out of the pipeline.