Writes
Andromeda stores a Core API write only when it changes something. If you send back what the entity already holds, you get the usual success answer and nothing else happens. modifiedAt stays put, no subscriber hears about it, and the temporal history doesn't grow.
That matters most for sensors. A device that repeats its last reading every minute doesn't fill the history with copies. A new observedAt on the same value does count, though, because it is a new observation.
A write that changes nothing
Say the entity already has temperature at 21, observed at 2026-10-01T08:00:00Z, and you send the same reading again.
PATCH /ngsi-ld/v1/entities/urn:ngsi-ld:Room:1/attrs
Content-Type: application/json
{
"temperature": {
"type": "Property",
"value": 21,
"observedAt": "2026-10-01T08:00:00Z"
}
}
HTTP/1.1 204 No Content
The answer is the same as for a real update. Underneath, the entity and every attribute keep their modifiedAt, no notification goes out, and nothing is added to the history.
This holds for Update Attributes, Append Attributes, Partial Attribute Update, Replace Attribute, Merge Entity and Replace Entity. In Batch Entity Update, Batch Entity Merge and Batch Entity Upsert (both modes), it holds entity by entity. Deleting an attribute always counts as a change.
What counts as a change
An attribute instance has changed when it is new, or when any of the following differs from the instance it replaces.
- the value
observedAtexpiresAt- the data-integrity seals
- any sub-attribute, however deep
createdAt and modifiedAt aren't compared, because Andromeda sets them itself.
A Scope counts as changed only when it differs from the Scope the entity already had.
Changing only the entity's types or its expiresAt counts as well. The write is stored and moves the entity's modifiedAt, but no attribute instance is added to the history, because no attribute changed.
A time value in another spelling isn't a change. The same instant written with a comma before the fraction instead of a full stop is still the same value, as How time values are served explains.
Empty bodies
An update, append or merge body is a JSON Merge Patch, and an empty patch changes nothing. Andromeda treats it that way.
{}sent to Partial Attribute Update, Update Attributes, Append Attributes or Merge Entity gets204. Nothing is stored, notified or recorded.- In Batch Entity Update (with or without
noOverwrite), Batch Entity Merge, and Batch Entity Upsert in update mode, an entity that names nothing besidesidandtypecounts as a success. A batch made only of those gets204. - A Merge Entity that names only types the entity already has gets
204, and the entity stays as it was.
An empty body still goes through every other check.
- An entity that doesn't exist, or a missing attribute on Partial Attribute Update, still gets
404ResourceNotFound. {}sent asapplication/ld+jsongets400, because anapplication/ld+jsonbody must carry an@context. The same body with its@contextis fine.- A batch whose array is empty gets
400BadRequestData. Unlike the cases above, the specification refuses an empty batch outright. - In a
noOverwritebatch update, an entity whose every named instance, Scope and expiry is already held appends nothing. It is listed once, undererrorsasBadRequestData, and not undersuccess. An entity that appends even one instance, such as a newdatasetIdof an attribute it holds, or one new Scope, is a success.
What a write adds to the history
A Core API write that changes an entity adds three kinds of thing to its temporal history.
- One instance for every attribute instance it added or changed, as the entity now holds it.
- A deletion for every instance it removed.
- The Scope, when the write changed or removed it.
Instances the write left alone aren't recorded. That includes restated ones, the ones a noOverwrite append kept, and a Scope that Update Attributes ignored because the entity had none.
If you deleted history through the Temporal API, a Core API write that restates the current state won't bring it back. The restatement changes nothing, so nothing is recorded. Write the value again through the Temporal API instead.
When a write is stamped
A batch gives everything it writes one modifiedAt, the time the batch started. That covers every entity and attribute in it, and an identifier that appears more than once in the array gets the same time on each of its instances.
A large batch can run for seconds, and another request may change one of its entities in that time. If the batch then stamped that entity with its own start time, the entity's modifiedAt would move backwards, and an attribute the other request created would look as if it had been modified before it existed. Andromeda avoids this by writing such an entity the way a separate request would, after the other request's change. Its modifiedAt and the history the batch adds for it come after that change, and the latest instance in the history is the one the entity holds. The rest of the batch keeps the batch's time.
Deletions follow the same rule. When a batch delete reaches an entity after a later change to it, the deletion is recorded after that change, so the history never shows an instance still in effect once the entity is gone. When a batch create names an identifier whose entity was deleted while the batch ran, the new entity is recorded after the deletion.
A single write is affected only when the broker's clock steps back behind a time the entity already holds. The write then lands one microsecond after that time.
Configuration
temporal.history.core_writes(default"recorded"): set it to"not-recorded"to keep Core API writes out of the temporal history. The current entity still changes, and Temporal API writes are recorded as usual.
Where Andromeda interprets the specification
The specification says an attribute changes "when any of the members (including children) in its corresponding JSON-LD node is updated with a value different than the existing one". modifiedAt is the time of the last modification, and the temporal history is tied to modifications of the current state. A restatement modifies nothing, so it moves nothing, and a last modification never carries an earlier time than the one it replaced. The specification gives every entity of a batch the same modifiedAt, yet also asks that the entities be treated "as if they had all arrived in separate requests". Andromeda follows the second rule for an entity another request changed while the batch ran. The specification also leaves it to the broker whether Core API writes reach the history at all, which is what temporal.history.core_writes decides.
A Fragment is a JSON Merge Patch. An empty merge patch returns its target unchanged, and no write operation lists an empty Fragment among its errors. An empty batch array is another matter, because the specification refuses it explicitly.