SaaS APIs have been getting more “JSON-shaped” every year. The early days were simple: a record was mostly flat, and integrating meant translating `firstname` to `first_name` and calling it a day. Today, _the real data_ often lives one or two (or five) levels down: `user.profile.email`, `properties.custom_fields.priority`, `metadata.owner.id`, and so on. And the moment you want a reliable, real-time, two-way sync (read/subscribe **and** write), the glue code starts to sprawl: custom parsers, edge-case handling for weird field names, one-off transforms for each provider, and endless “why didn’t this update?” debugging. 📢📢 That’s why today we’re launching **nested field mappings** in Ampersand. We want to ensure enterprise agent builders can build deep integrations where nested data from Systems of Record is table stakes. # What’s new: Nested field mappings (read + write) using JSONPath bracket notation Ampersand now supports mapping to and from nested fields using **JSONPath bracket notation**, so you can work with nested data structures when reading from provider APIs _and_ writing back. Docs are [here](https://docs.withampersand.com/object-and-field-mapping#nested-field-mapping). Use this format to access nested values: ``` $['parentField']['childField'] ``` Bracket notation is also the safest way to reference field names that contain special characters like dots or spaces (which show up constantly in real-world SaaS schemas). And importantly: **this works both directions**. - **Reading:** extract nested provider values into mapped fields - **Writing:** reverse the same mapping when sending data back to the provider ## Why bracket notation (and not just dots)? Dot notation breaks down fast in the real world: - Some providers embed dots _in_ field names - Custom fields can include spaces, punctuation, or provider-specific prefixes - You end up writing fragile escaping logic Ampersand’s nested mapping support uses bracket notation, so those field names are parsed correctly and consistently. ## A concrete example: Read nested data, deliver clean mapped fields Say a provider returns this payload: ```json { "userInfo": { "email": "john@example.com", "name": "John Doe" }, "status": "active" } ``` You can define field mappings like: ```yaml read: objects: - objectName: contacts requiredFields: - mapToName: contactEmail fieldName: $['userInfo']['email'] - mapToName: contactName fieldName: $['userInfo']['name'] ``` Ampersand delivers the normalized output under `mappedFields`: ```json { "mappedFields": { "contactEmail": "john@example.com", "contactName": "John Doe" } } ``` This “provider shape in, product shape out” behavior is exactly what mappings are for, and nested mappings make it work when the provider isn’t flat. ## The best part: the same mapping applies in reverse when you write When you write back, Ampersand applies the same mapping **in reverse**, so your app can keep speaking in the stable schema you chose, while the provider receives the schema it expects. [Ampersand+1](https://docs.withampersand.com/object-and-field-mapping) Using the docs’ nested read/write mapping [example](https://docs.withampersand.com/object-and-field-mapping#reading-nested-data), notice something subtle and powerful: you can also use bracket notation on the _destination side_ (your `mapToName`) to produce nested output structures. ```yaml - mapToName: $['moreInfo']['currentStatus'] fieldName: status ``` Now your app can send: ```json { "moreInfo": { "currentStatus": "deleted" } } ``` …and the provider receives: ```json { "status": "deleted" } ``` Meanwhile nested provider fields like `userInfo.email` and `userInfo.name` map back into `userInfo` on the provider side during writes. # Object + field mapping: the integration primitive that scales Nested field mappings are the newest capability, but they sit on top of a broader (and increasingly essential) foundation: **object mapping** and **field mapping**. Ampersand supports both, and you can either: - **Predefine** mappings in your manifest (`amp.yaml`) - Or **prompt end users** to define mappings via Ampersand UI components (for custom fields, org-by-org differences, etc.) ## Object mapping Object mapping lets you rename provider objects into your own vocabulary—useful for enforcing conventions, or building a unified API across multiple providers. Example: map Salesforce `account` and HubSpot `companies` into your product’s canonical `company`. Ampersand delivers results under the mapped object name while still preserving the provider’s original name as `rawObjectName`. ## Field mapping Field mapping does the same at the field level—standardizing keys and insulating your app from provider-specific naming. And because Ampersand can apply the mapping in reverse for writes (when you inherit mappings), mapping becomes more than a “read transform”—it becomes a two-way contract. ## User-defined mappings (for custom fields) Customers don’t all use the same CRM schema. One org stores “notes” in `field_a`, another uses `field_b`. User-defined field mappings let you ask each customer to map _their_ field into _your_ concept, once, at install time—then you always receive it under the same canonical name. Ampersand currently supports user-defined **field** mappings (not object mappings yet). Please let us know if you need this feature and the business use cases that you want them for! ## Dynamic field mappings Sometimes the set of fields to map isn’t even fixed—you may need to generate it per customer, per workflow, or per product tier. Ampersand supports dynamic field mappings by passing a `fieldMapping` configuration into the `InstallIntegration` UI component, and these dynamic mappings are optional (users can install without mapping). ## Value mappings (including bracket notation support) Mappings aren’t only about _field names_. They’re also about _value vocabularies_—especially for enums like lifecycle stage, priority, lead source, etc. Ampersand supports value mappings (one-to-one), configured through the UI flow. And value mappings support bracket notation as well—so nested keys don’t block you from mapping nested data cleanly. # Why mappings matter even more in the age of AI: determinism + authorization AI is changing how integrations get built and operated. Teams are moving from “a developer writes a transformation” to: - agents generating connector code, - LLMs proposing field mappings from schema introspection, - copilots drafting sync logic, - autonomous workflows taking actions on customer systems. That’s exciting, and also exactly where integration complexity becomes dangerous. ## Determinism: a stable contract that the model can’t “creatively reinterpret” LLMs are probabilistic. Your integration shouldn’t be. Object and field mapping is the deterministic layer that turns “best effort schema guessing” into “this is the contract.” Once a mapping is set: - Reads always land in the same canonical shape (`mappedFields`) - Writes always reverse the same mapping back to provider fields Nested mappings extend that determinism to the parts of the payload that previously required bespoke parsing code. No more “sometimes it’s `user.email`, sometimes it’s `userInfo.email`” scattered through your app, your app talks to **your** schema, and Ampersand handles the rest. ## Authorization: mappings define _what data is allowed to move_ In modern integrations, authorization isn’t only “can we connect?” It’s also: - Which objects are we allowed to sync? - Which fields are we allowed to read? - Which fields are we allowed to write back? - What happens if a customer doesn’t have (or doesn’t want to map) a field? Ampersand’s read configuration distinguishes **required** vs **optional** fields, where required means every installing user must grant access, and optional means the user can choose whether to grant access. On the write side, inheritance and mapping rules give you a clean, enforceable boundary: you write in terms of your canonical mapped fields, and Ampersand routes that to the correct provider fields—_or strips fields that weren’t mapped to prevent provider errors_ (and, practically speaking, to prevent accidental writes into undefined/unapproved fields). In an AI-driven world where actions can be suggested (or even taken) automatically, that boundary becomes the difference between: - **a controlled system** that can be audited and reasoned about, - and **a fuzzy system** where the model’s guess becomes production behavior. ## Nested objects: the reality of SaaS data Nested data shows up everywhere: - “properties” objects wrapping actual values - “metadata” wrappers with IDs and provenance - field groups like `userInfo`, `billingAddress`, `owner`, `attributes` - special-character field names that are hard to address reliably Nested field mappings let you: - **pull values out of provider nesting** using `$['a']['b']` - **write values back into provider nesting** using the same mapping reversed - **emit nested output** on your side by using bracket notation in `mapToName` This is a big step toward integrations that preserve structure instead of flattening everything into an ad hoc key soup. ## The association graph: integrations aren’t tables, they’re relationships Even if every record were flat, integrations still wouldn’t be. A CRM isn’t “a list of contacts.” It’s a graph: - contacts associated with companies - deals associated with contacts and companies - tickets associated with contacts - activities associated with deals - custom objects linked across multiple entity types If you want your product to behave like a first-class citizen in your customer’s system, you have to model, and sync, those relationships. That’s the **association graph**: nodes are records, edges are associations, and your integration’s job is to keep those edges accurate over time. Ampersand exposes a couple of key primitives that map naturally onto this: ## Writing edges: create associations at write time (HubSpot) When creating records via the Write API, you can include an `associations` parameter to create linked records, currently supported for HubSpot. That’s not just convenience. It’s the difference between: 1. create record, then 1. do extra calls to connect it, then 1. reconcile failures… …versus treating “create node + connect edge” as a single declarative intent. ## Reacting to edge changes: association update events (HubSpot subscribe) On the eventing side, Ampersand’s subscribe webhooks include a normalized `subscribeEventType` that can be `create`, `update`, `delete`, and **`associationUpdate` (HubSpot only)**—so you can react when relationships change, not just when fields change. ## Where nested field mappings fit Associations are often represented as nested structures in API payloads (or at least nested metadata pointing to IDs). Nested field mappings let you reliably access and emit those nested relationship signals without writing custom traversal logic for each provider. Put differently: - **Nested mappings** let you address structure inside a node - **Association primitives/events** let you model edges between nodes - Together, they make the “graph” first-class in your integration architecture # Summary Nested field mappings are a deceptively small feature with an outsized impact: - They remove a major source of custom glue code for nested provider payloads - They make your “canonical schema” truly end-to-end (read + write) - They increase determinism in a world where AI systems are otherwise probabilistic - They strengthen authorization boundaries by keeping data movement explicit and controlled - They complement the association graph view of integrations, because real systems are networks, not tables For the full technical details and examples, see Ampersand’s [Object and field mapping documentation.](https://docs.withampersand.com/object-and-field-mapping#nested-field-mapping) We cannot wait to see what you build with it.