Skip to content

Migrating to v2 ​

v2 addresses everything by uuid instead of the integer id. Authentication, pagination, filters and errors work exactly as in v1, so for most integrations the move is a matter of switching ids and a few field names.

v1 keeps working. It receives bug fixes but no new features: new endpoints — such as recurring work steps — and webhooks exist in v2 only. You can move one resource at a time; both versions read and write the same data.

In short ​

v1v2
Base path/v1/…/v2/…
Key of a resourceid (integer)uuid
Path parameter/v1/assignment-events/4711/v2/assignment-events/01a14371-…
Reference to another resourceassignmentId: 12assignmentUuid: "01a1…"
List of referencesassignedUsers: [3, 4]userUuids: ["01a1…", "01a2…"]
Linked address, written"company" or an object{ "link": "company" } or { "link": "custom", … }
*.deleted webhook—{ "uuid": "…" }

Before you switch: collect the uuids ​

Every v1 resource already returns its uuid and can be filtered by it. If you store Artesa ids, store the uuid next to them while you are still on v1:

http
GET /v1/assignment-events?size=20&page=0
json
{ "items": [{ "id": 4711, "uuid": "01a14371-cc14-7524-8225-672c59cef75a", "assignmentId": 12, … }] }

Once every stored id has its uuid, the switch is a lookup instead of a migration. References to other resources (assignmentId, typeId, …) need the uuid of the referenced resource: read it from that resource's v1 list, or let v2 tell you once you read the referencing resource there.

You can also set the uuid yourself when creating a resource, in v1 and v2 alike. It has to be a UUIDv7; a generated key lets you retry a create without creating twice.

What changes ​

Identifiers ​

  • The id property is gone; uuid is the key, in responses and in paths.
  • Every reference ends in Uuid and holds a uuid: assignmentId → assignmentUuid, typeId → typeUuid, groupId → groupUuid, userId → userUuid, and so on.
  • A reference to a record of another company, or to none at all, answers 400 Unknown <property> <uuid>, as an unknown id does in v1.
  • Filters follow the property names: ?assignmentId=12 becomes ?assignmentUuid=01a1…, id[$in] becomes uuid[$in].

Lists of assigned users and gangs ​

assignedUsers and assignedGangs become userUuids and gangUuids. They behave as before: a patch replaces the whole list, [] removes everyone, and a gang brings its members along.

Addresses ​

v1 took a linked address as a string and an own address as an object, but returned both as an object with link. v2 reads and writes the same shape:

json
// v1 — write
{ "addressStartFrom": "company", "addressDestination": { "street": "Hauptstr. 1", "city": "Berlin" } }

// v2 — read and write
{
  "addressStartFrom": { "link": "company" },
  "addressDestination": { "link": "custom", "street": "Hauptstr. 1", "city": "Berlin" }
}

link is company, customer, assignment or custom. null still clears an address, and patching an own address with custom changes it in place. A plain string is rejected in v2.

Webhooks ​

Webhooks are only available in v2, managed at /v2/webhook-subscriptions. data is the resource as its v2 GET returns it, and *.deleted carries only the uuid.

Types are checked strictly ​

v1 converts a value to the documented type: "name": 1 is stored as "1", "isOutward": "true" as true. v2 answers 400 instead. Only dates are read from strings, as JSON has no dates: send them as ISO 8601.

What stays the same ​

  • The API key and the Authorization: Bearer … header.
  • page, size, pages and items of every list, and the filter syntax.
  • Error responses and status codes.
  • externalId on every resource that has one.
  • completedAt and confirmedAt take a date, null, or true for now and false for none.

Resource by resource ​

assignment-events ​

v1v2
iduuid
assignmentIdassignmentUuid
typeIdtypeUuid
groupIdgroupUuid
assignedUsersuserUuids
assignedGangsgangUuids
addressStartFrom, addressDestination, addressReturnTosame names, written as { "link": … }
—seriesUuid, seriesOccurrenceDate (new)

An event that is outward — set explicitly or by its type — starts and returns at the company and goes to the assignment, unless you give its addresses.

assignment-event-group-types ​

v1v2
iduuid

Read-only, as in v1. New: a name filter.

assignment-event-groups ​

v1v2
iduuid
typeIdtypeUuid
assignmentIdassignmentUuid

A group keeps its type: a patch with typeUuid is rejected with 400, where v1 accepted typeId and ignored it.

assignment-events/{assignmentEventUuid}/uploads ​

v1v2
/v1/assignment-events/{assignmentEventId}/uploads/{id}/v2/assignment-events/{assignmentEventUuid}/uploads/{uploadUuid}
id, categoryId, assignmentEventIduuid, categoryUuid, assignmentEventUuid
  • The list holds the event's files only; each is also on its assignment and listed there.
  • Get, update and delete answer 404 for an upload that isn't on the event in the path; in v1 any upload of the company could be reached through any event.
  • The create is still multipart; uuid, description and categoryUuid go next to file. A missing file or an invalid field is a 400.
  • An unknown or another company's event is a 400 on list and create.

assignment-status-types ​

v1v2
iduuid

Read-only, as in v1. New: name and category filters; category takes null for status types without one.

assignments ​

v1v2
id, /v1/assignments/{assignmentId}uuid, /v2/assignments/{assignmentUuid}
customerId, projectId, typeId, statusIdcustomerUuid, projectUuid, typeUuid, statusUuid
responsibleUserIdresponsibleUserUuid
address: { …fields }address: { link: "company" | "customer" } or { link: "custom", …fields }; null removes it
  • An assignment's address links to the company's or the customer's address, or is its own (custom).
  • v1 changed whatever address the assignment had in place, a linked one too. In v2, custom changes only an own address in place; on a linked one it creates a new own address, and the company's or customer's address stays as it is.
  • An address in v1's shape (fields without link) is a 400.

assignments/{assignmentUuid}/uploads ​

v1v2
/v1/assignments/{assignmentId}/uploads/{id}/v2/assignments/{assignmentUuid}/uploads/{uploadUuid}
id, categoryId, assignmentIduuid, categoryUuid, assignmentUuid
  • The list holds every file on the assignment: its own and those of its events, event groups and forms. An event's files are also listed under that event's uploads.
  • Get, update and delete answer 404 for an upload that isn't on the assignment in the path; in v1 any upload of the company could be reached through any assignment.
  • The create is still multipart; uuid, description and categoryUuid go next to file. A missing file or an invalid field is a 400.
  • An unknown or another company's assignment is a 400 on list and create.

customers ​

v1v2
id, /v1/customers/{customerId}uuid, /v2/customers/{customerUuid}
billingAddress optional, the address's fieldsalways present: null or { link: "custom", …fields }, written the same way; null removes it
telephones, emails, urls: [] is ignored[] removes the stored values; a list replaces all of them
telephones documented as at most oneone per label (main, mobile, fax), so up to three
isDefault in written numbers and emailsnot accepted (it never did anything); still read

gangs ​

v1v2
id, userIdsuuid, userUuids (a patch replaces the whole list)
order optional on the itemalways present
externalId filteralso takes null
—deactivatedAt filter (date or null)

projects ​

v1v2
id, /v1/projects/{projectId}uuid, /v2/projects/{projectUuid}
POST with an existing number returns that project409 Conflict
externalId in a patch: no length limitat most 255 characters

roles ​

v1v2
iduuid

Read-only, as in v1. Users reference their role as roleUuid.

time-entries ​

v1v2
iduuid
typeIdtypeUuid
userIduserUuid
assignmentIdassignmentUuid
eventIdeventUuid
eventTypeIdeventTypeUuid

The filters are renamed the same way and take null for entries without a type, assignment or event. endsAt is required on create and can't be set to null on a patch (400): an entry keeps its end. externalId takes at most 255 characters on a patch too.

time-entry-types ​

v1v2
iduuid
eventTypeIdeventTypeUuid

Time entry types still can't be deleted; set deactivatedAt instead. A patch leaves out what it doesn't send: v1 reset an omitted order to 1.

time-sheet-days ​

v1v2
id, userIduuid, userUuid
approvedById, declinedById, manipulatedByIdapprovedByUuid, declinedByUuid, manipulatedByUuid
startsAt, endsAt, breakTime, duration on create and patchstartsAtManual, endsAtManual, breakTimeManual, durationManual
GET /v1/time-sheet-days/by-user?userId=…&day=…POST /v2/time-sheet-days/by-user with { "userUuid": …, "day": … }

startsAt, endsAt, breakTime and duration are the effective values the day calculation settles on, and read-only: sending one is a 400. The manual overrides are read and written under their own names; null drops one, and the effective value falls back to the calculated one.

A status change the day doesn't allow — re-submitting a submitted day, submitting an approved one — answers 400 naming the allowed statuses, where v1 ignores it silently. declinedNote is only accepted together with status: "declined".

by-user creates the day it doesn't find, so it is a POST now; it answers 200 with the day either way.

time-sheet-months ​

v1v2
id, userIduuid, userUuid
month: "2026-05-01"month: "2026-05", in filters too
sumTarget: 0 without working-time rulessumTarget: null, as on weeks
PATCH with { "status": … }POST /v2/time-sheet-months/{uuid}/submit, /approve, /decline, /reset
GET /v1/time-sheet-months/by-user?userId=…&month=…POST /v2/time-sheet-months/by-user with { "userUuid": …, "month": "2026-05" }

status is read-only; a patch takes note, carryInManual, carryOutManual and externalId, and null drops a carry override. decline takes an optional declinedNote, set on every day it declines. Months now also show carryInManual and carryOutManual. Each action moves the month's days that can make the step (reset back to draft) and answers the month once its status is recomputed. by-user answers 200 with the month, found or created.

time-sheet-weeks ​

v1v2
id, userIduuid, userUuid
PATCH with { "status": … }POST /v2/time-sheet-weeks/{uuid}/submit, /approve, /decline, /reset
GET /v1/time-sheet-weeks/by-user?userId=…&weekStart=…POST /v2/time-sheet-weeks/by-user with { "userUuid": …, "weekStart": … }

status is read-only; a patch takes only externalId. decline takes an optional declinedNote, set on every day it declines. Each action moves the week's days that can make the step (reset back to draft) and answers the week once its status is recomputed. by-user answers 200 with the week, found or created.

upload-categories ​

v1v2
id, roleIdsuuid, roleUuids (a patch replaces the whole list)
order nullablealways an integer ≥ 1
—createdAt, updatedAt on the item and as filters
—name filter

user-absence-types ​

v1v2
iduuid

Every field of a type is always returned; color and deactivatedAt may be null. A create may set color: null. A patch leaves out what it doesn't send: v1 reset an omitted needApproval, needCompensation and order to their defaults. New: name, createdAt and updatedAt filters.

user-absences ​

v1v2
iduuid
typeIdtypeUuid
userIduserUuid
needApprovalByIdneedApprovalByUuid
declinedByIddeclinedByUuid
compensatedByIdcompensatedByUuid

The …ByUuid filters take null to find absences without one. The status filter takes operators ($in, $ne, …). requestedAt is null only for drafts made in Artesa; a create keeps a given one and takes the time of the request otherwise. externalId takes at most 255 characters on a patch too.

users ​

v1v2
iduuid
roleIdroleUuid

A user's role is set at create: a patch with roleUuid is rejected with 400, where v1 accepted roleId and ignored it. A patch keeps an omitted color, where v1 reset it to #FFFFFF. The deactivatedAt and externalId filters take null.

New in v2 ​

  • /v2/assignment-event-series: recurring work steps, read-only. A series' occurrences are ordinary assignment events with seriesUuid and seriesOccurrenceDate set; patching or deleting one changes only that occurrence. Series are created about twelve months ahead, so setting one up sends one assignment-event.created per occurrence.

Checklist ​

  1. Store the uuid of every Artesa record you keep, while still on v1.
  2. Switch the base path to /v2 and address records by uuid.
  3. Rename references (…Id → …Uuid) and assignment lists (assignedUsers → userUuids, assignedGangs → gangUuids), in bodies and filters.
  4. Write linked addresses as { "link": … }.
  5. On time sheets, write day overrides as …Manual, replace status patches of weeks and months with their actions, and by-user GETs with POSTs.
  6. Subscribe to webhooks at /v2/webhook-subscriptions if you poll for changes today.