← All posts
Changelog

UnifyPort API Update: Conversation Labels, Contact Messages, and Request Tracing — UnifyPort

Three weeks after the v1 stable release, the UnifyPort API picks up its first feature batch: conversation labels for organizing WhatsApp chats, a contact-card message type, request tracing across every call, and a set of developer-experience improvements that make the webhook event reference substantially more useful.

Here’s what shipped, with the real endpoints and field names.

Conversation labels — 4 new endpoints

You can now create, list, update, and delete labels on conversations, and tag or untag conversations in bulk. This is the same label system WhatsApp exposes natively — UnifyPort surfaces it through four endpoints under the Conversations category.

List labels

GET /v1/accounts/{account_id}/conversations/labels?limit=50&cursor=

Returns paginated labels, each with id and name:

{
  "data": {
    "items": [
      { "id": "label_example", "name": "VIP" }
    ],
    "next_cursor": "",
    "has_more": false
  }
}

Create or update a label

POST /v1/accounts/{account_id}/conversations/labels/upsert

Pass an empty label_id to create; pass an existing label_id to rename:

{
  "label_id": "",
  "name": "VIP"
}

Delete a label

POST /v1/accounts/{account_id}/conversations/labels/delete
{
  "label_id": "label_example"
}

Tag or untag conversations

POST /v1/accounts/{account_id}/conversations/labels

action is add or remove; conversation_ids must contain at least one ID:

{
  "label_id": "label_example",
  "action": "add",
  "conversation_ids": ["peer_example"]
}

All four endpoints are WhatsApp-only for now. Other providers return 501 unsupported_by_provider. The corresponding error codes — list_conversation_labels_failed, upsert_label_failed, delete_label_failed, set_label_members_failed — are documented in the error reference.

Contact (vCard) message type

POST /v1/messages now accepts message.type: "contact" with a structured contacts array. UnifyPort generates the vCard for you — you send structured JSON, the provider receives a proper contact card.

{
  "account_id": "acc_example",
  "to": {
    "id": "user_example",
    "type": "user"
  },
  "message": {
    "type": "contact",
    "contacts": [
      {
        "name": "Jane Doe",
        "phones": [{ "number": "+8613800000000", "type": "CELL" }],
        "emails": [{ "address": "jane@example.com" }],
        "organization": "ACME",
        "title": "PM"
      }
    ]
  }
}

Each card requires name. The phones[].number, emails[].address, organization, and title fields are optional. You can send a single card or multiple cards in the array — WhatsApp maps single cards to its contact type and multiple cards to contact_array.

An empty contacts array or a card missing name returns 400 invalid_request. Non-WhatsApp providers return 400 unsupported_message_type until support ships for those channels.

Request tracing — request_id and X-Request-Id

Every API response — success and error — now carries a top-level request_id field and returns the same value as the X-Request-Id response header. Quote it when reporting issues or debugging failed calls.

For client-side reconciliation, send your own X-Request-Id request header and it’s echoed back as client_request_id in the response. This lets you correlate outbound API calls with your own internal request IDs without maintaining a separate mapping layer.

Real webhook payload samples

The API reference now includes real payload shapes for webhook events, drawn from the actual device-webhook parsers (Telegram and WhatsApp).

message.received shows payloads by content type — text, image, voice, document, and location — so you can see the exact field structure for each variant instead of guessing from a generic data: {} placeholder.

group.updated shows payloads by change kind:

  • Renamedchanges[].kind: "renamed" with the new group name
  • Members added/removedchanges[].kind: "member_added" or "member_removed" with members[] arrays of participant IDs
  • Admin promoted/demotedchanges[].kind: "promoted" or "demoted" with members[]

A single group.updated event can carry several changes at once — for example, a rename plus a member addition in the same snapshot.

Three new developer tools

The tools page now includes three browser-based utilities, all running client-side:

  • HMAC Signature Generator — generate and verify HMAC-SHA1/SHA256/SHA512 signatures in your browser; useful for debugging webhook signing_secret verification
  • Unix Timestamp Converter — convert Unix timestamps to human-readable dates and back, with auto-detection of seconds vs. milliseconds
  • JSON Formatter — format, minify, and validate JSON with syntax highlighting and one-click JSON Path copying

Breaking change: two fields removed

Two undocumented fields have been removed from the POST /v1/messages reference to match the actual API surface:

  • idempotency_key — removed. It was documented but never implemented in the device API.
  • provider_data.reply_to — removed from the documented surface. The provider_data object now documents parse_mode as its example field.

If your code was sending idempotency_key or provider_data.reply_to, check whether you were relying on behavior that was already a no-op. Neither field was processed by the API.

What’s next

Conversation labels and contact messages ship WhatsApp-first because WhatsApp’s upstream API supports them natively. As other providers add equivalent capabilities — or as the normalization layer can bridge the gap — they’ll light up on additional channels with the same endpoint surface. The 501 unsupported_by_provider response is intentional: it tells you exactly which provider doesn’t support a feature yet, rather than silently dropping the request.

The full API reference is live at unifyport.ai/docs.