Klaviyo for Odoo
User guide
This guide covers Klaviyo for Odoo. It is one app that you install once. The parts for other Odoo apps switch themselves on when those apps are installed: Sales, Inventory, Point of Sale, eCommerce, Email Marketing, Loyalty and Repairs, and in Odoo Enterprise, Subscriptions, Appointments and Helpdesk. Section 5 says what each part sends.
1. Before you start
- Odoo 17.0, 18.0, 19.0 or 20.0, on Odoo.sh or self-hosted. Odoo Online does not accept third-party modules, so it cannot use this connector.
- A Klaviyo account, and a Klaviyo user who may authorize apps.
- An Odoo user in the group Klaviyo Manager. Klaviyo User can read the status page. System administrators can also see the tokens.
- Your Odoo must be reachable from the internet over HTTPS if you want consent changes to arrive at once. Without a public address the connector still works and checks Klaviyo every 15 minutes.
2. Install
- Open Apps, remove the "Apps" filter and search for "Klaviyo".
- Install Klaviyo for Odoo. Nothing else needs installing: the parts for Sales, Inventory and the other Odoo apps you use switch on by themselves.
- A new Klaviyo app appears. Opening it for the first time takes you to the setup assistant.
Every company has its own Klaviyo account record. In a multi-company database, open the app once per company.
3. Connect to Klaviyo
- In the setup assistant press Connect with Klaviyo.
- Sign in to Klaviyo and approve the access the app asks for. The permissions, and why the app needs each one, are listed at the end of this guide.
- Klaviyo sends you back to Odoo and the assistant moves on.
Odoo stores the access and refresh tokens encrypted (see "How the tokens are protected" below). A small relay run by FlexigoTech at connect.flexigotech.com only passes the tokens during sign-in and while refreshing them. It never receives contacts, orders or any other data. Your data goes straight from Odoo to Klaviyo.
If the connection breaks (for example Klaviyo revokes the app), the account shows "disconnected" and the Klaviyo Managers get an activity. Press Connect with Klaviyo again. The buttons Disconnect and Forget credentials are on the account form: Disconnect revokes the token in Klaviyo and removes the webhook, and Forget credentials is for a token Klaviyo could not revoke.
How the tokens are protected
The access token, the refresh token and the webhook secret are encrypted with Fernet before they are written to the database. The encryption key comes from one of two places.
- If
odoo.confhas the optionklaviyo_token_key(any long secret text), that is the key. It lives on the server, outside the database, so it also protects the tokens if someone obtains a copy of the database. Odoo 19 and 20 log a warning about an unknown option inodoo.conf; it is harmless. - If the option is not set, the key is derived from the
database.secretof the database. That key is inside the database, so this protects against exports, single-table copies and partial reads, but not against someone who gets the whole database.
We recommend setting klaviyo_token_key. Keep it the same on every server that runs this database, and keep a copy. If the key changes or is lost, Odoo cannot read the stored tokens. The account then goes to the state Error with the message "Reconnect Klaviyo", sends nothing, and does not keep retrying. Press Connect with Klaviyo again and the new tokens are encrypted with the current key. If you add the option to a database that already works, existing tokens keep working and are re-encrypted with the server key the next time they change.
Updating from a version that stored tokens without encryption encrypts them during the update.
4. The setup assistant
The assistant has five steps, and you can go back at any time.
- Connect.
- Who sends marketing. Choose Klaviyo, Odoo or both. This matters if you also send mass mailings from Odoo, because two systems must not email the same person. For email and for SMS you also choose who is authoritative for consent: Odoo, Klaviyo, or both (an unsubscribe in either one wins, and this is the default). Pick the Klaviyo list that new subscribers join. If the list uses double opt-in, Klaviyo sends the confirmation email itself.
- What to sync. Switches for contacts, email activity coming back to Odoo, catalog, lists from tags and erasure on delete, plus test mode (section 6). Each installed part adds its own switch, such as orders, invoices without a sales order, loyalty, repairs, mailing lists or store visitors.
- History. Optionally send the contacts you already have, and the past orders, deliveries, till orders, repairs, redeemed rewards, tickets, appointments and subscriptions of the parts that are installed. Choose a start date and the sources. This can be done later with the Send history button.
- Summary. It says in plain words what will happen. Press Start.
Everything works with the defaults. You can run the assistant again at any time from Set up Klaviyo on the account. It shows your current choices and only saves what you change.
5. What each part syncs
Klaviyo for Odoo (core)
| What | Direction | When |
|---|---|---|
| Contacts with an email or phone | Odoo to Klaviyo, as profiles | Queued when a contact is created or changed, sent by a job that runs every minute |
| Field mapping | Odoo to Klaviyo | Same as contacts. Defaults work without configuration. You add fields on the Contacts tab |
| Contact tags | Odoo to Klaviyo, as lists | Choose the tags on the Lists tab |
| Products for sale, variants, categories | Odoo to Klaviyo catalog | When they change, and a daily refresh for stock and prices |
| Marketing opt-outs and explicit consent | Both ways | Immediately with the webhook, otherwise every 15 minutes |
| Email opens, clicks and emails received in 30 days | Klaviyo to Odoo | With the webhook, written on the contact |
| Existing contacts | Odoo to Klaviyo, in bulk | On demand, from the assistant |
Details worth knowing:
- A profile is identified by email, by a phone number in E.164 format and by an external id made of a prefix and the Odoo id. If you change the email of a contact, the same profile is updated.
- Phone numbers are converted using the contact's country, or the company's country when the contact has none. A number that cannot be converted is not sent and the status page shows a warning.
- Only contacts that match the filter on the Contacts tab are sent. By default that means contacts with an email or a phone that are not archived.
- Odoo never subscribes anyone on its own. A person is subscribed in Klaviyo only after explicit consent in Odoo, which is a ticked consent box on the contact.
- An opt-out in Odoo (the email or phone blacklist) is sent to Klaviyo and wins over the consent box.
- An unsubscribe, a spam report or a manual suppression in Klaviyo blacklists the address in Odoo when Klaviyo is authoritative or both are.
Parts that switch on with other Odoo apps
| Odoo app | What is sent | When |
|---|---|---|
| Sales | Placed Order and Ordered Product | When an order is confirmed |
| Sales | Cancelled Order | When an order that Klaviyo already saw as placed is cancelled |
| Sales | Refunded Order, one per order with the accumulated total | After a settling period (48 hours by default, configurable) since the last refund |
| Sales | Placed Order and Ordered Product for invoices without a sales order (B2B), and Refunded Order when they are credited | Optional, off by default |
| Sales | Customer scores on the profile: Lifecycle Stage, RFM Segment, RFM Recency, Frequency and Monetary Score, Number of Orders, Total Spent and Last Order Date | Every night, for the contacts whose scores changed |
| Sales | Orders of the past | On demand, from the history step |
| Inventory | Fulfilled Order, with tracking number and carrier | When a delivery is done. A partial delivery and its backorder are two events |
| Point of Sale | Placed Order and Ordered Product, with the same names as Sales and the channel POS | When a till order is paid |
| Point of Sale | Refunded Order, one per order with the accumulated total, and Refunded Order Adjustment for a refund made after it was sent | After the same settling period as Sales |
| Point of Sale | The customer's most recent store (Most Recent Store ID, Name and Location) on the profile | After each paid till order |
| Point of Sale | Till orders of the past | On demand, from the history step |
| eCommerce | Viewed Product, Added to Cart and Started Checkout, sent from the visitor's browser by Klaviyo's script | Only after the visitor accepts the optional cookies of Odoo's cookies bar, or when you manage visitor consent yourself |
| eCommerce | The public product page, image and website categories in the Klaviyo catalog | When a product is published or changed |
| eCommerce | A consent box at checkout for guests | Only when you write a consent text on the account. Off by default |
| Email Marketing | The mailing lists you choose, as Klaviyo lists, and the people on them as profiles, with no second profile when an Odoo contact has the same email | When a list or a subscription changes, and a nightly check |
| Email Marketing | Consent for that list, sent once for each person on it. Leaving a list takes the profile out of that list only | When the person joins or leaves the list |
| Loyalty | Loyalty Points, Loyalty Level, Loyalty Next Reward, Loyalty Points To Next Reward, Gift Card Balance, Active Gift Cards, eWallet Balance, Active Coupons and Coupon Expires On, on the profile | When a card, program or reward changes |
| Loyalty | Redeemed Reward, one per order and card | 5 minutes after the last change to the redemption |
| Repairs | Repair Created and Repair Completed | When a repair is confirmed and when it is done. The repair request and the internal notes are never sent |
| Helpdesk (Enterprise) | Opened Ticket and Closed Ticket | When a ticket is opened and when it reaches a closed stage. Free text written by the customer is never sent |
| Appointments (Enterprise) | Booked Appointment and Cancelled Appointment | When a booking is accepted and when it is cancelled |
| Subscriptions (Enterprise) | Started, Renewed and Cancelled Subscription, with the plan, recurring amount, monthly revenue and dates | When the subscription is confirmed, renewed and closed |
| Subscriptions (Enterprise) | Subscription properties on the profile: status, active subscriptions, monthly revenue, next invoice date and subscriber since | When a subscription changes, and every night |
Notes, section lines, down payments and empty lines of an order are not products. Confirming an order twice, or resetting it to draft and confirming it again, sends nothing new. The Orders tab on the account has the event names and the identifier prefix, and a filter to leave out orders that your online store already sends to Klaviyo.
Point of sale orders without a customer are not sent, and the email typed at the till for a receipt is never used. Lines paid at the till for a sales order are not sent twice.
In the online store, Klaviyo's script identifies a visitor only with data already saved: a signed-in customer, or a guest after they submit their address. It sends the email and the name, never the phone or the address. Employees browsing the store are not tracked, and in test mode anonymous visitors send nothing.
When Klaviyo sends your marketing, an Odoo mailing warns you before it goes to people who are already Klaviyo profiles. An unsubscribe made in Klaviyo also closes the subscriptions of the mailing lists you send to Klaviyo.
6. Test mode: trying it in production
Test mode lets you connect a real database and check the result before any customer reaches Klaviyo.
- In the assistant switch on Test mode. The module ships a contact tag called "Klaviyo test" and uses it by default.
- Give that tag to a few test contacts.
- Only tagged contacts are sent as profiles, get lists and consent. Untagged contacts are held back.
- Check the profiles in Klaviyo. Turn test mode off when you are satisfied.
Some rules apply while test mode is on:
- An unsubscribe that comes from Klaviyo is always applied in Odoo, tagged or not. A subscription from Klaviyo is applied only for tagged contacts.
- An opt-out made in Odoo for an address that no tagged contact has is held back, because Klaviyo would create a new profile to unsubscribe it. When you leave test mode, the held opt-outs and ticked consent boxes are sent.
- An explicit erasure request (section 7) is always sent, tagged or not.
- The automatic erasure when a contact is deleted does not run for untagged contacts, unless this account created their profile.
- Events of untagged customers that were waiting when you switched test mode on are not sent.
7. Data protection: erase and release
To erase people from Klaviyo:
- Select the contacts, open Actions and choose Erase from Klaviyo (GDPR). Only Klaviyo Managers see this action.
- A dialog warns that this cannot be undone and that other contacts with the same email or number are blocked too. Confirm.
- Odoo asks Klaviyo to delete the profiles and marks the contacts as excluded, so they are never sent again. Klaviyo does the deletion in the background, usually within a few minutes.
If Klaviyo has no profile for the contact, nothing is left to delete and that counts as done. Klaviyo deletes every profile that has the identifier you give, so a shared phone number can affect another person. For that reason the connector uses the email when there is one, and the phone number only when there is no email.
If you want the Klaviyo profile erased whenever a contact is deleted in Odoo, switch on "Erase the Klaviyo profile when a contact is deleted" in the assistant. It is off by default, because deleting a duplicate contact should not erase a real customer. Merging duplicates never erases the profile of the contact that stays.
After an erasure the connector remembers the address, as a keyed hash and not in clear, and does not send anything with it to that Klaviyo account again, not even an opt-out. To allow an address again, select the contact and choose Release erased address (Klaviyo) in Actions. This is a deliberate step for a Klaviyo Manager.
Two things we saw in tests with a real account:
- After an erasure, the unsubscribe recorded for the email address stays in Klaviyo. Erasing a profile does not turn a subscription back on.
- Klaviyo processes deletions in the background. A 202 answer only means "accepted".
Data in special categories should not be mapped to Klaviyo properties. None are mapped by default.
8. The Status tab
Open the Klaviyo account and go to Status.
- Last 24 hours: sent, failed, skipped, warnings, throttled and waiting.
- Health: token status, webhook status, last run, last success, failed runs in a row and the state of the history import.
- See the jobs lists every job with the reason Klaviyo gave for failures. Retry failed jobs puts them back in the queue.
Odoo creates an activity for the Klaviyo Managers when:
- five runs in a row have failures;
- work has been waiting and nothing has reached Klaviyo for two hours;
- a deletion request fails or runs out of attempts;
- a consent change from Klaviyo could not be applied in Odoo.
Klaviyo limits how many requests it accepts. When it answers "too many requests" the queue waits as long as Klaviyo says and continues. Failed requests are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours before they are marked failed.
9. Timing you should expect
- A new subscription can take about five minutes to show in Klaviyo, even though the request was accepted at once.
- Contact changes reach Klaviyo within a minute or two, when the queue job runs.
- Klaviyo accepts an event request before it has stored it. If an event is missing, look at the status page before sending it again. Klaviyo keeps only one event for the same identifier.
- Klaviyo ignores events for addresses that end in example.com. Use real addresses when you test.
10. Known limits
- Odoo Online is not supported.
- SMS consent uses the same rules as email, but it has not been checked against a Klaviyo account enabled for SMS.
- Sales orders: changes made to an order after it was confirmed are not sent again, and an order that is cancelled and confirmed again does not send a second Placed Order. A goods return does not send an event; the money goes through refunds.
- Point of sale: the till sends no Cancelled Order or Fulfilled Order, and it does not ask for marketing consent.
- Online store: a site with a Content Security Policy must allow static.klaviyo.com and a.klaviyo.com. Back in Stock is not included.
- Email marketing: unsubscribing from a single list in Klaviyo does not reach Odoo; a full unsubscribe does. Renaming a mailing list in Odoo does not rename the Klaviyo list. Sends, opens and clicks of Odoo mailings are not sent.
- Loyalty: a redemption undone after it was sent is not taken back in Klaviyo. On Odoo 17 only gift cards, coupons and codes send Redeemed Reward, and the history step does not offer redemptions.
- Repairs, Helpdesk and Appointments: a cancelled repair, a rescheduled appointment, ticket ratings and SLAs send nothing. On Odoo 17, cancelling an appointment for one of more than two attendees sends nothing.
- Subscriptions: pausing, resuming and upsells send no event (they show in the profile properties), and recurring invoices are not sent as orders.
- A contact without email or phone cannot be a profile.
- The connector does not merge Klaviyo profiles. If two profiles clash, the job fails with the reason and you decide what to merge in Klaviyo.
- Erased addresses stay excluded until a manager releases them.
11. Getting help
Send the status page numbers, the text of a failed job and your Odoo version to FlexigoTech support. How to reach us, and when we answer, is on the support page.
Permissions the app asks for
When you connect, Klaviyo asks you to approve these 14 permissions (scopes). Each one is used by a call the module makes.
| Scope | Why |
|---|---|
| accounts:read | Read the account id, name, time zone and currency after connecting, and prove that the token works. |
| profiles:read | Find the profile behind a duplicate error, read subscription state when polling, and check the result of bulk imports. |
| profiles:write | Create and update profiles from Odoo contacts. |
| lists:read | Load your lists for the consent choice and find a list that already has a tag's name. |
| lists:write | Create a list for a chosen tag and add or remove profiles. |
| events:write | Send order events and the history of events in bulk. |
| events:read | Read consent events when Odoo has no public address for a webhook, and check what was stored. |
| metrics:read | Find Klaviyo's own consent metrics, needed to read consent events. |
| subscriptions:write | Subscribe people who consented in Odoo and unsubscribe people who opted out in Odoo. |
| catalogs:read | Check the state of catalog items before updating or deleting them. |
| catalogs:write | Create, update and delete catalog items, variants and categories. |
| data-privacy:write | Send data deletion requests when a manager asks to erase a contact. |
| webhooks:read | Check that the webhook Odoo created still exists and is enabled. |
| webhooks:write | Create the webhook that brings unsubscribes and email activity back to Odoo, and remove it on disconnect. |
