Skip to main content
Some data in tipee is not a list of independent records. It is a timeline: an ordered set of periods that never overlap, each covering a date range and carrying a set of values. Activity rates, sector assignments and vacation entitlements all work this way, and future entitlements will too. They share the same write semantics, which this page describes once. Each entity’s own page lists what is specific to it — above all, which fields decide whether two periods count as identical.
The consequence to internalise. An endpoint such as resources.assign-activity-rate does not mean “create a record”. It means “make these values true over this range”, and tipee reshapes the neighbouring periods so that the statement holds. Most of the time this is what you want, and you can let tipee do it. The two situations worth knowing about are further down: a call that appears to do nothing, and a call that deletes an existing period.

Anatomy

A timeline belongs to one subject — usually a collaborator — and to one kind of data. A collaborator has an activity rate timeline, a sector timeline, and so on; they are independent of each other. Each period has a date range, which may be open-ended where the entity allows it, and a set of values whose shape depends on the entity. Within one timeline, periods are sorted by start date and never overlap.

The two rules

Rule 1 — the requested range always wins. The date range you send is stored exactly as sent. tipee never shifts your start date, never trims your end date, never fills a gap on your behalf. Existing periods yield in whichever direction is needed. Rule 2 — identical neighbours merge. If the period you assign is adjacent to or overlapping an existing one, and the two are identical, they become a single period spanning both ranges. Otherwise the existing period is truncated, split, or removed to make room. Adjacent means the two ranges touch: one ends the day before the other starts. Overlapping means they share at least one day. A gap of one day or more is never bridged. Identical means that every field in the entity’s identity set holds the same value. That set is defined per entity and listed on the entity’s own page.
The comparison is on effective values, not on what your payload contained. Sending a value explicitly, omitting it so a default applies, or leaving it empty so it is inherited are all equivalent — what matters is the value that ends up applying. So two payloads that look different can merge, and two payloads that look alike may not.

What a write does

In every diagram, Before is the existing timeline, Assign is the period you send, and Result is the resulting timeline. Different letters mean different values; the same letter twice means identical values — that is how the diagrams encode Rule 2. Columns are contiguous: there is no hidden gap between two adjacent columns.

Assigning next to an identical period extends it

This is the one that surprises integrators. You assigned a new period, the call succeeded, and the number of periods did not change — because tipee merged yours into its neighbour. Nothing was lost: the existing period now runs further.
Had the values differed, you would simply have two periods side by side.

Assigning over a different period truncates it

The existing period is cut back to the day before your range starts.

Assigning the exact same range replaces it

No error, no duplicate. This is the normal way to modify an existing period.

Assigning inside a period splits it in two

Three periods result, and the tail keeps the original values.

Assigning over a whole period deletes it

When your range covers an existing period entirely and the values differ, that period is removed. It leaves no trace and there is no warning. Check the extent of your range before assigning over a populated stretch of timeline.
Overlapping ranges with identical values merge into the union of the two ranges, exactly as adjacent ones do.
Splitting works on open-ended periods too. The tail stays open-ended.
The rules are symmetric. A range starting before an existing period pushes that period’s start forward.
A range spanning several periods makes each neighbour yield on its own side.
A merge can happen on both sides at once. Overwriting a middle period so that it becomes identical to its neighbours collapses everything into one.

Calls that appear to do nothing

A merge can absorb your request entirely. The call succeeds, the timeline is unchanged, and nothing is wrong. Assigning an identical open-ended period after an existing open-ended one changes nothing: the union of the two ranges is the existing range.
Assigning an identical bounded period inside an open-ended one silently discards your end date. You asked for values ending on Feb 28, the call succeeded, and the period still runs forever — because the union of [Jan 1 → ∞] and [Feb 1 → Feb 28] is still [Jan 1 → ∞]. This one does not generate a support call. It generates wrong data months later.What to do: an assign can never shorten a period. To end one, use the entity’s unassign endpoint.
The same holds for an identical period assigned inside an existing bounded one. In all three cases, changing any single identity field gives you the split you expected instead.

Removing instead of assigning

Where an entity exposes an unassign endpoint, it clears the timeline over the intersection of your range with the existing periods, and succeeds silently when there was nothing to remove. It is the only way to end a period on a given date.
It can also punch a hole in the middle of a period, splitting it in two.

Recipes

Assign the new values over an open-ended range beginning on that date. The existing period is truncated the day before, and you end up with two periods. If the new values happen to be identical to the current ones, nothing happens — which is the correct outcome, since the timeline already said what you wanted it to say.
Re-assign the exact same date range with the new values. The period is replaced in place.What to do afterwards: read the timeline back. If the corrected values now match a neighbour, the two will have merged, and your period no longer exists as a separate entry.
An assign cannot shorten a period from the right. Use the entity’s unassign endpoint over the range you want cleared, starting the day after the intended end.
There is no merge indicator in a response, and periods are not always addressable individually. Reading the timeline before and after the write, and diffing the two, is the reliable way to know whether your call created, merged, truncated, split or did nothing.

What varies between entities

The rules above hold for every timeline. What each entity defines for itself, on its own page:

The identity fields

Which fields must match for two periods to be considered identical — the input to Rule 2.

The endpoints

The assign and unassign operation names, their payloads, and what they return.

Range constraints

Whether open-ended ranges are accepted, and whether the timeline may contain gaps at all.

Defaults and inheritance

Which values can be omitted, and what they resolve to — which in turn decides what counts as identical.
Read this page first, then the entity’s page for its specifics.