Object Oriented Marketing: What SFMC and Braze Teach Us About the Next Engagement Platform

Home / MarTech / Object Oriented Marketing
Daniel Robles Published Platform architecture Composable

On October 20, Salesforce signed a definitive agreement to acquire Braze for roughly $5.66 billion. The company that owns the platform most of us grew up on, Marketing Cloud, is buying the platform most of us moved to. The official line is that the roadmaps will merge into something better. Sure. Marketing Cloud still looks and feels like ExactTarget did fifteen years ago, and it is still a pain to use. I am not holding my breath for Braze. Braze already has its own unfinished integration: OfferFit, now rebranded as BrazeAI Decisioning Studio, still takes data over SFTP. In 2026. So before anything else gets “embedded,” it is worth writing down what each platform taught us: how data gets in, how it gets used, where each one hurts, and what a modern engagement layer should look like if we stop shopping for suites and start designing around objects.

TABLEOBJECTSpersoneventproductoffercanvasbriefconsent
Hover (or tap) the table and each object to see the data behind it. Blue objects Braze lets you read and extend today; orange ones it does not.

1. Two platforms, one owner

ExactTarget was a top-tier CRM and ESP tool of its time. Salesforce bought it in 2013, it became Marketing Cloud, and for close to a decade it was the default answer for any enterprise lifecycle program. Data extensions, AMPscript, Automation Studio and Journey Builder were the vocabulary of the job.

Today, Braze is widely considered the best customer engagement platform, especially for app-first and digital-first brands. It is real time, it is approachable for non-technical marketers, and it treats the customer as a living profile rather than a row. Per the Salesforce announcement, the deal is $47.50 per share in cash and is expected to close in Salesforce's fiscal 2028, subject to shareholder and regulatory approval. Braze brings roughly 2,770 customers with it and, per its fiscal 2026 results, $738.2 million in annual revenue and more than $800 million in annual recurring revenue. That averages out to roughly $300K per customer, but the money is concentrated: the 333 customers paying $500K or more a year account for 64% of recurring revenue, which works out to about $1.5 million each. Salesforce is not buying a long tail of small senders. It is buying a few hundred enterprise engagement contracts.

WHAT SALESFORCE IS BUYING$5.66Benterprise value$738.2MFY2026 revenue$800M+annual recurring rev.~2,770customersWHERE THE MONEY IScustomers333 of 2,609 customers (~13%)ARR64% of ARR (~$512M)blue = customers paying $500K+ a year~$300Kavg ARR per customer~$1.5Mavg for the top 333Sources: Salesforce announcement (Oct 20, 2026); Braze FY2026 results (fiscal year ended Jan 31, 2026).
About 13% of Braze customers bring in 64% of its recurring revenue. Hover or tap any box for details.

I have run both. I have my own qualms with Braze's architecture and with the direction of its recent acquisitions (OfferFit in 2025, and now being acquired itself). One example: six years ago I submitted a concept to Braze for exposing and extending the campaign and Canvas objects through the API (here is the gist). Six years later, the endpoint has seen minor changes and the core gap is still there. More on that in section 5. None of that changes the core lesson. The two platforms embody two different data models, and the model you operate on decides how your team spends its week.

2. Tables versus objects

Felipe Díaz put this well in a recent LinkedIn post, “Where's my data extension?”. If you come from SFMC you think in tables. Braze does not have one. Go read his post; it is the cleanest explanation of the mental shift I have seen. I want to take the idea one step further: from data objects to marketing objects.

The same shift applies to how messages get started. SFMC trained us on triggered sends: an application calls the ESP and says “send email 123 to this person.” Object-oriented platforms make something better possible, which I call Marketing Eventing: systems publish facts (“this appointment was a no-show”), and the marketing layer decides what, if anything, to do about them.

A caveat on where that marketing layer lives. Putting every decision inside Braze or SFMC is still suite thinking, and I avoid it when I can. In a composable stack the ESP should be close to a dumb pipe: it renders and delivers. Audience logic, eligibility and next-best-action belong upstream in the CDP or the orchestration layer, where every channel and vendor can share them and where swapping the ESP does not mean rebuilding your marketing brain. See Migrating from a Suite to a Composable Stack and The Perfect MarTech Stack.

Table-oriented (SFMC)Object-oriented (Braze)Rows you must keep in syncProfiles that update themselvesSubscribers_DEContactKeyEmailStatusOrders_Nightly_DEContactKeySKUTotalSendable_Audience_DEContactKeyEmailLastSKUSQL QueryAutomation Studioruns 02:00 nightlyJoin, overwrite, schedule, repeat.User profile external_id: u_1042attributes first_name: "Ana" plan_tier: "premium" last_sku: "sku_44"events appointment_booked appointment_no_showpurchases sku_44 $39.00subscriptions email: opted_in push: subscribedCatalogproductsid: sku_44nameimage_urlpricecategoryThe person and the things, joined at send time.
In SFMC the audience is a table you rebuild. In Braze the audience is a set of profiles that update themselves, and the catalog is joined at send time. Hover or tap any box for details.
EFFORT TO PUT ONE PRODUCT NAME IN AN EMAILSFMC1import / API to DE2SQL query activity3schedule automation4sendable DE + relation5AMPscript Lookup()5 steps | 4 things to maintain | as fresh as the last runBraze1/users/track or CDI2Liquid catalog_items2 steps | 1 profile + 1 catalog | real time
The tax of each model: what it takes to get one value from your data into a message. Hover or tap any box for details.
  • SFMC is table oriented. Every audience, lookup and personalization source is a data extension. Keeping those tables correct is a job: imports, SQL query activities, schedules, and somebody who knows which automation feeds which journey.
  • Braze is object oriented. The user profile is a JSON object. Attributes describe what is true, events describe what happened, purchases and subscriptions hang off the same object. Catalogs hold the things (products, offers, clinics, content) and Liquid joins them at render time.

That shift is why Braze scales without a fleet of automations whose only job is to keep data extensions alive. You update the object; every segment, Canvas and message that reads it sees the change.

Braze is not the only platform built this way. If you are building an evaluation shortlist of profile-and-event engines, start here:

Platform Model Best fit
IterableUser profiles, custom events, catalogsCross-channel B2C; closest like-for-like Braze alternative
Customer.ioPeople, events and objects with relationshipsProduct-led and technical teams; strong API and data pipelines
KlaviyoProfiles, metrics (events) and catalogsE-commerce, especially Shopify-centric brands
MoEngageUser profiles and events with real-time segmentsApp-first brands, strong in APAC and EMEA
CleverTapUser profiles and eventsMobile apps, retention and in-app engagement
Bloomreach EngagementCustomer profiles, events and product catalogsCommerce brands that want search, merchandising and messaging together
AirshipChannels and events around app usersMobile push, in-app and app experiences
Hightouch / MessageGearsWarehouse-native: the warehouse is the profileTeams that want zero copies of customer data in the engine

They differ in the parts that matter most for this article: how far you can extend objects beyond the user, how much campaign metadata the API exposes, and whether data has to be copied in at all.

3. Salesforce Marketing Cloud: how data gets in and gets used

SFMC exposes a REST API and an older SOAP API. Authentication is OAuth client credentials against a tenant-specific subdomain. Data lands in data extensions through file imports, Marketing Cloud Connect (synchronized data extensions from Sales or Service Cloud), or the API. From there, SQL query activities in Automation Studio reshape it into sendable tables that journeys and sends read.

Authenticate

POST https://{subdomain}.auth.marketingcloudapis.com/v2/token
{
  "grant_type": "client_credentials",
  "client_id": "<client_id>",
  "client_secret": "<client_secret>",
  "account_id": "<mid>"
}

Upsert rows into a data extension

POST https://{subdomain}.rest.marketingcloudapis.com/hub/v1/dataevents/key:Orders_DE/rowset
[
  {
    "keys":   { "ContactKey": "u_1042", "OrderId": "o_88123" },
    "values": { "SKU": "sku_44", "Total": 39.00, "OrderDate": "2026-10-30T15:02:11Z" }
  }
]

Fire a Journey Builder entry event

POST https://{subdomain}.rest.marketingcloudapis.com/interaction/v1/events
{
  "ContactKey": "u_1042",
  "EventDefinitionKey": "APIEvent-appointment-no-show",
  "Data": {
    "ContactKey": "u_1042",
    "EmailAddress": "ana@example.com",
    "AppointmentId": "apt_5521",
    "ClinicName": "Downtown"
  }
}

The biggest blocker to real-time 1:1 personalization in SFMC: journey data is a snapshot

Whatever you send in Data is written into the journey's entry data extension at the moment of entry and frozen there. If the appointment is rescheduled an hour later, the patient rebooks, the price changes or the product sells out, the journey keeps acting on the old row. Every decision split, wait step and message downstream reads stale facts.

The workarounds are all more tables: contact data through Data Designer attribute groups (only as fresh as the data extensions behind them), AMPscript lookups against tables another automation refreshes, or exit criteria that re-check a data extension on a schedule. Each one moves you back to batch. You cannot deliver a true 1:1 message from data that stopped updating when the person entered the journey.

Braze is not fully immune. canvas_entry_properties are also captured at entry. The difference is that the profile itself stays live: attributes, events and catalog lookups are read at send time, so the right design is to keep changing facts on the object and pass only immutable context (an appointment ID) at entry.

Use it in a message

%%[
  SET @sku  = AttributeValue("LastSKU")
  SET @name = Lookup("Products_DE", "Name", "SKU", @sku)
  SET @img  = Lookup("Products_DE", "ImageUrl", "SKU", @sku)
]%%
Still thinking about %%=v(@name)=%%?

New to AMPscript, or want to compare it with Braze Liquid? Try the interactive AMPscript 101 and Liquid 101 lessons, each with a cheat sheet and a playground that runs against the same customer.

Where SFMC hurts

  • Data maintenance is the product. Most of the work is keeping data extensions correct: imports, query activities, overwrite versus update actions, retention policies, and the schedules that tie them together.
  • Batch by default. Audiences are as fresh as the last automation run. Query activities time out after 30 minutes, so large joins get split, staged and chained.
  • Hidden dependency graph. Which automation feeds which DE, which DE feeds which journey, which journey sends which email: none of it is a first-class object you can query. Teams keep it in spreadsheets and tribal memory.
  • The contact model is hard to reason about. Contacts, subscribers, All Subscribers, sendable relationships, Contact Builder attribute groups and business units all overlap.
  • Two APIs and uneven coverage. Some operations exist only in SOAP, some only in REST, and many configuration objects are not exposed cleanly at all.
  • Real time is bolted on. Entry events and transactional sends exist, but journey data freezes at entry and personalization still reads from tables that a batch process maintains. This is the single biggest obstacle to true 1:1 messaging.

Where SFMC is still strong

  • Native relationship with Sales and Service Cloud for B2B and high-touch B2C.
  • Mature email deliverability tooling and dedicated IP management at enterprise scale.
  • SQL is a superpower when you have someone who can write it: any shape of audience is possible.
  • Business units, roles and audit features that large regulated enterprises expect.

4. Braze: how data gets in and gets used

Braze is REST-first with API keys scoped by permission. Data arrives from SDKs (web, iOS, Android), from /users/track, from Cloud Data Ingestion (direct syncs from Snowflake, BigQuery, Redshift or Databricks), or from a CDP. Every write lands on the user object. Segments are evaluated against the current state of those objects, Canvases are triggered by events or schedules, and Liquid reads the profile, the triggering event, catalogs and Connected Content at send time. Currents streams engagement events back to the warehouse.

Animation: in SFMC data moves from source to import, staging data extension, SQL query, sendable data extension and journey entry over hours; in Braze an app event updates the profile and triggers a Canvas message in seconds
Same trigger, two data models. Timings are illustrative; the number of hops is the point.

Update the object

POST https://rest.iad-0X.braze.com/users/track
Authorization: Bearer <REST_API_KEY>
{
  "attributes": [
    { "external_id": "u_1042", "plan_tier": "premium", "last_sku": "sku_44",
      "preferred_clinic": "downtown" }
  ],
  "events": [
    { "external_id": "u_1042", "name": "appointment_no_show",
      "time": "2026-10-30T15:00:00Z",
      "properties": { "appointment_id": "apt_5521", "clinic_id": "downtown" } }
  ],
  "purchases": [
    { "external_id": "u_1042", "product_id": "sku_44", "currency": "USD",
      "price": 39.00, "time": "2026-10-30T15:02:11Z" }
  ]
}

One request, three kinds of change, one object. Each array holds up to 75 objects per request. Send changes, not full re-syncs; on older contracts, attribute writes still consume billable data points.

Hold the things in a catalog

POST https://rest.iad-0X.braze.com/catalogs/products/items
{
  "items": [
    { "id": "sku_44", "name": "Daily Multivitamin", "price": 39.00,
      "image_url": "https://cdn.example.com/sku_44.png", "category": "wellness" }
  ]
}

Join the person and the thing at send time

{% catalog_items products {{custom_attribute.${last_sku}}} %}
Still thinking about {{ items[0].name }}?
<img src="{{ items[0].image_url }}" alt="{{ items[0].name }}">

Same job as the AMPscript Lookup() above, against an object instead of a table. Try it in the Liquid playground, or keep the Liquid cheat sheet handy.

Trigger a Canvas from your own system

POST https://rest.iad-0X.braze.com/canvas/trigger/send
{
  "canvas_id": "<canvas_id>",
  "recipients": [
    { "external_user_id": "u_1042",
      "canvas_entry_properties": { "appointment_id": "apt_5521", "reschedule_url": "https://..." } }
  ]
}

Or sync from the warehouse with Cloud Data Ingestion

-- Source table Braze reads on a schedule; only rows newer than the last sync are applied
EXTERNAL_ID | UPDATED_AT           | PAYLOAD
u_1042      | 2026-10-30 15:05:00  | {"ltv_band": "high", "churn_risk": 0.12, "next_refill_date": "2026-11-14"}

Where Braze is strong

  • Real-time, event-driven segmentation and triggers without batch plumbing.
  • A Canvas builder that non-technical marketers can actually own.
  • One profile across email, push, SMS, RCS, in-app, content cards, banners and webhooks.
  • Catalogs, Liquid and Connected Content cover most personalization without a lookup table.
  • Currents and warehouse ingestion make it a good citizen in a composable stack.

Where Braze hurts

  • Easy to start, easy to sprawl. The same approachability that makes Braze great for non-technical teams lets a workspace grow without a plan. It still needs up-front architecture: attribute naming, event taxonomy, identity resolution, catalog design, teams, tags and ownership. When I joined Hims & Hers I inherited exactly that: a Braze instance that had grown fast and without a plan. My first months were spent on data architecture and governance, not campaigns.
  • Both platforms have charged for the data you bring in. For years Braze billed on data points: every custom attribute update, custom event and purchase counted. That was Braze's version of SFMC's data extension storage entitlement (1 GB to 100 GB depending on edition, sold alongside metered automation runs and API calls). SFMC still meters that way. Braze stopped billing new customers on data points in Spring 2025 and dropped data point caps from its user-based pricing, although older contracts may still carry them. Even with the meter gone, Braze is no dumping ground: the habits it taught (send changes, not full re-syncs; model the data in the warehouse first) are still the right ones.
  • Campaigns and Canvases are black boxes to the API. You can create and extend user attributes and event properties. You cannot inspect or extend the objects marketers build every day: who owns a Canvas, which segment and filters gate entry, what the send settings are, or which brief and approval it belongs to.
  • Lineage is missing. There is no first-class graph of which Canvas uses which segment, attribute, catalog or Content Block. Deprecating an attribute is a search exercise.
  • Acquired, not integrated. Braze bought OfferFit in 2025 and rebranded it BrazeAI Decisioning Studio, but architecturally it still sits beside Braze rather than inside it. In my experience: a minimal UI with heavy reliance on Braze's own team, SFTP-based data ingestion, a second copy of your data stored inside Braze, and setup cycles that can run a full quarter. Now Braze itself is being folded into a bigger suite. That pattern is good for bundling and not obviously good for teams that want a sharp engagement engine with clean APIs.

5. The campaign is an object too

Braze got the customer right: the customer is an object, and admins can extend it. It stopped there. The Canvas, the campaign and the segment are also objects, and they are the ones a lifecycle team actually manages. Today they are closed. Here is what the export endpoint returns for a Canvas (GET /canvas/details), trimmed:

{
  "created_at": "2024-02-20T00:00:00Z",
  "updated_at": "2024-02-20T00:00:00Z",
  "name": "Canvas Example",
  "description": "Example canvas description",
  "archived": false,
  "draft": true,
  "schedule_type": "action",
  "first_entry": "2024-02-21T00:00:00Z",
  "last_entry": "2024-04-01T00:00:00Z",
  "channels": ["email", "push"],
  "variants": [{ "name": "Variant A", "id": "variant_a_123", "first_step_id": "step_1" }],
  "tags": ["test", "canvas"],
  "teams": ["martech"],
  "steps": [{ "name": "Welcome Email", "type": "email", "id": "step_1",
              "next_paths": [{ "name": "Yes", "next_step_id": "step_2" }] }],
  "message": "success"
}

It tells you the shape of the Canvas. It does not tell you who built it, who changed it, who it targets, how people get in, or how sends are throttled. You cannot audit a program of hundreds of Canvases from this, and you cannot build governance, QA or reporting tooling on top of it.

I first submitted this concept to Braze six years ago: the response I wanted instead, a v2 of the same endpoint. It is shared as a public gist. Abridged:

{
  "canvas_details": {
    "created_by": "email_address",
    "created_on": "2024-02-20T00:00:00Z",
    "modified_by": "email_address",
    "modified_on": "2024-02-20T00:00:00Z",
    "link_to_canvas_in_ui": "url"
  },
  "basics": {
    "name": "canvas name",
    "team": "martech",
    "tags": "test canvas",
    "conversion_events": { "1": "purchase", "2": "visit", "3": "share", "4": "add" }
  },
  "entry_schedule": {
    "type": "action",
    "type_base_options": {
      "action_based_options": {
        "logic_groups": [
          { "type": "Perform Custom Events", "metadata": { "name": "APPOINTMENT_NO_SHOW" } },
          { "type": "Email Address", "metadata": { "logic": "contains regex", "value": "@forhims.com" } },
          { "type": "Make Purchase", "metadata": { "logic": "Make Any Purchase" } }
        ]
      },
      "scheduled_based_options": {
        "logic_groups": [[
          { "join": null, "logic": "X Custom Event In Y Day",
            "event_name": "APPOINTMENT_CANCELLED", "timing": "exactly 4 times in the last day" },
          { "join": "or", "logic": "Segment Membership", "inclusion": true, "segment_name": "pierluc_test" }
        ]]
      }
    },
    "entry_window": { "start_time": {}, "end_time": {}, "time_zone": {} }
  },
  "target_audience": {
    "segments": { "segment_objects": {} },
    "additional_filters": {},
    "entry_controls": {
      "allow_users_to_renter": true,
      "reeligibility_timeframe": { "type": "specified_window", "value": 5, "measure": "days" },
      "limit_people": true
    }
  },
  "send_settings": {
    "subscription_settings": "all",
    "rate_limit": 10000,
    "frequency_capping": 100000,
    "quiet_hours_start": "2000",
    "quiet_hours_end": "0800",
    "fallback_behavior": "abort"
  },
  "legacy_response": { "...": "the current response, unchanged, for backward compatibility" }
}

Everything in that payload already exists in the Braze UI. Exposing it turns a Canvas from a picture into data: you can lint entry criteria, catch a missing quiet-hours window before launch, find every Canvas that depends on a segment, and report on who owns what.

Then let admins extend the object

Reading is half of it. The other half is letting the platform owner extend inherent objects the same way we already extend the user profile. CRMs solved this decades ago with custom fields and lookups. Engagement platforms have not.

Animation: a Canvas system object with locked native fields gains proposed custom fields such as owner, brief_id, legal_approval, cost_center, experiment_id and jira_ticket, each linking to an external object such as an owner directory, campaign brief, compliance record or experiment registry
User and event schemas are extensible today. Campaigns and Canvases should be too.

A hypothetical API for it, in the shape Braze already uses for catalogs:

// Proposed: define custom fields on a system object (admin only)
POST /objects/canvas/fields
{
  "fields": [
    { "name": "owner",          "type": "lookup",   "target": "dashboard_users", "required": true },
    { "name": "brief_id",       "type": "lookup",   "target": "catalog:campaign_briefs" },
    { "name": "legal_approval", "type": "lookup",   "target": "catalog:approvals", "required_to_launch": true },
    { "name": "cost_center",    "type": "picklist", "values": ["Growth", "Retention", "Clinical"] },
    { "name": "experiment_id",  "type": "lookup",   "target": "catalog:experiments" },
    { "name": "jira_ticket",    "type": "url" }
  ]
}

// Proposed: set values on a specific Canvas
PATCH /canvas/{canvas_id}/fields
{ "owner": "daniel@example.com", "brief_id": "br_2026_q4_noshow",
  "legal_approval": "appr_7781", "cost_center": "Retention" }

// Proposed: query objects by their extensions
GET /canvas/list?fields.cost_center=Retention&fields.legal_approval=null

With that, the approval workflow, the brief, the experiment readout and the owner live on the Canvas instead of in a spreadsheet next to it. A launch can be blocked when legal_approval is empty. Finance can see spend by cost_center. A new team member can open a Canvas and know why it exists. That is what managing a platform looks like, as opposed to operating one.

6. Why a suite is a risk, not a strategy

A Salesforce-owned Braze may be a better product in three years. It may also be bundled, repriced and pulled toward the parent's data platform. Neither outcome is in your control. What is in your control is how tightly your operating model is coupled to any one vendor.

  • Your data model should outlive your vendor. If your event taxonomy, attribute names and audience logic only exist inside one UI, a migration is a rewrite.
  • Your campaign metadata should be yours. Owner, brief, approval, experiment and cost center belong in a system you control, synced into the engine, not trapped in it.
  • Bundles hide cost. Suite pricing makes it cheaper to add the next module and more expensive to leave any of them.
  • Roadmaps follow the parent. Salesforce already says Braze is expected to complement Data 360 and Agentforce and to “strengthen Marketing Cloud's cross-channel personalization.” That is integration work, and your API requests compete with it.

This is the same argument I made in Migrating from a Suite to a Composable Stack. The difference now is that the best engagement engine on the market is about to become part of a suite.

7. Object Oriented Marketing

“Composable” describes the stack. I use Object Oriented Marketing to describe how the team operates on top of it. The idea is borrowed from software: define the objects, give them clear interfaces, and let different engines act on them. Engines are replaceable. Objects are not.

With a CDP: one brain, many pipesSourcesProduct appCommerceCRMSupportClinical / opsData + identityWarehouseIdentity graphReverse ETL / CDIEvent streamObject layer (yours)PersonEventProductOfferCampaignBriefApprovalCDP: decide + orchestrateAudiencesRules + eligibilityAI decisioningJourney orchestrationArbitration + frequencyExperimentsDelivery (dumb pipes, rented)BrazeSFMCTwilio / SMSPush providerAd platformsWeb / in-appChannelsEmailPushSMS / RCSIn-appWebPaidContractsSchema registryNaming + taxonomyConsent + PIIOwners per objectAPI-first metadataAudit + lineageExit plan
Without a CDP, each engine decides, orchestrates and delivers on its own, so audiences, rules and consent are rebuilt per tool and nothing arbitrates across channels. With a CDP, one system decides and orchestrates, and Braze, SFMC and the rest become delivery pipes. Either way, the object layer and its contracts are what survive a vendor change. Hover or tap any box for details.
  1. Everything is an object. Person, event, product, offer, campaign, brief, approval, experiment. Each has a schema, an owner and an ID that is stable across tools.
  2. Objects live upstream. The warehouse (or a CDP on top of it) is the system of record. Engagement platforms receive the subset they need to act.
  3. Engines act on objects; they do not define them. The CDP decides and orchestrates; Braze, SFMC or the next tool handles final delivery. Without a CDP every sender becomes its own brain, and audiences, rules and consent get rebuilt in each one. Either way, a sender should not be the only place a campaign's purpose, owner or approval is recorded.
  4. Interfaces over integrations. Every object is readable and writable through an API, and changes are published as events so other systems can react.
  5. Extend, don't fork. When a tool lacks a field, extend the object (custom field, lookup, catalog) rather than creating a shadow table no one else can see.
  6. Inheritance for programs. A “no-show recovery” template carries default entry controls, quiet hours, approval requirements and naming. Individual Canvases inherit and override, and the overrides are visible.

8. What a best-of-breed engagement platform should have

Take the best of both: SFMC's enterprise controls and query power, Braze's real-time object model and usability. Then add what neither has.

Capability SFMC today Braze today What we should expect
Customer modelTables keyed by contactExtensible profile objectProfile object, schema owned in the warehouse
FreshnessBatch, automation-drivenReal timeReal time, with warehouse sync as a first-class path
Reference dataData extensions + Lookup()Catalogs + LiquidCatalogs that can be the target of lookups from any object
Campaign objectsPartial, uneven across APIsRead-only summaryFull read/write API, custom fields and lookups
LineageNone nativeNone nativeQueryable graph: attribute → segment → Canvas → message
GovernanceBusiness units, rolesTeams, permissionsLaunch rules on object fields (approval required, owner required)
Change eventsLimitedEngagement via CurrentsConfig changes as events too: who changed what Canvas, when
UsabilityTechnicalMarketer-friendlyMarketer-friendly UI over an engineer-friendly model
PortabilityLowMediumEverything exportable as code: Canvases, segments, templates

9. What to do now

  • If you are on SFMC: inventory your automations and map which data extensions they keep alive. That map is your migration plan, whichever engine you pick.
  • If you are on Braze: do the architecture work now. Document the attribute and event taxonomy, put owners on Canvases through naming and tags, and mirror campaign metadata in your own system until the platform supports it natively.
  • Either way: move the definition of your objects upstream, keep campaign metadata in a system you own, and ask your vendor for read/write APIs on the objects you manage every day. The acquisition is a good moment to ask.

SFMC taught us to manage tables. Braze taught us to manage people as objects. The next step is to manage the whole program as objects: the customer, the things we sell, and the campaigns themselves.

Credit to Felipe Díaz for “Where's my data extension?”, which framed the table-versus-object shift better than I could. Related: Migrating from a Suite to a Composable Stack and Reverse ETL.