Skip to main content

Write user data

Immediate writes change the data of exactly one owner. You name that owner explicitly with a target, and the write is applied right away. Use them for support tools, data corrections, and integrations that sync data for one user at a time. To change data for many users, use a bulk job.

All three write operations require the Data: Write permission or the Admin permission. The examples on this page send operations to https://personalization.service.eu.axinom.net/graphql-management with an Authorization: Bearer <token> header. See Get started for how to obtain a token.

Choose a write operation​

GoalOperationEffect
Store one JSON value under a keysetKeyValueInserts the entry or replaces its value
Insert or update one item of an arrayupsertArrayItemInserts the item or updates the item with the same itemId
Replace the complete array with a new list of itemsreplaceArrayItemsStores the given items and removes every item that you omit

The data you write is stored in the same way as data an end user wrote with the Key-value API or the Array API. End users see it through the End-User API, and you can inspect it with the read operations.

Name the owner with a target​

Every write takes a target that identifies one user's key and scope.

FieldRequiredMeaning
userIdAlwaysThe user that owns the data
scopeAlwaysUSER, PROFILE, or APPLICATION
profileIdPROFILE and APPLICATION scope onlyThe profile that owns the data
applicationIdAPPLICATION scope onlyThe application that owns the data
keyAlwaysThe key the data is stored under

The scope decides which ids you provide.

ScopeProvideOmit
USERuserIdprofileId, applicationId
PROFILEuserId, profileIdapplicationId
APPLICATIONuserId, profileId, applicationIdNone

Ids that the scope needs must be given, and ids that the scope does not use must be left out. A target that breaks this rule is rejected with the TARGET_CONSTRAINT reason, and nothing is written. Nothing is guessed or dropped silently.

The same key can hold different data in different scopes and for different owners. Writing to one target never changes data stored under another target.

The key must match ^[\w-]+(:[\w-]+)*$. Values are limited to 10,240 bytes. See Errors and limits for all limits.

Set a key-value​

setKeyValue stores one JSON value under the target key. It inserts a new entry when none exists for the target and replaces the stored value otherwise. The whole value is replaced, so include every property that should remain.

mutation SetUserPreferences($input: SetKeyValueInput!) {
setKeyValue(input: $input) {
acknowledged
insertedCount
updatedCount
}
}
{
"input": {
"target": {
"userId": "11111111-1111-4111-8111-111111111111",
"scope": "USER",
"key": "ui:preferences"
},
"value": {
"theme": "dark",
"language": "en"
}
}
}

For a user who had no value under ui:preferences yet, a successful response reports one inserted entry:

{
"data": {
"setKeyValue": {
"acknowledged": true,
"insertedCount": 1,
"updatedCount": 0
}
}
}

When an entry already exists, updatedCount reports the updated entry instead of insertedCount.

setKeyValue writes JSON values only. It never writes or changes a progress entry, even when a progress entry is stored under the same key. Progress is read-only in the Management API.

To change only some properties of existing values, or to change values for many users, use a bulk patch.

Upsert an array item​

upsertArrayItem inserts or updates one item of an array. The item is identified by the itemId that you choose. When no item with that itemId exists in the target array, the service inserts it. When one exists, the service updates it.

mutation UpsertFavorite($input: UpsertArrayItemInput!) {
upsertArrayItem(input: $input) {
acknowledged
insertedCount
matchedCount
modifiedCount
deletedCount
item {
id
itemId
value
dateModified
owner {
userId
profileId
applicationId
scope
key
}
}
}
}
{
"input": {
"target": {
"userId": "11111111-1111-4111-8111-111111111111",
"scope": "USER",
"key": "content:favorites"
},
"itemId": "movie:100",
"value": {
"title": "Example Movie",
"addedBy": "support"
},
"sortString": "Example Movie",
"sortDate": "2026-11-05T10:30:00Z"
}
}

The itemId must match ^[\w-]+(:[\w-]+)*$ and can be at most 256 characters. It does not need to appear in the value. Use a stable domain identifier such as movie:100.

The response fields describe what happened.

FieldMeaning
acknowledgedWhether the database accepted the write
insertedCount1 when a new item was inserted, otherwise 0
matchedCount1 when an existing item was matched, otherwise 0
modifiedCountThe number of items the update modified
deletedCountOlder duplicate items removed while converging on the itemId
itemThe inserted or updated item, including its owner

Sort fields​

The optional sort fields are sortString, sortNumber, and sortDate. They follow three rules on an update.

  • If you omit a sort field, the stored value stays as it is.
  • If you send a value, the stored value is replaced.
  • If you send null, the stored value is removed.

The following variables update only the value and clear the date, while the other sort fields stay unchanged:

{
"input": {
"target": {
"userId": "11111111-1111-4111-8111-111111111111",
"scope": "USER",
"key": "content:favorites"
},
"itemId": "movie:100",
"value": {
"title": "Example Movie",
"addedBy": "support"
},
"sortDate": null
}
}

Date inputs accept the formats listed in Errors and limits.

Older items​

Older array items can lack a stored itemId and be identified by a value.id. When the itemId you upsert matches such an item, the service updates that item to use the itemId, in the same way the End-User API does. Matching duplicates are removed, which deletedCount reports. See Migrate from deprecated Array APIs for background.

Replace a whole array​

replaceArrayItems replaces the complete array of one target with the list of items you send.

warning

Replacement removes every existing item that is not in your list. Send the full set of items that the array should contain after the call. Items that you retain are written again from the values you send.

mutation ReplaceFavorites($input: ReplaceArrayItemsInput!) {
replaceArrayItems(input: $input) {
acknowledged
insertedCount
items {
id
itemId
value
dateModified
owner {
userId
scope
key
}
}
}
}
{
"input": {
"target": {
"userId": "11111111-1111-4111-8111-111111111111",
"scope": "USER",
"key": "content:favorites"
},
"items": [
{
"itemId": "movie:100",
"value": { "title": "Example Movie" },
"sortString": "Example Movie"
},
{
"itemId": "series:200",
"value": { "title": "Example Series" },
"sortNumber": 2
}
]
}
}

Replacement follows these rules:

  • items must not be empty. An empty list is rejected with the EMPTY_ARRAY reason.
  • Every itemId must be unique within the request. A repeated itemId is rejected with the DUPLICATE_ITEM_ID reason.
  • The replacement is atomic. Either the whole array is replaced or the stored data stays unchanged.
  • insertedCount is the number of replacement items inserted, and items returns them in the order you sent them.
  • Each item can carry the optional sortString, sortNumber, and sortDate fields.

To remove all items of an array, use a bulk delete.

Choose between upsert and replace​

SituationUse
Add or correct a single itemupsertArrayItem
Rebuild an array from an authoritative list in another systemreplaceArrayItems
You do not know all the items that the array currently holdsupsertArrayItem

Verify a write​

Read the data back with the read operations. The following query and variables read back the favorites written above:

query VerifyFavorites($input: ListArrayItemsInput!) {
listArrayItems(input: $input) {
nodes {
itemId
value
dateModified
}
pageInfo {
hasNextPage
endCursor
}
}
}
{
"input": {
"filter": {
"userId": "11111111-1111-4111-8111-111111111111",
"key": "content:favorites",
"scope": "USER"
},
"first": 50
}
}

Handle write errors​

Reason or codeMeaning
FORBIDDENThe token lacks Data: Write or Admin
TARGET_CONSTRAINTThe ids in the target contradict its scope
IDENTIFIER_CONSTRAINTA key or itemId does not match the required pattern or length
EMPTY_ARRAYreplaceArrayItems received no items
DUPLICATE_ITEM_IDreplaceArrayItems received the same itemId more than once
CONCURRENT_WRITE_CONFLICTAn array write conflicted with another change and was not completed

A CONCURRENT_WRITE_CONFLICT leaves the stored data unchanged. You can send the same request again.

Inspect errors[].extensions rather than the message text. See Errors and limits for the full reference.

Next steps​

Was this page helpful?