Show examples in:
Javascript HTTP
Content Management API > Record

Update a record

📚 New to DatoCMS records?

We strongly recommend reading the Introduction to Records guide first. The payload for updating a record follows the same structure as creating one, so that guide is also an essential prerequisite.

The fundamental rules for structuring field values (i.e., strings, numbers, objects, references) are the same for both creating and updating records. For a complete reference on how to format the value for every field type, please see the Field Types Overview in the main records guide.

When updating an existing record, you only need to provide the fields you want to change. Any fields you omit from your payload will remain untouched.

⚠️ Null vs. Omitted Fields

There's a crucial difference between omitting a field and explicitly setting it to null or an empty value:

  • Omitted fields keep their existing values unchanged
  • Fields set to null or empty values (like [] for arrays) are cleared/deleted

The following sections highlight the rules and strategies that are specific to the update process.

Updating Block Fields

As with creation, you cannot edit blocks directly; you must update the parent record that contains them. The payload you send uses a mix of block IDs and block objects to define the desired final state.

The rules for adding, updating, keeping, deleting, and reordering blocks are covered in detail in the main records guide.

🛠️ Block and Structured Text utilities

We provide many utility functions to help you work with blocks and structured text nodes effectively. Check out our helper libraries for common operations:

  • Block Utils - Functions like buildBlockRecord(), duplicateBlockRecord(), and localization helpers
  • Structured Text Utils - Tree manipulation utilities like mapNodes(), filterNodes(), and findFirstNode()

Updating Localized Fields

➡️ Before proceeding, ensure you have read the general guide on Localization

When you send an update request, the API follows these strict rules.

Rule 1: To change a locale value, send the whole set

When you update a translated field, you must provide the entire object for that field, including all the languages you want to keep unchanged. You can't just send the one language you're changing.

  • Correct: To update the Italian title, you send both English and Italian:
    {
    "title": {
    "en": "Hello World",
    "it": "Ciao a tutti! (Updated)"
    }
    }
  • Incorrect: If you only send the Italian value, the API will assume you want to delete the English one!
Rule 2: To add/remove a language, send all translated fields

This is the only time you can't just send the one field you're changing. To add or remove a language from an entire record, you must include all translated fields in your request. This is to enforce the Locale Sync Rule and ensure all fields remain consistent.

  • Example: To add French to a blog post that already has a translated title and content, your request must include both fields with the new fr locale.
Rule 3: Limited permissions? Only send what you can manage

If your API key only has permission for certain languages (e.g., only English), you must only include those languages in your update. The system is smart and will automatically protect and preserve the content for the languages you can't access (like Italian or French).

Update scenarios at a glance

This table shows what happens in different situations. The key takeaway is that your update payload defines the new final state for the languages you are allowed to manage.

Your Role managesRecord currently HasYour payload sendsResult
EnglishEnglishEnglish✅ English is updated.
English, ItalianEnglishEnglish, Italian✅ English is updated.
➕ Italian is added.
English, ItalianEnglish, ItalianEnglish✅ English is updated.
➖ Italian is removed.
English, ItalianEnglish, ItalianEnglish, Italian✅ English is updated.
✅ Italian is updated.
Eng, Ita, FreEnglish, ItalianEnglish, French✅ English is updated.
➖ Italian is removed.
➕ French is added.
EnglishEnglish, ItalianEnglish✅ English is updated.
🛡️ Italian is preserved.
English, ItalianEnglish, FrenchEnglish, Italian✅ English is updated.
🛡️ French is preserved.
➕ Italian is added.
English, ItalianEnglish, FrenchItalian➖ English is removed.
🛡️ French is preserved.
➕ Italian is added.
Block fields

The rules about localization work in combination with the rules for updating blocks: you use full block objects to create/update and block IDs to leave unchanged, but you do so within the object for a specific locale.

Example: Updating a block in one locale

This payload updates the title of an existing block in the en locale, while leaving the second English block and all Italian blocks untouched. The it locale needs to be included in the payload, or the Italian locale will be deleted!

{
"content_blocks": {
"en": [
{
"id": "dhVR2HqgRVCTGFi0bWqLqA",
"type": "item",
"attributes": { "title": "Updated English Title" }
},
"kL9mN3pQrStUvWxYzAbCdE"
],
"it": [
"dhVR2HqgRVCTGFi_0bWqLqA",
"kL9mN3pQrStUvWxYzAbCdE"
]
}
}
Example: Adding a new block to one locale

This payload adds a new block to the it locale only. The en locale needs to be included in the payload, or the Italian locale will be deleted!

{
"content_blocks": {
"en": [
"dhVR2HqgRVCTGFi_0bWqLqA",
"kL9mN3pQrStUvWxYzAbCdE"
],
"it": [
"fG8hI1jKlMnOpQrStUvWxY",
{
"type": "item",
"attributes": { "title": "Nuovo Blocco" },
"relationships": {
"item_type": { "data": { "id": "BxZ9Y2aKQVeTnM4hP8wLpD", "type": "item_type" } }
}
},
"dhVR2HqgRVCTGFi0bWqLqA"
]
}
}
Example: Adding a new locale

To add a new locale to an existing record, you must provide values for all localized fields for that new locale, and include existing locales that you want to preserve.

{
"title": {
"en": "English Title",
"fr": "Titre Français",
},
"content_blocks": {
"en": [
"dhVR2HqgRVCTGFi_0bWqLqA",
"kL9mN3pQrStUvWxYzAbCdE"
],
"fr": [
{
"type": "item",
"attributes": { "title": "Nouveau Bloc Français" },
"relationships": {
"item_type": { "data": { "id": "BxZ9Y2aKQVeTnM4hP8wLpD", "type": "item_type" } }
}
}
]
}
}
🌐 Unified field processing utilities

We provide utilities that offer a unified interface for working with DatoCMS field values that may or may not be localized. They eliminate the need for conditional logic when processing fields that could be either localized or non-localized. Check out our Unified Field Processing utilities for streamlined localization handling.

Mass Block Operations

Sometimes, you need to perform mass operations on any block of a specific kind, regardless of where they're embedded in your content structure — whether in Modular Content fields, Single Block fields, or deeply nested within Structured Text documents. In these cases, manually traversing each record and field would be extremely time-consuming and error-prone.

DatoCMS provides powerful utilities that can systematically discover, traverse, and manipulate blocks across your entire content hierarchy. These utilities handle the complexity of localized content, nested structures, and different field types automatically, making what would otherwise be a complex operation straightforward and reliable.

Optimistic Locking

To prevent clients from accidentally overwriting each other's changes, the update endpoint supports optimistic locking. You can include the record's current version number in the meta object of your payload.

If the version on the server is newer than the one you provide, the API will reject the update with a 422 STALE_ITEM_VERSION error, indicating that the record has been modified since you last fetched it.

Body parameters

type string Required

Must be exactly "item".

meta.created_at string Optional

Date of creation

meta.first_published_at null, string Optional

Date of first publication

meta.current_version string Optional

The ID of the current record version (for optimistic locking, see the example)

Example: "4234"
meta.stage string, null Optional

The new stage to move the record to

relationships.item_type.data Optional

The record's model

relationships.creator.data Optional

The entity (account/collaborator/access token/sso user) who created the record

Returns

Returns a resource object of type item.

Examples

PUT https://site-api.datocms.com/items/:item_id HTTP/1.1
Authorization: Bearer YOUR-API-TOKEN
Accept: application/json
X-Api-Version: 3
Content-Type: application/vnd.api+json
{
"data": {
"type": "item",
"id": "hWl-mnkWRYmMCSTq4z_piQ"
}
}
Terminal window
curl -g 'https://site-api.datocms.com/items/:item_id' \
-X PUT \
-H "Authorization: Bearer YOUR-API-TOKEN" \
-H "Accept: application/json" \
-H "X-Api-Version: 3" \
-H "Content-Type: application/vnd.api+json" \
--data-binary '{"data":{"type":"item","id":"hWl-mnkWRYmMCSTq4z_piQ"}}'
await fetch("https://site-api.datocms.com/items/:item_id", {
method: "PUT",
headers: {
Authorization: "Bearer YOUR-API-TOKEN",
Accept: "application/json",
"X-Api-Version": "3",
"Content-Type": "application/vnd.api+json",
},
body: JSON.stringify({
data: { type: "item", id: "hWl-mnkWRYmMCSTq4z_piQ" },
}),
});
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: cache-control: max-age=0, private, must-revalidate
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 28
{
"data": {
"type": "item",
"id": "hWl-mnkWRYmMCSTq4z_piQ",
"relationships": {
"item_type": {
"data": {
"type": "item_type",
"id": "DxMaW10UQiCmZcuuA-IkkA"
}
}
},
"attributes": {
"title": "My first blog post!",
"content": "Lorem ipsum dolor sit amet...",
"category": "24",
"image": {
"alt": "Alt text",
"title": "Image title",
"custom_data": {},
"focal_point": null,
"upload_id": "20042921"
}
},
"meta": {
"created_at": "2020-04-21T07:57:11.124Z",
"updated_at": "2020-04-21T07:57:11.124Z",
"published_at": "2020-04-21T07:57:11.124Z",
"first_published_at": "2020-04-21T07:57:11.124Z",
"publication_scheduled_at": "2020-04-21T07:57:11.124Z",
"unpublishing_scheduled_at": "2020-04-21T07:57:11.124Z",
"status": "published",
"is_current_version_valid": true,
"is_published_version_valid": true,
"current_version": "4234",
"stage": null,
"has_children": true
}
}
}