Omnisend is an omnichannel e-commerce platform: email, SMS, push notifications, and popups in one tool with native integration for Shopify, WooCommerce, and BigCommerce. Unlike Drip or Klaviyo, Omnisend is stronger in SMS + email in a single workflow and e-commerce event automation. Without a Kommo integration, the marketer does not know which Omnisend subscribers have already become paying clients and on which plan. With the integration, Won -> Omnisend profile is instantly updated -> a post-purchase sequence for the new client is triggered.
Omnisend vs Klaviyo vs Drip for e-commerce
| Parameter | Omnisend | Klaviyo | Drip |
|---|---|---|---|
| SMS | Yes, native | Yes (SMS add-on) | Yes (US only) |
| Push notifications | Yes | No | No |
| E-commerce automation | Shopify/WooCommerce native | Shopify native | API-based |
| Price | From $16/month | From $20/month | From $39/month |
| API | REST with API Key | REST with API Key | REST Basic Auth |
Omnisend is chosen by teams with Shopify + a dedicated sales team: native store integration + CRM context via Kommo.
What is synchronized
Kommo -> Omnisend:
— Won -> create/update Contact with tags customer, plan_{tier}, channel_{source}
— Won -> set custom fields: plan, mrr, deal_id, won_date
— Won -> add to “Paying customers” segment (via tag)
— Plan change -> update tag and custom field plan
— Customer lost -> add tag churned, remove active
Omnisend -> Kommo:
— contact.unsubscribed -> Note + task for manager
— contact.bounced -> Note: verify contact email
— automation.email.clicked -> Note (optional for key emails)
Omnisend API: key requests
Base URL: https://api.omnisend.com/v3.
Authentication: X-API-KEY: {api_key} in the header.
API Key: Omnisend -> Store Settings -> Integrations -> API Keys.
import requests
from datetime import datetime, timezone
OMNISEND_API_KEY = "your_api_key"
OMNISEND_BASE_URL = "https://api.omnisend.com/v3"
HEADERS = {
"X-API-KEY": OMNISEND_API_KEY,
"Content-Type": "application/json",
}
def upsert_contact(email: str, tags: list, custom_props: dict,
first_name: str = "", phone: str = "") -> dict:
# Create or update a contact. Omnisend upserts by email
payload = {
"email": email,
"status": "subscribed",
"statusDate": datetime.now(timezone.utc).isoformat(),
"tags": tags,
}
if first_name:
payload["firstName"] = first_name
if phone:
payload["phone"] = phone # E.164 format: +31612345678
if custom_props:
payload["customProperties"] = custom_props
resp = requests.post(
f"{OMNISEND_BASE_URL}/contacts",
headers=HEADERS,
json=payload
)
# 200 = updated, 201 = created, both are OK
if resp.status_code not in (200, 201):
resp.raise_for_status()
return resp.json()
def add_tags(contact_id: str, tags: list) -> None:
resp = requests.post(
f"{OMNISEND_BASE_URL}/contacts/{contact_id}/tags",
headers=HEADERS,
json={"tags": tags}
)
resp.raise_for_status()
def remove_tag(contact_id: str, tag: str) -> None:
resp = requests.delete(
f"{OMNISEND_BASE_URL}/contacts/{contact_id}/tags/{tag}",
headers=HEADERS
)
if resp.status_code != 404:
resp.raise_for_status()
def get_contact_by_email(email: str) -> dict | None:
resp = requests.get(
f"{OMNISEND_BASE_URL}/contacts",
headers=HEADERS,
params={"email": email}
)
resp.raise_for_status()
contacts = resp.json().get("contacts", [])
return contacts[0] if contacts else None
def on_deal_won(lead: dict, contact: dict):
email = get_contact_email(contact)
name = contact["name"]
first_name = name.split()[0] if name else ""
plan = get_custom_field(lead, PLAN_FIELD_ID) or "starter"
source = get_custom_field(lead, SOURCE_FIELD_ID) or "direct"
amount = lead.get("price", 0)
phone = get_contact_phone(contact) or ""
tags = ["customer", f"plan_{plan}", f"source_{source}", "active"]
custom_props = {
"plan": plan,
"mrr": str(amount),
"kommo_deal_id": str(lead["id"]),
"won_date": datetime.now(timezone.utc).strftime("%Y-%m-%d"),
}
omnisend_contact = upsert_contact(
email=email,
tags=tags,
custom_props=custom_props,
first_name=first_name,
phone=phone,
)
create_kommo_note(lead["id"],
f"Omnisend: contact updated (tags: customer, plan_{plan})")
def on_plan_upgrade(lead: dict, contact: dict, old_plan: str, new_plan: str):
email = get_contact_email(contact)
c = get_contact_by_email(email)
if not c:
return
contact_id = c["contactID"]
remove_tag(contact_id, f"plan_{old_plan}")
add_tags(contact_id, [f"plan_{new_plan}", "upgraded"])
def on_deal_lost(lead: dict, contact: dict):
email = get_contact_email(contact)
c = get_contact_by_email(email)
if not c:
return
contact_id = c["contactID"]
remove_tag(contact_id, "active")
add_tags(contact_id, ["churned"])
Handle Omnisend Webhook:
from flask import Flask, request
app = Flask(__name__)
@app.route("/webhooks/omnisend", methods=["POST"])
def omnisend_webhook():
payload = request.json
event = payload.get("event")
contact = payload.get("contact", {})
email = contact.get("email", "")
if not email:
return "", 200
deal_id = find_kommo_deal_by_contact_email(email)
if not deal_id:
return "", 200
if event == "contact.unsubscribed":
create_kommo_note(deal_id,
"Omnisend: client unsubscribed from mailings")
create_kommo_task(deal_id,
"Clarify communication preferences - client unsubscribed from Omnisend")
elif event == "contact.bounced":
create_kommo_note(deal_id,
"Omnisend: email not delivered (bounce)")
create_kommo_task(deal_id,
"Verify email - Omnisend recorded a hard bounce")
return "", 200
Setting up a Webhook in Omnisend: Store Settings -> Integrations -> Webhooks -> Add Webhook. Select events and specify the URL.
SMS automation: Omnisend’s unique advantage
For EU e-commerce teams, the SMS channel in Omnisend is particularly valuable: EU rates are lower than US, and GDPR opt-in/opt-out support is built in. On Won with a phone number — Omnisend automatically enrolls the contact in the SMS last-step if email is not opened.
# On Won with phone number - set SMS status
if phone:
payload["phone"] = phone
payload["phoneStatus"] = "subscribed" # GDPR: only if consent is given
payload["phoneStatusDate"] = datetime.now(timezone.utc).isoformat()
Real-world case
DTC e-commerce SaaS (EU, Shopify + Kommo + Omnisend, 40–60 new clients per month):
- Before: Omnisend segmented all subscribers without distinguishing “purchased / not purchased”. The post-purchase sequence went to everyone — including those who had not yet become clients. Onboarding emails without CRM context.
- After: Won -> tag
customer+ custom fields plan/mrr -> the correct segment is triggered in Omnisend. Trial users and paying clients receive different sequences. Upgrade emails do not go to those already on the top plan. - Additionally: when the
churnedtag is applied -> Omnisend starts a win-back automation. 9% of churned clients recovered over 6 months.
Who this is relevant for
- E-commerce with a dedicated sales team: Shopify + managers
- Teams with an SMS channel in EU — Omnisend natively supports EU carriers
- SaaS with a subscription model: different email sequences for each plan
- Companies with 30+ active clients where segmentation already matters
Frequently asked questions
Omnisend vs Klaviyo — when to choose which for e-commerce?
Klaviyo: if you have Shopify and need the richest Shopify triggers (abandoned cart with specific items, Browse Abandonment, Price Drop). Omnisend: if SMS + email in one workflow are needed, more affordable pricing, slightly simpler UI. For Kommo + Klaviyo — similar architecture, the difference is in platform capabilities.
Omnisend customProperties — how do I create a custom field?
Via the API: on the first POST /contacts with customProperties: {"field_name": "value"}, Omnisend automatically creates the custom property. It is then available in segmentation and personalization. Field name — any string; best practice: snake_case.
Omnisend webhook — how do I secure the endpoint?
Omnisend does not sign webhooks with HMAC. Protection: secret URL (/webhooks/omnisend/{random_secret}) + optional IP whitelist. Omnisend publishes their IP addresses in the documentation.
Does Omnisend support GDPR double opt-in?
Yes. When creating a contact via API, you can specify status: "unconfirmed" — the contact will receive a confirmation email. After confirmation, the status changes to subscribed. For EU teams this is mandatory when there is no explicit consent at registration.
Summary
- Omnisend API:
X-API-KEYin header,https://api.omnisend.com/v3 - Upsert contact:
POST /contacts— 200/201 both OK - Tags:
POST /contacts/{id}/tags, remove:DELETE /contacts/{id}/tags/{tag} - customProperties: created automatically on first API use
- SMS: pass
phone(E.164) andphoneStatus: "subscribed"only with consent - Webhook: no HMAC — protection via secret path + IP whitelist
If you use Omnisend and Kommo and want to sync CRM statuses with email/SMS segments — describe your plan structure and current automation workflows. Exceltic.dev will configure the mapping and event handling.