Skip to content

Audience

Contacts

Keep the people you message as contacts, group them with tags, lists and segments, and record who agreed to hear from you.

What a contact is#

A contact is a person your account can message: a name, a phone number in E.164, optionally an email address and recipient IDs for channels that do not use a phone number, plus your own attributes and tags. Contacts belong to the account and are the same for live and test keys.

You do not need contacts to send single messages with POST /v1/messages. You need them for campaigns, and they are where unsubscribes are recorded.

Contact
{
  "id": "ct_8Jk3mP6qR9sT2vW5xY1z",
  "object": "contact",
  "first_name": "Layla",
  "last_name": "Haddad",
  "phone": "+971501234567",
  "email": "layla@example.com",
  "channel_identifiers": {
    "telegram": "482910375"
  },
  "locale": "ar",
  "timezone": "Asia/Dubai",
  "attributes": {
    "city": "Dubai",
    "orders": 12
  },
  "tags": [
    "vip",
    "newsletter"
  ],
  "consent": {
    "whatsapp": {
      "state": "opted_in",
      "source": "api",
      "updated_at": "2026-10-01T08:00:00.000Z"
    }
  },
  "blocked": false,
  "blocked_reason": null,
  "last_messaged_at": "2026-10-05T09:30:00.000Z",
  "created_at": "2026-10-01T08:00:00.000Z",
  "updated_at": "2026-10-05T09:30:00.000Z"
}

Create and update#

POST /v1/contacts/upsert is the call most integrations want: it creates the contact, or updates the one that already has the phone number. A number without a country code is read as a number of your account country; anything that is not a phone number is rejected with 400 parameter_invalid.

curl https://api.omnimessage.co/v1/contacts/upsert \
  -H "Authorization: Bearer om_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Layla",
    "last_name": "Haddad",
    "phone": "+971501234567",
    "email": "layla@example.com",
    "locale": "ar",
    "timezone": "Asia/Dubai",
    "tags": [
      "vip",
      "newsletter"
    ],
    "consent": {
      "whatsapp": "opted_in"
    }
  }'
  • POST /v1/contacts creates and fails with 409 contact_exists when the number is taken.
  • PATCH /v1/contacts/{id} changes the fields it carries. tags replaces the whole tag set; use POST /v1/contacts/tags to add or remove tags on up to 1,000 contacts at once.
  • DELETE /v1/contacts/{id} removes the contact from every list. Messages already sent stay in the message log.
  • Keys need the scopes contacts:read and contacts:write.

Attributes#

Attributes are your own fields, such as a city or an order count. Define each one in the console under Contacts → Attributes with a key and a type (text, number, yes / no, date); the API then accepts a value for that key and rejects unknown keys. Attributes can be used in segments and as merge tags in campaign messages: {{attributes.city}}.

Tags, lists and segments#

What it isHow it changes
TagA label on the contact, such as vip.You set it: tags on the contact or POST /v1/contacts/tags.
ListA fixed group you choose, such as newsletter subscribers.POST /v1/contact_lists/{id}/members and /members/remove.
SegmentA saved rule, such as "VIP in Dubai who opted in".On its own: members are worked out every time the segment is used.

Segments are built in the console with a rule builder that shows the matching count as you type. The API reads them: GET /v1/segments, and GET /v1/segments/{id}/preview for the current count and the first matches.

Each contact has a consent state per channel type: opted_in, opted_out, or none (unknown). Set it with the consent field when you know it, for example { "consent": { "whatsapp": "opted_in" } } after a checkout opt-in.

  • When a contact replies with a stop word (STOP, UNSUBSCRIBE and equivalents in other languages) on a channel, they become opted_out for that channel type. START opts them back in.
  • A contact that is opted_out on a channel type, or blocked, is left out of every campaign on it. This also holds for numbers you paste into a campaign.
  • Every change is kept with its source and time; the console shows the history on the contact.

Importing files#

The console imports CSV and Excel files: Contacts → Import. You upload the file, match its columns to contact fields or attributes, choose what happens to phone numbers that already exist, and confirm that the people in the file agreed to be contacted. The import runs in the background; rows that could not be imported are listed in a downloadable report with the reason for each. From code, loop over POST /v1/contacts/upsert instead.

    Loading