Skip to content

Conversation

@fivetran-catfritz
Copy link
Contributor

@fivetran-catfritz fivetran-catfritz commented Aug 25, 2025

PR Overview

Package version introduced in this PR:

  • v1.1.0-a1
  • v1.1.0-a2
  • v1.1.0

This PR addresses the following Issue/Feature(s):

  • GA-1001660

Summary of changes:

  • Fixes attribution for order lifecycle events. Fulfilled Order and similar order-related events now inherit the touch from their corresponding Placed Order, rather than the nearest intervening event.

Submission Checklist

  • Alignment meeting with the reviewer (if needed)
    • Timeline and validation requirements discussed
  • Provide validation details:
    • Validation Steps: Check for unintentional effects (e.g., add/run consistency & integrity tests)
    • Testing Instructions: Confirm the change addresses the issue(s)
    • Focus Areas: Complex logic or queries that need extra attention
  • Merge any relevant open PRs into this PR

Changelog

  • Draft changelog for PR
  • Final changelog for release review

@fivetran-catfritz fivetran-catfritz self-assigned this Aug 25, 2025
{% macro default__remove_string_from_numeric(column_name) %}

cast(regexp_replace(cast({{ column_name }} as {{ dbt.type_string() }}), '[^0-9.]*', '') as {{ dbt.type_numeric() }})
cast(nullif(regexp_replace(cast({{ column_name }} as {{ dbt.type_string() }}), '[^0-9.]*', ''), '') as {{ dbt.type_numeric() }})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In our internal data, one example was malformed, so this prevents a run failure.


final as (

order_seq as (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general approach is to group the events based on the prior placed order event, than use the group to assign the touch_id.

order_seq.*,
case
when
lower(order_seq.type) in ('fulfilled order', 'fulfilled partial order', 'delivered shipment', 'marked out for delivery', 'confirmed shipment', 'refunded order')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will ask the customer for feedback on what event types should be tied to a placed order, but these seemed reasonable to me for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fivetran-catfritz These make sense to me, but are they standard event types from Klaviyo? Or is there a list you referenced to find these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are from this list. I added a comment like you did elsewhere so we don't lose it!

Copy link
Contributor

@fivetran-jamie fivetran-jamie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, couple of questions

order_seq.*,
case
when
lower(order_seq.type) in ('fulfilled order', 'fulfilled partial order', 'delivered shipment', 'marked out for delivery', 'confirmed shipment', 'refunded order')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fivetran-catfritz These make sense to me, but are they standard event types from Klaviyo? Or is there a list you referenced to find these?

CHANGELOG.md Outdated
[PR #53](https://github.com/fivetran/dbt_klaviyo/pull/53) includes the following updates:

## Bug Fixes
- Updated attribution for order lifecycle events. `Fulfilled Order` and similar order-related events now inherit the touch from their corresponding `Placed Order`, rather than the nearest intervening event.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be good to include all affected event types

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Added.

else order_seq.last_touch_id
end as adjusted_last_touch_id
from order_seq
left join placed_index
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should place some sort of time limit, like the 'placed order' event needs to have happened within the previous X days/weeks/months. Just in case 'fulfilled order' or any of these other event types were logged without an order placement for some reason (not sure how possible/common this is though).

@fivetran-catfritz thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I added line 135 and set it at 3 months for now. I tested it again and no change as expected in the case I was looking at. What do you think?

Copy link
Contributor Author

@fivetran-catfritz fivetran-catfritz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comments @fivetran-jamie. Made some updates!

order_seq.*,
case
when
lower(order_seq.type) in ('fulfilled order', 'fulfilled partial order', 'delivered shipment', 'marked out for delivery', 'confirmed shipment', 'refunded order')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are from this list. I added a comment like you did elsewhere so we don't lose it!

CHANGELOG.md Outdated
[PR #53](https://github.com/fivetran/dbt_klaviyo/pull/53) includes the following updates:

## Bug Fixes
- Updated attribution for order lifecycle events. `Fulfilled Order` and similar order-related events now inherit the touch from their corresponding `Placed Order`, rather than the nearest intervening event.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Added.

else order_seq.last_touch_id
end as adjusted_last_touch_id
from order_seq
left join placed_index
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I added line 135 and set it at 3 months for now. I tested it again and no change as expected in the case I was looking at. What do you think?

Copy link
Contributor

@fivetran-jamie fivetran-jamie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two suggestions that shouldn't block approval!

-- list of possible Shopify order events --> https://help.klaviyo.com/hc/en-us/articles/115005080447
lower(order_seq.type) in ('fulfilled order', 'fulfilled partial order', 'delivered shipment', 'marked out for delivery', 'confirmed shipment', 'cancelled order', 'refunded order')
and order_seq.placed_order_group > 0
and {{ dbt.datediff('placed_order_index.placed_order_occurred_at', 'order_seq.occurred_at', 'month') }} <= 3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am 99.9% sure that if placed_order_index.placed_order_occurred_at is null this will work OK and the clause will be treated basically as false, but perhaps we wanna add a coalesce like this to be safe?

coalesce({{ dbt.datediff('placed_order_index.placed_order_occurred_at', 'order_seq.occurred_at', 'month') }}, 100) <= 3

I'll leave that up to you 🤓

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh true--better to be safe. Added!

CHANGELOG.md Outdated
[PR #53](https://github.com/fivetran/dbt_klaviyo/pull/53) includes the following updates:

## Bug Fixes
- Updated attribution for Shopify order lifecycle events. The following event types now inherit the touch from their corresponding `Placed Order` event, rather than the nearest intervening event.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last changelog suggestion: Include the 3-month timeframe guardrail you just added

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!

@fivetran-catfritz fivetran-catfritz added the pre-release Triggers the auto-releaser workflow. label Aug 26, 2025
@fivetran-catfritz fivetran-catfritz added pre-release Triggers the auto-releaser workflow. and removed pre-release Triggers the auto-releaser workflow. labels Aug 26, 2025
@fivetran-catfritz fivetran-catfritz added pre-release Triggers the auto-releaser workflow. and removed pre-release Triggers the auto-releaser workflow. labels Sep 2, 2025
@fivetran-catfritz fivetran-catfritz added pre-release Triggers the auto-releaser workflow. and removed pre-release Triggers the auto-releaser workflow. labels Sep 2, 2025
Copy link
Contributor

@fivetran-jamie fivetran-jamie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

Copy link
Contributor

@fivetran-jamie fivetran-jamie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking really good. Couple of minor code suggestions and some documentation comments/questions

CHANGELOG.md Outdated

| Data Model(s) | Change type | Old | New | Notes |
| ---------- | ----------- | -------- | -------- | ----- |
| `klaviyo__events`<br>`int_klaviyo__event_attribution`<br>`stg_klaviyo__event` | New column | | `event_attribution` | New field sourced from `property_attribution` in the `EVENT` source table. Contains Klaviyo's native attribution data for events. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be good to briefly highlight here that we're now using this field by default for attribution (that's the big breaking change)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

DECISIONLOG.md Outdated
Comment on lines 18 to 19
- Events with `property_attribution` are used directly
- Events without it inherit from parent events via `attributed_event_id`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lil unclear to me

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that. reworded!

DECISIONLOG.md Outdated
Comment on lines 47 to 49
| `klaviyo__email_attribution_lookback` | `120` | Lookback window (hours) for email attribution |
| `klaviyo__sms_attribution_lookback` | `24` | Lookback window (hours) for SMS attribution |
| `klaviyo__eligible_attribution_events` | `['email open', 'email click', 'sms open']` | Event types eligible for attribution |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might wanna note here that these ones are only applicable when using_native_attribution is False

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

Comment on lines 191 to 197
{% if using_native_attribution %}
left join inherited
on events.unique_event_id = inherited.unique_event_id
{% else %}
left join session_calculated
on events.unique_event_id = session_calculated.unique_event_id
{% endif %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotta join on source_relation

CHANGELOG.md Outdated
- For background on why this method was chosen, see the [DECISIONLOG.md](https://github.com/fivetran/dbt_klaviyo/blob/main/DECISIONLOG.md).

## Breaking Changes
- **Native attribution is now enabled by default** (`using_native_attribution: true`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't 100% accurate, right? The default is really determined by whether you have the property_attribution field (if you do, then yah using_native_attribution is basically true)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. removed.

@fivetran-catfritz fivetran-catfritz added docs:ready Triggers the docs generator workflow. and removed docs:ready Triggers the docs generator workflow. labels Sep 16, 2025
Copy link
Contributor

@fivetran-jamie fivetran-jamie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Couple of documentation comments that shouldn't block this moving forward. Approved as long as those are addressed

DECISIONLOG.md Outdated
Comment on lines 18 to 19
- Events with `property_attribution` are used directly
- Events without it use the following session-based attribution
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kinda sounds like we're talking about singular event records instead of the EVENT table overall

@fivetran-catfritz fivetran-catfritz added docs:ready Triggers the docs generator workflow. and removed docs:ready Triggers the docs generator workflow. labels Sep 17, 2025
@fivetran-catfritz fivetran-catfritz added docs:ready Triggers the docs generator workflow. and removed docs:ready Triggers the docs generator workflow. labels Sep 17, 2025
Copy link
Contributor

@fivetran-joemarkiewicz fivetran-joemarkiewicz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with just a few small notes that don't require blocking approval. Once the update is applied then this will be ready to go!

{"name": "uuid", "datatype": dbt.type_string()},
{"name": "property_value", "datatype": dbt.type_string()}
{"name": "property_value", "datatype": dbt.type_string()},
{"name": "property_attribution", "datatype": dbt.type_string()}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be called out as a breaking change since a user could have been passing this field through as a passthrough column.

@fivetran-catfritz fivetran-catfritz merged commit e0d58be into main Sep 17, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs:ready Triggers the docs generator workflow.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants