> ## Documentation Index
> Fetch the complete documentation index at: https://docs.custral.com/llms.txt
> Use this file to discover all available pages before exploring further.

# record.updated

> One field on a record changed. Carries the old value, the new value, and the whole record after the change.

<Warning>
  This event fires **once per field**, not once per save. A form that writes six
  fields to one record produces six deliveries, each naming a different
  `propertyId`. Debounce on `recordId` if you only care that the record moved.
</Warning>

## Payload

```json theme={null}
{
  "id": "whd_7Kc2pR4nM",
  "event": "record.updated",
  "createdAt": "2026-09-11T14:22:07.118Z",
  "data": {
    "recordId": "rec_3Ab9xK2mQ7",
    "objectId": "obj_2Zc7pL",
    "propertyId": "prop_5Nd3kR",
    "previous": ["opt_qualified"],
    "current": ["opt_negotiation"],
    "record": {
      "id": "rec_3Ab9xK2mQ7",
      "name": "Ravenna Foods",
      "stage": ["opt_negotiation"],
      "annual_value": 48000
    }
  }
}
```

<ResponseField name="recordId" type="string" required>
  The record that changed.
</ResponseField>

<ResponseField name="objectId" type="string">
  The object it belongs to.
</ResponseField>

<ResponseField name="propertyId" type="string">
  The `prop_…` id of the **one** property this delivery is about. Resolve it
  against the object's schema to get its key and name.
</ResponseField>

<ResponseField name="previous" type="any">
  The value before the change, in stored shape. `null` means the field was
  genuinely empty. The key being **absent** means no prior value could be read,
  which is a different thing.
</ResponseField>

<ResponseField name="current" type="any">
  The value after the change. `null` means the field was cleared.
</ResponseField>

<ResponseField name="record" type="object">
  The **whole record after the change**, keyed by property key, plus an `id` key
  holding the record id.
</ResponseField>

## Why `record` is there as well

Because it saves you a round trip in the common case. The changed field is named
by `propertyId`, and `record` gives you the state the record is now in, so a
handler that needs three fields to make a decision has them without calling back.

<Note>
  `record` here carries an `id` key, which
  [`record.created`](/dev/webhooks/events/record-created) does not. That
  asymmetry comes from the emitters and is faithful to what is actually sent.
</Note>

## Naming the property

`propertyId` is an id, not something to show a person. Read the object's schema
once and cache the map:

```ts theme={null}
const object = await custral.objects.retrieve("deals");
const byId = new Map((object.properties ?? []).map((p) => [p.id, p]));

custral.on("record.updated", (event) => {
  const property = byId.get(event.data.propertyId as string);
  console.log(`${property?.name ?? "A field"} changed on ${event.data.recordId}`);
});
```

## Worth knowing

* **Order is not guaranteed.** Two changes to the same record can arrive out of
  order, so `record` is the more trustworthy view of current state than
  replaying `current` values in arrival order. If it matters,
  [read the record back](/dev/webhooks/delivery#order-is-not-guaranteed).
* **Bulk edits are suppressed**, along with imports and AI property fills.
* **Computed properties.** A formula or rollup recalculating is not itself a
  field write and does not reliably produce this event. Treat computed values in
  `record` as a snapshot rather than a trigger.

## Example

```ts theme={null}
custral.on("record.updated", async (event) => {
  const {recordId, propertyId, previous, current, record} = event.data;

  if (propertyId !== process.env.STAGE_PROPERTY_ID) return;

  const from = Array.isArray(previous) ? previous[0] : previous;
  const to = Array.isArray(current) ? current[0] : current;
  if (to !== process.env.CLOSED_WON_OPTION_ID) return;

  await billing.startSubscription({
    account: String(record?.name ?? recordId),
    value: Number(record?.annual_value ?? 0),
    movedFrom: from,
  });
});
```


## Related topics

- [Records](/dev/webhooks/events/records.md)
- [record.deleted](/dev/webhooks/events/record-deleted.md)
- [record.created](/dev/webhooks/events/record-created.md)
- [Troubleshooting](/dev/webhooks/troubleshooting.md)
- [Payload format](/dev/webhooks/payload.md)
