Skip to content
CantiereGO

Export format

What the archive of a workspace's data that an owner downloads from CantiereGo holds: every file, every field and the JSON Schema that describes it. It is the up-to-date register of the structures and formats of the exportable data that Article 26 of Regulation (EU) 2023/2854 (the Data Act) provides for, for whoever moves to another provider or wants a complete copy of their own data.

The archive is a ZIP file. It holds manifest.json, which describes it; thirteen JSON files, one per family of records, complete and authoritative; anagrafiche.xlsx, the live registry in the import template's format; and five folders: schemas/, presence/, timesheets/, photos/ and documents/.

In the JSON files, in UTF-8, the keys are in English and the values are as the app records them: ids are UUIDs; instants are ISO 8601 dates and times with their offset from universal time, days YYYY-MM-DD, times of day HH:MM:SS; choices are codes, which each schema lists; an amount or a length with decimals is written as text; null means the datum is not there. Texts the office wrote stay as they were written.

The last section, "Jurisdiction and access to the data", gives the information Article 28 of the same Regulation calls for: where the infrastructure CantiereGo processes the data on is located, and which technical, organisational and contractual measures A176lab takes against access to the data, or its transfer, by authorities of third countries.

The format and its version

The manifest's format field gives the format's name and version: cantierego-workspace-export for the workspace archive and cantierego-medical-export for the medical archive, both at version 1.0.0, the one this page describes.

The version is semantic. A new optional field or file raises the minor version (1.1.0); a field removed, renamed or retyped, or a changed meaning, makes a new major version (2.0.0), whose schemas are published in a new folder, /formato-esportazione/2/. The schemas on this page, under /formato-esportazione/1/, describe the latest minor version of major version 1.

Every archive carries, in its schemas/ folder, the schemas of the version it was written with: the contract travels with the data.

How to download it, and for how long

An owner requests the archive from the workspace's settings (Settings › Company). The app prepares it in the background and lists it when it is ready; one is prepared a day per workspace, one at a time. Each archive's file can be downloaded for 14 days after it is prepared, and in any case not after the workspace's deletion has begun. The same operations are available through the app's programming interface, with the account's credentials: the OpenAPI document published at https://app.cantierego.it/api/v1/openapi.json describes it.

The workspace is closed from the same settings. After the closure its owners can still read it, export it and reopen it for at least 30 days: once 30 days have passed since the closure, the deletion begins at the first nightly pass and ends within 40 days of the closure. A legal hold on attendance or a hold on a document stops the deletion and keeps what it holds.

A workspace closed before this rule applied keeps the date its owners were given at the closure: its deletion may begin 21 days after the closure.

The archive's files

manifest.json

The file to read first, written last: the format and its version, the workspace, the instant and the day the archive was written, how many rows each JSON file holds and how many files each folder holds, what was withheld for medical confidentiality and which files were missing.

On the documents' originals, missing.originals and missing.originals_reason are authoritative: a lost original, or document reading suspended for an incident, leaves a revision whose file field names a file the archive does not hold.

JSON Schema: manifest.schema.json

The fields of manifest.json
FieldMeaning
formatThe archive's format.
format.nameThe format's name.
format.versionThe format's version, semantic: a new optional entry is a minor version; an entry removed, renamed or of another type is a new major.
export_idThe export's id.
workspaceThe workspace exported.
workspace.idThe workspace's id.
workspace.nameName.
workspace.slugThe short name in the app's address.
workspace.legal_nameLegal name.
generated_atWhen the archive was written.
dayThe workspace's day on which the archive was written.
timezoneThe workspace's time zone.
app_versionThe version of the app that wrote the archive.
read_modeHow the families of records were read: one after another, a page at a time.
read_mode_noteWhat the sequential reading implies.
read_mode_note.itThe text in Italian.
read_mode_note.enThe text in English.
filesFor each JSON file the rows written, for each folder the files.
files.workspace.jsonRows in workspace.json: always 1.
files.members.jsonMembers plus invitations in members.json.
files.employers.jsonEmployers in employers.json.
files.sites.jsonSites in sites.json.
files.workers.jsonWorkers in workers.json.
files.vehicles.jsonVehicles in vehicles.json.
files.documents.jsonDocuments in documents.json.
files.attendance_events.jsonPunches in attendance_events.json.
files.presence.jsonIntervals in presence.json.
files.timesheet_sends.jsonSends in timesheet_sends.json.
files.imports.jsonImports in imports.json.
files.consents.jsonAcknowledgements in consents.json.
files.audit.jsonRows in audit.json.
files.anagrafiche.xlsxRows of the sheets of anagrafiche.xlsx, headers excluded: the live registry in the import template's columns.
files.schemas/Schemas in the schemas/ folder.
files.presence/Monthly sheets in the presence/ folder.
files.timesheets/Files in the timesheets/ folder.
files.photos/Photos in the photos/ folder.
files.documents/Originals in the documents/ folder.
withheldWhat the archive does not hold, for medical confidentiality.
withheld.medicalThe total of medical documents, outcomes and judgments withheld.
withheld.decisionsMedical outcomes withheld.
withheld.documentsMedical documents withheld.
withheld.factsFitness judgments withheld because they select an outcome.
withheld.audit_rowsActivity-log rows left out or masked for medical confidentiality.
withheld.by_employerThe total withheld by the employer it is answerable to — a fitness judgment and an outcome, the employer the outcome was recorded for, even if the worker has since moved to another —: the key is the employer's id, or none.
withheld.noteWhat is withheld and where it is.
withheld.note.itThe text in Italian.
withheld.note.enThe text in English.
missingThe files the archive should have held and did not find.
missing.photosWorkers whose photo was missing.
missing.originalsRevisions whose original was missing.
missing.timesheetsThe paths of the sent files that were missing.
missing.originals_reasonWhy the originals are missing: null if only a few are missing that the file store no longer held; document_reads_disabled if document reading was suspended for an incident and no original was read, every one then listed in originals.

workspace.json

One object: the workspace's identity and registered office, its attendance and compliance settings, its state (suspension, closure, from when deletion may begin), the document retention policies in every version, and the chat channels' settings.

JSON Schema: workspace.schema.json

The fields of workspace.json
FieldMeaning
idThe workspace's id.
slugThe workspace's short name in the app's address (/w/<slug>).
nameThe workspace's name.
legal_nameLegal name.
vat_numberVAT number.
tax_codeThe company's tax code.
sdi_codeThe SdI recipient code for electronic invoicing.
pecCertified email address (PEC).
addressThe company's registered office.
address.streetStreet and number.
address.capPostcode, five digits.
address.cityMunicipality.
address.provinceProvince code, two letters.
address.countryCountry, a two-letter ISO 3166-1 code (IT when not given).
phonePhone.
timezoneThe IANA time zone the workspace reads its days in (for example Europe/Rome).
settingsThe workspace's settings by section: an empty object until the office has saved one; each section present is written whole.
settings.attendanceThe attendance settings, saved by the office.
settings.attendance.red_badge_policyA self-service punch with a red badge: recorded or refused.
settings.attendance.work_startStart of the working hours (local time).
settings.attendance.work_endEnd of the working hours (local time).
settings.attendance.break_minutesBreak minutes.
settings.attendance.retention_yearsYears attendance is kept after the end of the employment.
settings.attendance.legal_holdWhether the deletion of attendance is suspended by a legal obligation.
settings.attendance.consultantThe payroll consultant the monthly hours are sent to, or null.
settings.attendance.consultant.nameName.
settings.attendance.consultant.emailEmail.
settings.attendance.consultant.noteNote.
settings.complianceThe compliance policy, saved by an owner.
settings.compliance.strictWhether missing requirements turn the badge red instead of staying warnings.
created_byThe account that created the workspace.
created_atWhen it was created.
updated_atWhen it was last changed.
suspended_atWhen the platform suspended the workspace.
suspended_reasonThe reason for the suspension given to the owners.
closed_atWhen it was closed.
closed_byThe account of the owner who closed the workspace.
closed_sourceWho closed the workspace: an owner or the platform.
purge_afterThe instant from which the closed workspace's deletion may begin.
retention_policiesThe document retention policies, every version the office wrote.
retention_policies[].idThe policy's id.
retention_policies[].versionThe policy's version for its category.
retention_policies[].employer_idThe employer, for a policy on medical documents; null otherwise.
retention_policies[].categoryThe category of documents: ordinary, restricted, medical, vehicles'.
retention_policies[].purposeThe purpose of keeping them.
retention_policies[].triggerThe event the term runs from: the end of the worker's employment, the site's closure, the employer's archiving, the end of the rental, the document's archiving.
retention_policies[].superseded_triggerFor a superseded revision: the term runs from its replacement, or as for the current revision.
retention_policies[].fallbackThe fallback event when the main one does not come.
retention_policies[].retention_monthsMonths kept after the event.
retention_policies[].justification_refThe legal or contractual reference.
retention_policies[].review_onThe day the policy is to be reviewed.
retention_policies[].created_byThe account that wrote it.
retention_policies[].created_atWhen it was created.
bot_settingsThe workspace's chat channels and the times of its two scheduled notices; null if they were never set.
bot_settings.telegram_enabledWhether the Telegram channel is open for the workspace.
bot_settings.whatsapp_enabledWhether the WhatsApp channel is open for the workspace.
bot_settings.forgotten_exit_atThe time of the forgotten-exit notice.
bot_settings.office_summary_atThe time of the office summary.
bot_settings.updated_byThe account that set them.
bot_settings.updated_atWhen it was last changed.

members.json

One object with five lists: the members, current and past, with their role; the invitations, accepted, expired or pending, never their link; the Telegram or WhatsApp chats the members linked to the workspace; the notices they asked for; every grant or withdrawal of a notice.

JSON Schema: members.schema.json

The fields of members.json
FieldMeaning
membersThe members, current and past.
members[].user_idThe account's id.
members[].full_nameThe account's full name.
members[].emailThe account's email.
members[].phoneThe account's phone, in E.164 form.
members[].roleThe role in the workspace.
members[].statusThe membership's status.
members[].created_atWhen the account joined the workspace.
members[].updated_atWhen it was last changed.
members[].ended_atWhen the membership ended.
invitationsThe invitations, accepted, expired or pending.
invitations[].idThe invitation's id.
invitations[].roleThe role offered.
invitations[].emailThe email the invitation was sent to.
invitations[].phoneThe phone the invitation was sent to.
invitations[].worker_idThe worker the invitation links the account to.
invitations[].expires_atWhen the invitation expires.
invitations[].accepted_atWhen it was accepted.
invitations[].accepted_user_idThe account that accepted.
invitations[].invited_byThe account that invited.
invitations[].created_atWhen it was sent.
bot_linksThe Telegram or WhatsApp chats the members linked to the workspace.
bot_links[].idThe link's id.
bot_links[].user_idThe member's account.
bot_links[].providerThe channel.
bot_links[].member_enabledWhether the member uses the channel in this workspace.
bot_links[].office_enabledWhether the office leaves the channel open to the member.
bot_links[].worker_idThe worker the chat is tied to, if any.
bot_links[].notice_versionThe version of the channel's notice accepted.
bot_links[].plan_held_atWhen it was suspended by the plan's limits.
bot_links[].created_atWhen the chat was linked.
bot_links[].updated_atWhen it was last changed.
bot_subscriptionsThe notices the members asked for in the workspace, granted or not.
bot_subscriptions[].idThe subscription's id.
bot_subscriptions[].user_idThe member's account.
bot_subscriptions[].providerThe channel.
bot_subscriptions[].purposeThe notice: badges to watch, expiring documents, forgotten exit, the office summary.
bot_subscriptions[].scope_idThe chat's link to the workspace.
bot_subscriptions[].grantedWhether the notice is granted.
bot_subscriptions[].notice_versionThe version of the notice accepted.
bot_subscriptions[].consent_event_idThe record of the consent.
bot_subscriptions[].created_atWhen it was created.
bot_subscriptions[].updated_atWhen it was last changed.
bot_consentsEvery grant or withdrawal of a notice in the workspace.
bot_consents[].idThe record's id.
bot_consents[].scope_idThe chat's link to the workspace.
bot_consents[].user_idThe member's account.
bot_consents[].providerThe channel.
bot_consents[].actionA grant or a withdrawal of the notice.
bot_consents[].purposeThe notice.
bot_consents[].notice_versionThe notice's version.
bot_consents[].grantedWhether the notice was granted.
bot_consents[].sourceWhere it was given: in the app, in the private chat, in the migration.
bot_consents[].actor_user_idThe account that recorded it, if any.
bot_consents[].created_atThe instant.

employers.json

A list, one object per employer, archived ones included: the workspace's own company, the contractors and the self-employed, with the patente a crediti, the SOA qualification and the DURC as the office recorded them, the block, the instruction on restricted and medical documents, and whom the employer named with named access to its fitness judgments: the appointment, never a judgment.

JSON Schema: employers.schema.json

The fields of employers.json
FieldMeaning
idThe employer's id.
kindA company or a self-employed worker.
is_owntrue for the workspace's own company.
legal_nameThe legal name, or the self-employed worker's full name.
vat_numberVAT number.
tax_codeTax code.
addressRegistered office.
address.streetStreet and number.
address.capPostcode, five digits.
address.cityMunicipality.
address.provinceProvince code, two letters.
address.countryCountry, a two-letter ISO 3166-1 code (IT when not given).
emailEmail.
phonePhone.
notesThe office's notes.
patente_statusThe patente a crediti's status as the office recorded it.
patente_exempt_reasonThe exemption's ground: a SOA qualification of class III or above, an equivalent title of another Member State of the Union, an activity not subject to it.
patente_creditsThe licence's credits.
patente_numberThe licence's number.
patente_checked_onThe day the office checked the licence.
patente_exception_untilThe end of the exception on the credits.
patente_exception_reasonThe reason for the exception on the credits.
patente_suspended_untilThe end of the licence's suspension, when the office recorded it (only with the status suspended).
soa_classThe class of the SOA qualification the exemption rests on (only with the ground soa_iii).
soa_valid_untilThe SOA qualification's expiry (only with the ground soa_iii).
durc_statusThe DURC's outcome as the office recorded it.
durc_valid_untilThe DURC's expiry.
durc_document_idThe DURC document that supports the data.
durc_revision_idThe DURC document's revision cited.
durc_review_idThe office's review that applied the DURC.
blocked_atWhen the office blocked the employer.
blocked_reasonWhy it was blocked.
blocked_byThe account that blocked the employer.
deleted_atWhen it was archived.
plan_held_atWhen it was suspended by the plan's limits.
created_byThe account that created it.
created_atWhen it was created.
updated_atWhen it was last changed.
document_policyThe employer's instruction on its restricted and medical documents: purpose, basis and activation; null if there is none.
document_policy.purpose_refThe purpose's reference.
document_policy.basis_refThe legal basis's reference.
document_policy.controller_ackWhether the controller confirmed the instruction.
document_policy.outcome_aware_sinceSince when the app records the outcomes of fitness judgments.
document_policy.terms_versionThe version of the terms accepted.
document_policy.uploads_allowedWhether medical documents can be uploaded.
document_policy.review_onThe day the instruction is to be reviewed.
document_policy.updated_byThe account that wrote it.
document_policy.created_atWhen it was created.
document_policy.updated_atWhen it was last changed.
medical_accessWhom the employer named for its fitness judgments, withdrawn appointments included: the appointment, never a judgment.
medical_access[].idThe appointment's id.
medical_access[].user_idThe account appointed.
medical_access[].can_writeWhether it can also record judgments.
medical_access[].appointment_refThe reference of the act of appointment.
medical_access[].granted_byThe account that made the appointment.
medical_access[].granted_atWhen the appointment was made.
medical_access[].revoked_byThe account that withdrew the appointment.
medical_access[].revoked_atWhen it was withdrawn.

sites.json

A list, one object per site, archived ones included: the site's details and hours, the employers taking part, the workers' assignments, past ones included, the delegated tablets' links and the inspector access codes with the sessions opened with each.

The site's punch code, a tablet's token and an inspector's code are credentials and are not here. An inspector session carries the address, masked, and the browser, as the office reads them in the code's use log.

JSON Schema: sites.schema.json

The fields of sites.json
FieldMeaning
idThe site's id.
codeThe site's code, which never changes.
nameThe site's name.
addressAddress.
cityMunicipality.
provinceProvince code.
postal_codePostcode.
client_nameThe client.
client_tax_codeThe client's tax code or VAT number.
works_directorThe works director.
safety_coordinatorThe safety coordinator.
starts_onWhen the works start.
planned_end_onThe planned end.
actual_end_onThe actual end.
contract_valueThe contract value, in euro, written as text.
statusThe site's status.
geo_latThe geofence's latitude, as text.
geo_lngThe geofence's longitude, as text.
geo_radius_mThe geofence's radius, in metres.
geofence_enabledWhether self-service punches ask for the position.
work_starts_atStart of the working hours (local time); null: the workspace's.
work_ends_atEnd of the working hours (local time); null: the workspace's.
break_minutesBreak minutes; null: the workspace's.
notesThe office's notes.
deleted_atWhen it was archived.
plan_held_atWhen it was suspended by the plan's limits.
created_byThe account that created it.
created_atWhen it was created.
updated_atWhen it was last changed.
employersThe employers taking part in the site.
employers[].idThe participation's id.
employers[].employer_idThe employer.
employers[].roleRole: contractor, subcontractor or self-employed worker.
employers[].subcontract_authorizationThe details of the subcontract authorisation.
employers[].subcontract_authorized_onThe day of the subcontract authorisation.
employers[].created_byThe account that recorded it.
employers[].created_atWhen it was created.
employers[].updated_atWhen it was last changed.
assignmentsThe workers' assignments, past ones included.
assignments[].idThe assignment's id.
assignments[].worker_idThe worker.
assignments[].roleThe task on the site.
assignments[].starts_onThe assignment's start.
assignments[].ends_onThe assignment's end.
assignments[].accreditedWhether the worker is accredited on the site.
assignments[].accredited_atWhen the worker was accredited.
assignments[].accredited_byThe account that accredited the worker.
assignments[].created_byThe account that recorded it.
assignments[].created_atWhen it was created.
assignments[].updated_atWhen it was last changed.
gate_linksThe site's delegated tablet links; their token is not here.
gate_links[].idThe link's id.
gate_links[].labelThe tablet's name.
gate_links[].expires_atWhen it expires.
gate_links[].revoked_atWhen it was revoked.
gate_links[].revoked_byThe account that revoked it.
gate_links[].created_byThe account that created it.
gate_links[].last_used_atWhen it was last used.
gate_links[].use_countUses.
gate_links[].created_atWhen it was created.
gate_links[].updated_atWhen it was last changed.
access_grantsThe site's inspector access codes, with the log of their use; the code is not here, nor any part of it.
access_grants[].idThe code's id.
access_grants[].kindFor whom: safety coordinator, labour inspectorate, local health authority (ASL), client, other.
access_grants[].labelThe holder's name, required for other.
access_grants[].legacyWhether it comes from the previous system.
access_grants[].expires_atWhen it expires.
access_grants[].revoked_atWhen it was revoked.
access_grants[].revoked_byThe account that revoked it.
access_grants[].created_byThe account that created it.
access_grants[].use_countSessions opened with the code.
access_grants[].last_used_atWhen it was last used.
access_grants[].created_atWhen it was created.
access_grants[].updated_atWhen it was last changed.
access_grants[].usesThe sessions opened with the code.
access_grants[].uses[].idThe session's id.
access_grants[].uses[].started_atWhen it was opened.
access_grants[].uses[].expires_atWhen it expires.
access_grants[].uses[].last_seen_atWhen it was last read.
access_grants[].uses[].ended_atWhen it was closed.
access_grants[].uses[].ended_reasonWhy it closed: expired, code expired, code revoked, site archived.
access_grants[].uses[].ipThe address it was opened from, masked.
access_grants[].uses[].user_agentThe browser it was opened from.
access_grants[].uses[].readsReads made.

workers.json

A list, one object per worker, deactivated ones included: the worker's details, the photo's path, the requirements (training and the fitness judgment), the training reviews, the identity checks, the badge with its verdict and reasons, and the history of its states. The token that opens the badge is a credential and is not here.

A fitness judgment appears, as the office reads it without named access, only while it selects no outcome; once it selects one, only its existence, its kind and its version remain, with withheld set to true, and its content leaves through the medical archive. A verdict's reasons never name a health cause. An identity check for medical documents appears without its reference (verification_ref is null), which leaves through the medical archive.

The archive carries what the office sees: the version field is on the requirements, where the worker's page shows it, and not on the other rows, where it is only the internal check on concurrent changes.

JSON Schema: workers.schema.json

The fields of workers.json
FieldMeaning
idThe worker's id.
employer_idThe employer the worker works for.
user_idThe account linked to the worker, if any.
first_nameFirst name.
last_nameSurname.
tax_codeTax code, sixteen characters.
birth_dateDate of birth.
birth_placePlace of birth.
nationalityCitizenship.
addressAddress.
phonePhone.
emailEmail.
job_titleJob title.
hired_onDate of hiring.
employment_ended_onThe end of the employment.
employer_changed_atWhen the worker last changed employer.
badge_exemptWhether the worker is exempt from the badge.
badge_exempt_reasonWhy the worker is exempt from the badge.
photo_updated_atWhen the photo was last changed.
notesThe office's notes.
training_basisThe training rule applied: previous combined record, specific training, or refresher.
training_review_requiredWhether the office must review the training before it is valid again.
deleted_atWhen the worker was deactivated.
disposed_atWhen the worker's data were erased.
plan_held_atWhen it was suspended by the plan's limits.
created_byThe account that created it.
created_atWhen it was created.
updated_atWhen it was last changed.
photoThe photo's path in the archive (photos/<id>.jpg), or null if there is none.
factsThe worker's requirements.
facts[].idThe requirement's id.
facts[].kindKind: general, specific, refresher or foreman training, other training, basic training (previous combined record) or fitness judgment.
facts[].titleThe title of another training course (training_other).
facts[].completed_onThe day it was obtained.
facts[].expires_onThe day it expires; the requirement is valid through that day.
facts[].referenceThe reference of the certificate or of the document.
facts[].noteThe office's note.
facts[].document_idThe document that supports the requirement, if there is one.
facts[].created_atWhen it was created.
facts[].updated_atWhen it was last changed.
facts[].sourceOrigin: entered by hand, applied by the review of a document, or imported from the previous system. null on a withheld judgment.
facts[].document_revision_idThe document revision cited as evidence.
facts[].document_review_idThe office's review that applied the requirement.
facts[].claim_idThe review's claim that wrote the requirement.
facts[].decision_idAlways null in this archive: a judgment that selects an outcome is withheld, and the outcomes leave through the medical archive.
facts[].versionThe requirement's version, which grows with every change.
facts[].providerThe training body that delivered the course (training only).
facts[].hoursThe course's length in hours, above zero, at most one decimal (training only), written as text.
facts[].delivery_modeHow the course was delivered (training only).
facts[].placeWhere the course took place (training only).
facts[].created_byThe account that recorded the requirement; it stays on a withheld judgment too.
facts[].withheldtrue if the requirement is a fitness judgment that selects an outcome, withheld from this archive.
training_reviewsThe training reviews the office made.
training_reviews[].idThe review's id.
training_reviews[].kindKind: conversion, review, withdrawal or correction.
training_reviews[].basis_beforeThe training rule before.
training_reviews[].basis_afterThe training rule after.
training_reviews[].general_fact_idThe general training requirement considered.
training_reviews[].specific_fact_idThe specific training requirement considered.
training_reviews[].refresher_fact_idThe refresher requirement considered.
training_reviews[].snapshotThe training requirements at the time of the review: a free-form object, in the shape the app used at the time.
training_reviews[].applicability_refThe reference the rule applied rests on.
training_reviews[].reviewer_idThe account that made the review.
training_reviews[].reviewed_atWhen the review was made.
identity_checksThe identity checks the office made before the worker's app shows one of the worker's restricted or medical documents.
identity_checks[].idThe check's id.
identity_checks[].user_idThe account of the worker checked.
identity_checks[].scopeFor which documents.
identity_checks[].employer_idThe employer, for medical documents.
identity_checks[].custodian_idThe account that made the check.
identity_checks[].verified_atWhen the check was made.
identity_checks[].verification_refThe document shown or the check's reference; null for a check of medical documents, whose reference leaves through the medical archive.
identity_checks[].revoked_byThe account that withdrew it.
identity_checks[].revoked_atWhen it was withdrawn.
badgeThe worker's badge, or null if it was never issued. The token that opens it is a credential and is not here.
badge.idThe badge's id.
badge.issued_atWhen it was issued.
badge.expires_atWhen it expires.
badge.revoked_atWhen it was revoked.
badge.revoked_reasonWhy it was revoked.
badge.revoked_byThe account that revoked it.
badge.suspended_atWhen it was suspended.
badge.suspended_reasonWhy it was suspended.
badge.suspended_byThe account that suspended it.
badge.blocked_atWhen it was blocked.
badge.blocked_reasonWhy it was blocked.
badge.blocked_byThe account that blocked it.
badge.override_untilThe end of the override in force.
badge.override_reasonWhy the override was granted.
badge.override_byThe account that granted the override.
badge.stateVerdict: green, red or suspended.
badge.state_reasonsThe verdict's reasons.
badge.state_reasons[].codeThe rule's code (for example missing_durc, training_specific_expired, override_active); overridden:<code> for a reason an override lifted.
badge.state_reasons[].detailThe text that goes with the reason, where the rule carries one.
badge.state_reasons[].untilThe day the reason refers to: an expiry or the end of an override.
badge.warningsThe verdict's warnings.
badge.warnings[].codeThe rule's code (for example missing_durc, training_specific_expired, override_active); overridden:<code> for a reason an override lifted.
badge.warnings[].detailThe text that goes with the reason, where the rule carries one.
badge.warnings[].untilThe day the reason refers to: an expiry or the end of an override.
badge.state_computed_atWhen the verdict was computed.
badge.created_atWhen it was created.
badge.updated_atWhen it was last changed.
badge_historyThe badge's changes of state.
badge_history[].idThe change's id.
badge_history[].from_stateThe state before.
badge_history[].to_stateThe state after.
badge_history[].reasonsThe reasons for the new state.
badge_history[].reasons[].codeThe rule's code (for example missing_durc, training_specific_expired, override_active); overridden:<code> for a reason an override lifted.
badge_history[].reasons[].detailThe text that goes with the reason, where the rule carries one.
badge_history[].reasons[].untilThe day the reason refers to: an expiry or the end of an override.
badge_history[].causeWhat moved the verdict.
badge_history[].actor_user_idThe account that acted, if any.
badge_history[].created_atWhen the state changed.

vehicles.json

A list, one object per vehicle, archived ones included: registered and unregistered vehicles, rented ones, machines and equipment, with the site they are on, the inspection, the insurance and the rental.

JSON Schema: vehicles.schema.json

The fields of vehicles.json
FieldMeaning
idThe vehicle's id.
kindKind: registered vehicle, unregistered, rented, machine, equipment, other.
nameName.
plateNumber plate.
makeMake.
modelModel.
yearYear.
serialSerial number.
site_idThe site it is on.
inspection_kindInspection: with an expiry, not recorded, not applicable.
inspection_expires_onThe inspection's expiry.
inspection_revision_idThe document revision that proves it.
insurance_kindInsurance: with an expiry, not recorded, not applicable.
insurance_expires_onThe insurance's expiry.
insurance_revision_idThe document revision that proves it.
rental_companyRental company.
rental_refThe rental contract's reference.
rental_starts_onThe rental's start.
rental_ends_onThe rental's end.
rental_contract_document_idThe rental contract's document.
notesThe office's notes.
archived_atWhen it was archived.
plan_held_atWhen it was suspended by the plan's limits.
created_byThe account that created it.
created_atWhen it was created.
updated_atWhen it was last changed.

documents.json

A list, one object per ordinary or restricted document not deleted, with its revisions, the retention decisions and conflicts, the holds on deletion and the office's reviews with what each one asserted. The original of every available revision is in the documents/ folder, at the path its file field gives.

Medical documents, their revisions and their history are not here: they leave through the medical archive.

JSON Schema: documents.schema.json

The fields of documents.json
FieldMeaning
idThe document's id.
site_idThe site it belongs to, if it is a site's.
worker_idThe worker it belongs to, if it is a worker's.
employer_idThe employer it belongs to, if it is an employer's.
vehicle_idThe vehicle it belongs to, if it is a vehicle's.
kindThe document's kind.
custom_kindThe kind the office wrote for an other document.
sensitivitySensitivity: ordinary or restricted.
current_revision_idThe current revision.
current_review_idThe office's review in force.
statusThe document's status.
archived_atWhen it was archived.
created_byThe account that created it.
created_atWhen it was created.
updated_atWhen it was last changed.
revisionsThe document's revisions, in order.
revisions[].idThe revision's id.
revisions[].ordinalThe revision's number within the document.
revisions[].original_filenameThe name of the uploaded file.
revisions[].content_typeThe file's type (for example application/pdf).
revisions[].byte_lengthThe file's size, in bytes.
revisions[].sha256The file's SHA-256 digest, in hexadecimal.
revisions[].availabilityWhether the original is available.
revisions[].use_stateUse: candidate, reviewed, superseded, withdrawn.
revisions[].ready_atWhen the original was accepted.
revisions[].retired_atWhen the revision went out of use.
revisions[].document_dateThe document's date.
revisions[].validity_kindValidity: with an expiry, without one, not recorded.
revisions[].expires_onThe document's expiry.
revisions[].noteThe office's note.
revisions[].retention_policy_idThe retention policy applied.
revisions[].retention_triggerThe policy's event the term runs from, as applied.
revisions[].retention_trigger_onThe day of that event.
revisions[].retention_deadlineThe day until which it is to be kept.
revisions[].created_atWhen it was created.
revisions[].updated_atWhen it was last changed.
revisions[].fileThe original's path in the archive (documents/<document>/<revision>.<extension>), or null when the original is not available.
revisions[].retention_decisionsThe office's retention decisions on the revision.
revisions[].retention_decisions[].idThe decision's id.
revisions[].retention_decisions[].decisionApply the policy, keep, delete.
revisions[].retention_decisions[].policy_beforeThe policy before.
revisions[].retention_decisions[].policy_afterThe policy after.
revisions[].retention_decisions[].deadline_beforeThe term before.
revisions[].retention_decisions[].deadline_afterThe term after.
revisions[].retention_decisions[].reasonThe reason.
revisions[].retention_decisions[].decided_byThe account that decided.
revisions[].retention_decisions[].decided_atWhen it was decided.
revisions[].retention_conflictsThe retention conflicts found on the revision, resolved ones included.
revisions[].retention_conflicts[].idThe conflict's id.
revisions[].retention_conflicts[].reasonWhy: the revision proves a requirement, the subject became active again, the event is not plausible.
revisions[].retention_conflicts[].created_atWhen it was found.
revisions[].retention_conflicts[].resolved_atWhen it was resolved.
revisions[].retention_conflicts[].decision_idThe decision that resolved it.
holdsThe holds on deletion placed on the document, released ones included.
holds[].idThe hold's id.
holds[].authority_refThe reference of the authority or of the act.
holds[].placed_byThe account that placed it.
holds[].placed_atWhen it was placed.
holds[].review_onThe day it is to be reviewed.
holds[].released_byThe account that released it.
holds[].released_atWhen it was released.
reviewsThe office's reviews of the document, with what each one asserted.
reviews[].idThe review's id.
reviews[].revision_idThe document revision reviewed.
reviews[].previous_review_idThe office's previous review.
reviews[].reviewer_idThe account that made it.
reviews[].reviewed_atWhen the review was made.
reviews[].scopeWhat it did: filing, data, classification, withdrawal, restoration.
reviews[].snapshotThe document's data as the review saw them: a free-form object, in the shape the app used at the time.
reviews[].share_with_workerWhether the document is shown to the worker in the worker's app.
reviews[].claimsWhat the review asserted of the subject (a requirement, the DURC, a vehicle's expiry).
reviews[].claims[].idThe claim's id.
reviews[].claims[].worker_idThe worker, if the subject is a worker.
reviews[].claims[].employer_idThe employer, if the subject is an employer.
reviews[].claims[].vehicle_idThe vehicle, if the subject is a vehicle.
reviews[].claims[].claim_kindWhat it asserts.
reviews[].claims[].valuesThe values asserted (dates, references): a free-form object, in the shape of the claim's kind.

attendance_events.json

A list, one entry per punch, voided ones included, from every channel: the gate, the worker's app, the delegated tablet, manual entry, the chats, the import. Each punch carries the badge's verdict at that instant, the distance from the site and the position's accuracy: coordinates are never kept.

JSON Schema: attendance_events.schema.json

The fields of attendance_events.json
FieldMeaning
idThe punch's id.
worker_idThe worker.
site_idThe site.
kindIn or out.
occurred_atWhen the punch happened.
device_atThe instant by the device's clock, where it differs.
clock_offset_sThe device clock's offset, in seconds.
offlineWhether it was recorded offline and sent later.
channelChannel: gate, worker's app, delegated tablet, manual entry, WhatsApp, Telegram, import.
identified_byHow the worker was identified.
gate_link_idThe delegated tablet's link, if one was used.
actor_user_idThe account that recorded the punch, if any.
badge_stateThe badge's verdict at that instant.
badge_reasonsThe reasons for that verdict.
badge_reasons[].codeThe rule's code (for example missing_durc, training_specific_expired, override_active); overridden:<code> for a reason an override lifted.
badge_reasons[].detailThe text that goes with the reason, where the rule carries one.
badge_reasons[].untilThe day the reason refers to: an expiry or the end of an override.
accuracy_mThe position's accuracy, in metres.
distance_mThe distance from the site, in metres.
geofence_enforcedWhether the geofence was applied.
justificationThe justification of a manual entry or of one by tax code.
justification_docThe reference of the supporting document.
noteNote.
presumedWhether it is an entry or an exit the app presumed.
flagsFlags on the punch (for example clock_skew, late_sync).
voided_atWhen it was voided.
voided_reasonWhy it was voided.
voided_byThe account that voided it.
ipThe address the punch came from.
user_agentThe browser or app it came from.
created_atWhen it was recorded.
updated_atWhen it was last changed (a voiding, a note).

presence.json

A list, one entry per presence interval derived from the punches: a worker's stay on a site, with its ends, the presumed ones and its length. The monthly sheets are in the presence/ folder.

JSON Schema: presence.schema.json

The fields of presence.json
FieldMeaning
idThe interval's id.
worker_idThe worker.
site_idThe site.
starts_atThe interval's start.
ends_atThe interval's end; null while it is still open.
in_event_idThe entry punch.
out_event_idThe exit punch.
presumed_inWhether the entry is presumed.
presumed_outWhether the exit is presumed.
presumed_causeWhy an end is presumed: a change of site, the end of the day, a missing entry.
minutesThe length in minutes; null while it is still open.
flagsFlags on the interval (for example double_in).

timesheet_sends.json

A list, one entry per send of the presence-hours sheets to the payroll consultant, with the month, the recipient, the counts and the outcome. The files sent are in the timesheets/ folder.

JSON Schema: timesheet_sends.schema.json

The fields of timesheet_sends.json
FieldMeaning
idThe send's id.
period_monthThe month, as its first day.
site_idThe site, if the send covers one alone.
recipient_emailThe consultant's email.
worker_countWorkers on the sheet.
hours_totalTotal presence hours, as text.
presumed_countPresumed ends on the sheet.
anomaly_countAnomalies on the sheet.
statusThe send's status.
errorThe error code of a failed send.
is_resendWhether it is a new send of the same month.
sent_byThe account that sent it.
sent_atWhen it was sent.
created_atWhen it was requested.
updated_atWhen it was last changed.
filesThe paths of the files sent in the archive (timesheets/<id>.xlsx and .csv).

imports.json

A list, one entry per spreadsheet import, dry runs included, with the summary by sheet and the outcome of every row read. The uploaded file is not here: the app deletes it after seven days.

JSON Schema: imports.schema.json

The fields of imports.json
FieldMeaning
idThe import's id.
created_byThe account that uploaded the file.
file_nameThe name of the uploaded file.
file_sizeThe file's size, in bytes.
modeDry run or applied.
optionsThe options chosen.
options.update_existingWhether existing rows were to be updated.
statusStatus.
started_atWhen it started.
finished_atWhen it finished.
summaryThe summary, null until the import has finished: for each sheet the file held (IMPRESE, CANTIERI, LAVORATORI, ASSEGNAZIONI, MEZZI) the counts by outcome, and the notices.
summary.noticesThe notices from reading the file (for example a sheet skipped, and why).
summary.*The sheet's row counts by outcome.
summary.*.createdRows that created.
summary.*.updatedRows that updated.
summary.*.skippedRows skipped.
summary.*.failedRows in error.
summary.*.refusedRows refused by the plan's limits (absent from the summaries written before 19 October 2026).
errorThe error code of a failed import.
created_atWhen the file was uploaded.
updated_atWhen it was last changed.
rowsThe outcome of every row read.
rows[].idThe report row's id.
rows[].sheetThe sheet.
rows[].row_numberThe row's number in the sheet.
rows[].keyThe row's key (for example a tax code).
rows[].outcomeOutcome.
rows[].codeThe outcome's code.
rows[].messageThe message, in Italian.
rows[].entity_typeThe kind of row written.
rows[].entity_idThe row written.
rows[].created_atThe instant.

consents.json

A list, one entry per acknowledgement of the geolocation notice by a worker of the workspace, given or withdrawn, with the notice's version. Never the address or the browser it came from.

JSON Schema: consents.schema.json

The fields of consents.json
FieldMeaning
idThe acknowledgement's id.
worker_idThe worker linked to the account that gave it.
versionThe notice's version.
grantedWhether it was given (true) or withdrawn (false).
created_atThe instant.

audit.json

A list, one row per action recorded in the workspace, as the owners read the activity log: who acted, on what, and the state before and after. Like that log it carries no address and no browser: those of the rows about an account that the account itself wrote are in that account's export.

A medical document's history is not here, and the snapshot of a row about a fitness judgment, with an outcome or not, is replaced by {"withheld": "medical"}. The row of an old update of a requirement that names no kind (worker.fact_updated) loses its dates, reference and note, and its snapshot carries {"withheld": "unclassified"} beside the rest, since it could be a fitness judgment's. The manifest counts these rows in withheld.audit_rows.

JSON Schema: audit.schema.json

The fields of audit.json
FieldMeaning
idThe row's id.
actionThe action (for example worker.created, badge.issued).
entity_typeThe kind of thing acted on.
entity_idThe thing acted on.
actor_user_idThe account that acted, if any.
actor_kindWho acted: an account, the system, an external service, the platform.
beforeThe state before: a free-form object, shaped by the action; null if there is none.
afterThe state after: a free-form object, shaped by the action; null if there is none.
created_atThe instant.

anagrafiche.xlsx

The live registry in the import template's five sheets — IMPRESE (employers), CANTIERI (sites), LAVORATORI (workers), ASSEGNAZIONI (assignments), MEZZI (vehicles) — with the same headers, in the same order: a parallel, deliberately partial format, which any spreadsheet program opens and CantiereGo imports back. The JSON files stay complete and authoritative.

Values are written as the import reads them: dates as dates (dd/mm/yyyy), numbers as numbers, choices in the template's words, a yes or a no as the template writes it. The file has no row limit of its own; an xlsx sheet holds at most 1,048,576 rows, the header included, while the JSON files have no such limit.

  • The live registry alone: the employers, sites and vehicles not archived, the workers neither deactivated nor erased, the current and future assignments; rows suspended by the plan's limits are included. Archived, deactivated or erased rows and ended assignments are in the JSON files alone: the template has no state column, and an import would bring them back live.
  • Left empty, and in the JSON files: formazione_base_data and formazione_base_scadenza (the previous combined record, which the import refuses on a worker on specific training, as every worker an import creates is), idoneita_scadenza (a medical datum) and the vehicles' id column, which identifies a vehicle in this workspace alone. General, specific and refresher training and the other courses have no column in the template and are in the JSON files alone; the foreman course is in the workbook.
  • The IMPRESE sheet carries an employer only when it has a VAT number or a tax code, the key the import recognises it by. Another employer with neither also has its workers and their assignments left out, in the JSON files alone; the workspace's own company's workers are there all the same.
  • The workers of the workspace's own company have the datore_piva and datore_cf columns empty, and the import assigns them to the destination workspace's own company. Before importing, enter the company's VAT number or tax code in the destination workspace's settings: the company's row on the IMPRESE sheet then updates it instead of creating a second one.
  • A text that begins with =, +, - or @, a tab or a carriage return is written with an apostrophe before it, so that a spreadsheet never runs it as a formula; an import keeps the apostrophe, except in a phone number.
  • The sheets are read one after another, not as one snapshot: a row created while the archive is being written can name a code that a sheet read earlier does not carry.
  • An import reads a sheet of at most 5,000 rows (a longer one is skipped) from a file of at most 10 MiB: a larger registry goes back in several files, each within these limits, the employers and sites before the workers and assignments that name them.
  • Imported back into the same workspace with existing rows updated, the file has every row whose DURC or foreman course a document supports refused, with its error in the import's report: those data are corrected from the document's review. In the same workspace, a worker of an employer suspended by the plan's limits is refused, as the app would refuse it.

schemas/

The JSON Schemas of the archive's JSON files, the same ones published on this page: manifest.schema.json and one for each file, named after the file with .schema.json.

presence/

One CSV file for every month with at least one presence, presence/<YYYY-MM>.csv: the month's presence-hours sheet as the office downloads it, one row per worker, day and site. It is in Italian, separated by semicolons, in UTF-8 with a BOM, with the hours in decimals and a decimal comma.

timesheets/

The files sent to the payroll consultant, timesheets/<id>.xlsx and timesheets/<id>.csv, byte for byte as they left: read again, never rebuilt. The id is the send's in timesheet_sends.json.

photos/

The photo of every worker who has one, photos/<id>.jpg, in JPEG as the app keeps it. The id is the worker's in workers.json.

documents/

The original of every available revision of the documents in documents.json, documents/<document>/<revision>.<extension>, read from its exact version: its SHA-256 digest is in the revision's sha256 field. Never a medical document. A revision held by an open retention conflict carries no original until the conflict is resolved; a missing original is listed in the manifest, in missing.originals.

The medical archive

Fitness judgments with an outcome, the outcomes and medical documents are not in the workspace archive: in the office, only those the employer named with named access to its judgments read them, and they leave through an archive of their own, the medical archive, in the cantierego-medical-export format, under the same version and the same rule.

It is requested, from Settings › Documents, by a member who holds named access to the judgments of at least one employer, signed in with two-step verification. It covers the employers that access covers when it is requested and still covers when the archive is written: for their workers it holds the fitness judgments, with an outcome or not, the outcomes with the reference of the review of the restrictions, the medical documents with their history and their originals, and the identity checks for medical documents with their reference, these only for the employers whose checks the requester reads in the app: every one for an owner, those they are the medical custodian of for anyone else, none for whoever holds read-only access.

One can be requested a day. The archive belongs to whoever requested it: only they download it, while they still hold access to every employer it covers, for 14 days after it is prepared.

If nobody holds named access, an owner names themselves on the employer's page, with two-step verification, and requests the archive: this is how the company reaches its own medical records. An owner can name themselves on an active or a closed workspace, not on a suspended one. On a suspended or closed workspace only an owner who holds named access can request and download the medical archive: a holder who is not an owner should request and download it while the workspace is active.

A fitness judgment appears in the workspace archive, as the office reads it, only while it selects no outcome; once it selects one, its content is in the medical archive.

manifest.json — medical archive

The medical archive's manifest: the format and its version, the workspace, who requested it and the employers their named access covered, the instant and the day it was written, the counts, and which originals were missing, with the reason.

JSON Schema: medical/manifest.schema.json

The fields of manifest.json — medical archive
FieldMeaning
formatThe archive's format.
format.nameThe format's name.
format.versionThe format's version, under the same rule as the workspace archive's.
export_idThe export's id.
workspaceThe workspace exported.
workspace.idThe workspace's id.
workspace.nameName.
workspace.slugThe short name in the app's address.
workspace.legal_nameLegal name.
requested_byThe account that requested the archive.
employersThe employers whose judgments the requester's named access covered.
employers[].idThe employer's id.
employers[].legal_nameLegal name.
generated_atWhen the archive was written.
dayThe workspace's day on which the archive was written.
timezoneThe workspace's time zone.
app_versionThe version of the app that wrote the archive.
read_modeHow the records were read: one after another, a page at a time.
read_mode_noteWhat the sequential reading implies.
read_mode_note.itThe text in Italian.
read_mode_note.enThe text in English.
filesFor each JSON file the rows written, for each folder the files.
files.workers.jsonWorkers in workers.json.
files.schemas/Schemas in the schemas/ folder.
files.documents/Originals in the documents/ folder.
missingThe files the archive should have held and did not find.
missing.originalsRevisions whose original was missing.
missing.originals_reasonWhy the originals are missing: null if only a few are missing that the file store no longer held; document_reads_disabled if document reading was suspended for an incident and no original was read, every one then listed in originals.

workers.json — medical archive

A list, one object per worker of the employers covered, or with a medical record recorded under one of them: the fitness judgments, the outcomes with their reference, the medical documents with their revisions, the office's reviews, the retention decisions and conflicts and the holds, and the identity checks for medical documents with their reference, of those employers alone whose checks the requester reads in the app (every one for an owner, those they are the medical custodian of for anyone else, none for whoever holds read-only access). Each record sits under the employer it was recorded for; a judgment with no outcome under the worker's current employer.

JSON Schema: medical/workers.schema.json

The fields of workers.json — medical archive
FieldMeaning
workerThe worker.
worker.idThe worker's id.
worker.employer_idThe employer the worker works for.
worker.first_nameFirst name.
worker.last_nameSurname.
worker.tax_codeTax code, sixteen characters.
worker.deleted_atWhen the worker was deactivated.
worker.disposed_atWhen the worker's data were erased.
factsThe worker's fitness judgments.
facts[].idThe requirement's id.
facts[].kindKind: fitness judgment.
facts[].titleThe title of another training course (training_other).
facts[].completed_onThe day it was obtained.
facts[].expires_onThe day it expires; the requirement is valid through that day.
facts[].referenceThe reference of the certificate or of the document.
facts[].noteThe office's note.
facts[].document_idThe document that supports the requirement, if there is one.
facts[].created_atWhen it was created.
facts[].updated_atWhen it was last changed.
facts[].sourceOrigin: entered by hand, applied by the review of a document, or imported from the previous system. null on a withheld judgment.
facts[].document_revision_idThe document revision cited as evidence.
facts[].document_review_idThe office's review that applied the requirement.
facts[].claim_idThe review's claim that wrote the requirement.
facts[].decision_idThe outcome the judgment selects.
facts[].versionThe requirement's version, which grows with every change.
facts[].providerThe training body that delivered the course (training only).
facts[].hoursThe course's length in hours, above zero, at most one decimal (training only), written as text.
facts[].delivery_modeHow the course was delivered (training only).
facts[].placeWhere the course took place (training only).
facts[].created_byThe account that recorded the requirement; it stays on a withheld judgment too.
facts[].withheldAlways false: in this archive no judgment is withheld.
decisionsThe medical outcomes recorded.
decisions[].idThe outcome's id.
decisions[].employer_idThe employer the judgment is answerable to.
decisions[].previous_decision_idThe outcome this one replaces.
decisions[].outcomeOutcome: fit, fit with restrictions, temporarily unfit, permanently unfit, not classified (previous system).
decisions[].assessed_onThe day of the examination.
decisions[].valid_untilThe judgment's validity.
decisions[].next_review_onThe next examination.
decisions[].eligibility_untilUntil when the worker is admitted.
decisions[].boundary_basisWhat the admission limit rests on.
decisions[].boundary_refThe limit's reference.
decisions[].context_versionThe version of the context it was recorded in.
decisions[].applicabilityWhether the outcome is confirmed or to be reviewed.
decisions[].review_refThe reference of the review of the restrictions.
decisions[].retention_policy_idThe retention policy applied.
decisions[].document_idThe medical document that proves it.
decisions[].revision_idThe document's revision.
decisions[].review_idThe office's review.
decisions[].correction_ofThe outcome this one corrects.
decisions[].correction_refThe correction's reference.
decisions[].actor_idThe account that recorded it.
decisions[].created_atWhen it was recorded.
decisions[].policy_review_overdueWhether the retention policy was due for review.
documentsThe medical documents not deleted.
documents[].idThe document's id.
documents[].kindThe document's kind.
documents[].custom_kindThe kind the office wrote for an other document.
documents[].sensitivitySensitivity: always medical.
documents[].medical_employer_idThe employer the document is answerable to.
documents[].current_revision_idThe current revision.
documents[].current_review_idThe office's review in force.
documents[].statusThe document's status.
documents[].archived_atWhen it was archived.
documents[].created_byThe account that created it.
documents[].created_atWhen it was created.
documents[].updated_atWhen it was last changed.
documents[].revisionsThe document's revisions, in order.
documents[].revisions[].idThe revision's id.
documents[].revisions[].ordinalThe revision's number within the document.
documents[].revisions[].original_filenameThe name of the uploaded file.
documents[].revisions[].content_typeThe file's type (for example application/pdf).
documents[].revisions[].byte_lengthThe file's size, in bytes.
documents[].revisions[].sha256The file's SHA-256 digest, in hexadecimal.
documents[].revisions[].availabilityWhether the original is available.
documents[].revisions[].use_stateUse: candidate, reviewed, superseded, withdrawn.
documents[].revisions[].ready_atWhen the original was accepted.
documents[].revisions[].retired_atWhen the revision went out of use.
documents[].revisions[].document_dateThe document's date.
documents[].revisions[].validity_kindValidity: with an expiry, without one, not recorded.
documents[].revisions[].expires_onThe document's expiry.
documents[].revisions[].noteThe office's note.
documents[].revisions[].retention_policy_idThe retention policy applied.
documents[].revisions[].retention_triggerThe policy's event the term runs from, as applied.
documents[].revisions[].retention_trigger_onThe day of that event.
documents[].revisions[].retention_deadlineThe day until which it is to be kept.
documents[].revisions[].created_atWhen it was created.
documents[].revisions[].updated_atWhen it was last changed.
documents[].revisions[].fileThe original's path in the archive (documents/<document>/<revision>.<extension>), or null when the original is not available.
documents[].revisions[].retention_decisionsThe office's retention decisions on the revision.
documents[].revisions[].retention_decisions[].idThe decision's id.
documents[].revisions[].retention_decisions[].decisionApply the policy, keep, delete.
documents[].revisions[].retention_decisions[].policy_beforeThe policy before.
documents[].revisions[].retention_decisions[].policy_afterThe policy after.
documents[].revisions[].retention_decisions[].deadline_beforeThe term before.
documents[].revisions[].retention_decisions[].deadline_afterThe term after.
documents[].revisions[].retention_decisions[].reasonThe reason.
documents[].revisions[].retention_decisions[].decided_byThe account that decided.
documents[].revisions[].retention_decisions[].decided_atWhen it was decided.
documents[].revisions[].retention_conflictsThe retention conflicts found on the revision, resolved ones included.
documents[].revisions[].retention_conflicts[].idThe conflict's id.
documents[].revisions[].retention_conflicts[].reasonWhy: the revision proves a requirement, the subject became active again, the event is not plausible.
documents[].revisions[].retention_conflicts[].created_atWhen it was found.
documents[].revisions[].retention_conflicts[].resolved_atWhen it was resolved.
documents[].revisions[].retention_conflicts[].decision_idThe decision that resolved it.
documents[].holdsThe holds on deletion placed on the document, released ones included.
documents[].holds[].idThe hold's id.
documents[].holds[].authority_refThe reference of the authority or of the act.
documents[].holds[].placed_byThe account that placed it.
documents[].holds[].placed_atWhen it was placed.
documents[].holds[].review_onThe day it is to be reviewed.
documents[].holds[].released_byThe account that released it.
documents[].holds[].released_atWhen it was released.
documents[].reviewsThe office's reviews of the document, with what each one asserted.
documents[].reviews[].idThe review's id.
documents[].reviews[].revision_idThe document revision reviewed.
documents[].reviews[].previous_review_idThe office's previous review.
documents[].reviews[].reviewer_idThe account that made it.
documents[].reviews[].reviewed_atWhen the review was made.
documents[].reviews[].scopeWhat it did: filing, data, classification, withdrawal, restoration.
documents[].reviews[].snapshotThe document's data as the review saw them: a free-form object, in the shape the app used at the time.
documents[].reviews[].share_with_workerWhether the document is shown to the worker in the worker's app.
documents[].reviews[].claimsWhat the review asserted of the subject, the fitness judgment included.
documents[].reviews[].claims[].idThe claim's id.
documents[].reviews[].claims[].worker_idThe worker, if the subject is a worker.
documents[].reviews[].claims[].employer_idThe employer, if the subject is an employer.
documents[].reviews[].claims[].vehicle_idThe vehicle, if the subject is a vehicle.
documents[].reviews[].claims[].claim_kindWhat it asserts.
documents[].reviews[].claims[].valuesThe values asserted (dates, references): a free-form object, in the shape of the claim's kind.
identity_checksThe identity checks the employer's medical custodian made before the worker's app shows one of the worker's medical documents, with their reference. Only for the employers whose checks the requester reads in the app: every one for an owner, those they are the medical custodian of for anyone else; none for whoever holds read-only access.
identity_checks[].idThe check's id.
identity_checks[].user_idThe account of the worker checked.
identity_checks[].scopeFor which documents: always the medical ones.
identity_checks[].employer_idThe employer whose medical documents the check opens.
identity_checks[].custodian_idThe account that made the check.
identity_checks[].verified_atWhen the check was made.
identity_checks[].verification_refThe document shown or the check's reference.
identity_checks[].revoked_byThe account that withdrew it.
identity_checks[].revoked_atWhen it was withdrawn.

schemas/ — medical archive

The medical archive's two schemas, manifest.schema.json and workers.schema.json, the same ones published here under /formato-esportazione/1/medical/.

documents/ — medical archive

The original of every available revision of the medical documents, documents/<document>/<revision>.<extension>, read from its exact version. The manifest's missing.originals and missing.originals_reason are authoritative.

What the archive does not hold

  • Your account's data. Your profile, sessions, consents and the log rows about you are downloaded from your account (Account › Data and privacy), in a separate file this format does not describe.
  • Billing. The subscription and the invoices are read and downloaded on Settings › Plan and billing; the fiscal ledger stays with A176lab, which keeps it ten years.
  • Credentials. The sites' punch codes, the tokens of badges and delegated tablets, the digests of inspector codes and invitations and the keys the app stores files under are not here; the photos, the sheets sent and the originals, on the other hand, are, as files.
  • Medical records with an outcome. Fitness judgments with an outcome, the outcomes and medical documents leave through the medical archive.
  • The service's internal working: queues, attempts, counters and the message diagnostics (the bots' deliveries, kept 30 days). The next section lists every table that stays out, and why.
  • A snapshot. The families of records are read one after another, a page at a time: a record written while the archive is being written can appear in one file and not in another. The manifest says so in its read_mode field.

Every table, in the archive or out of it

Every table with a workspace_id column, and the workspace's own row, is here, with where it lands in the archive or why it stays out. The path gives the file and the keys its rows are nested under: sites.json/access_grants/uses are the sessions inside each inspector code, inside each site. The list is the one the service writes the archive by, and its tests fail if a table appears in the database that is not on it. members.json and consents.json also read the linked accounts: the members' names, emails and phones, the workers' acknowledgements.

The tables' workspace_id column is not repeated: the archive is one workspace's. Every other column is there, except those listed, each for one of these reasons: a credential or its digest; the service's internal working (a change counter, a storage key, a reference the service keeps for itself); a datum the archive already gives another way (already given: the parent an entry is nested under, a document's subject its revisions would repeat); or a column the office's view does not show, since the archive carries what the office sees.

Every table, in the archive or out of it
TableIn the archiveColumns left out, and why
workspaces workspace.json suspended_by, deleted_at, purged_at, purge_counts (internal working). suspended_by is the platform administrator who suspended the workspace; deleted_at, purged_at and purge_counts belong to the final deletion, never set on a workspace that still exists.
document_retention_policies workspace.json/retention_policies None.
bot_workspace_settings workspace.json/bot_settings version (internal working).
memberships members.json/members None.
invitations members.json/invitations token_hash (a credential).
bot_workspace_links members.json/bot_links connection_id, epoch, worker_link_epoch, notice_consent_id, version (internal working). connection_id, the account's connection to the chat, is in the account's export.
bot_notification_subscriptions members.json/bot_subscriptions connection_id, connection_proactive_epoch, version (internal working).
bot_consent_events members.json/bot_consents connection_id, instance_id (internal working).
employers employers.json version (internal working).
employer_document_policies employers.json/document_policy employer_id (already given); version (internal working).
employer_medical_access employers.json/medical_access employer_id (already given).
sites sites.json punch_code (a credential).
site_employers sites.json/employers site_id (already given).
site_workers sites.json/assignments site_id (already given).
gate_links sites.json/gate_links site_id (already given); token_hash (a credential).
access_grants sites.json/access_grants site_id (already given); code_hash, code_prefix (a credential); legacy_id, legacy_revoked (internal working). code_hash and code_prefix are the code's digest and the four characters that index it; legacy_id and legacy_revoked, the mapping to the previous system.
access_grant_uses sites.json/access_grants/uses grant_id (already given); token_hash (a credential). The address, masked, and the browser are there, as the office reads them in the use log.
workers workers.json photo_key, link_epoch, compliance_context_version, version (internal working). The photo is photos/<id>.jpg.
worker_compliance_facts workers.json/facts worker_id (already given). A fitness judgment that selects an outcome stays without its content, with withheld set to true; version is there, as the worker's page shows it.
worker_training_reviews workers.json/training_reviews worker_id (already given); context_version (internal working).
worker_document_identity_checks workers.json/identity_checks worker_id (already given); link_epoch (internal working). For a check of medical documents verification_ref is null: it leaves through the medical archive.
badges workers.json/badge worker_id (already given); attention_transition_id (internal working). The badge's token is in another table, badge_tokens, which stays out.
badge_state_changes workers.json/badge_history badge_id (already given); health_employer_id (internal working). health_employer_id tells which employer's health rule decided a state: a reference the service keeps for its own reading of the verdict, not a datum of the customer's; the health cause it points at is taken out of the reasons.
vehicles vehicles.json plate_normalized (already given); version (internal working). The plate as written is there.
documents documents.json deleted_at (already given); medical_employer_id (in the medical archive); version (internal working). A deleted document is not in the archive; a medical document is in the medical archive.
document_revisions documents.json/revisions document_id, worker_id, employer_id, vehicle_id (already given); upload_id, vault_key, vault_version_id, inbox_version_id, object_missing_at, version (internal working). The original is in the documents/ folder; a missing original is listed in the manifest, in missing.
retention_decisions documents.json/revisions/retention_decisions revision_id (already given).
retention_conflicts documents.json/revisions/retention_conflicts revision_id (already given).
document_holds documents.json/holds document_id (already given).
document_reviews documents.json/reviews document_id, worker_id, employer_id, vehicle_id (already given).
document_review_claims documents.json/reviews/claims review_id, document_id (already given). A fitness judgment's claim is in the medical archive.
attendance_events attendance_events.json client_id, health_employer_id (internal working). client_id is the key a device uses to avoid recording the same punch twice; health_employer_id, the same internal reference as in the badge's history.
attendance_presence presence.json created_at, updated_at (already given). period is written as starts_at, ends_at.
timesheet_sends timesheet_sends.json xlsx_key, csv_key (internal working). The files sent are in the timesheets/ folder.
imports imports.json file_key (internal working). The uploaded file is not in the archive.
import_rows imports.json/rows import_id (already given).
audit_events audit.json ip, user_agent (not shown by the office); request_id (internal working). The address and the browser of the rows about an account that the account itself wrote are in that account's export.
badge_tokens Out: a credential The tokens that open the badge's public page from its QR code.
worker_medical_decisions In the medical archive The outcomes of fitness judgments: in the medical archive, in workers.json, under decisions.
billing_accounts Out: billing records The workspace's link to the payment and e-invoicing systems, and the billing email.
billing_invoices Out: billing records The ledger of invoices and credit notes: the invoices are downloaded from Settings › Plan and billing, and the ledger stays with A176lab for ten years.
subscriptions Out: billing records The plan's subscription: read it on Settings › Plan and billing.
billing_checkouts Out: internal working Purchases in progress on the payment page, which expire.
workspace_exports Out: internal working The export requests themselves, with their state.
deletion_requests Out: internal working The requests to delete, and their progress.
erasure_intents Out: internal working The jobs that carry out a deletion, one step at a time.
document_deletions Out: internal working The jobs that delete a document or a revision from the file store.
document_processing_attempts Out: internal working The attempts to check and copy an uploaded file.
document_uploads Out: internal working An upload's reservation, before the file becomes a revision.
document_usage Out: internal working The counter of the space the documents take up.
bot_command_admissions Out: internal working The admission of the commands received by chat, with their deadlines.
bot_deliveries Out: internal working The diagnostics of the bots' message deliveries, kept 30 days: queues, attempts and the provider's keys, never a message's text.
bot_delivery_sources Out: internal working The tie between a delivery and what caused it.
bot_intents Out: internal working The hand-offs from a chat to the wallet to complete a punch.
bot_interactions Out: internal working The interactions in progress with the bot, which expire.
bot_legacy_links Out: internal working The mapping to the previous system's links.
bot_media_cleanup Out: internal working The bots' temporary files to delete.
bot_notification_sources Out: internal working The queue of the events that can raise a notice.

The JSON Schemas

Every JSON file of the two archives has a JSON Schema, draft 2020-12, written by hand and published here at the address its $id declares: every field is described, and a field the schema does not name is refused (additionalProperties: false), except in the objects the schema calls free-form, such as the activity log's snapshots. The service's tests build the archives of a test workspace and validate them against these schemas; every archive carries a copy in its schemas/ folder.

The workspace archive

The medical archive

Jurisdiction and access to the data

Article 28 of Regulation (EU) 2023/2854 asks whoever provides data processing services to state on their website the jurisdiction to which the infrastructure they process the data on is subject, and to describe the technical, organisational and contractual measures they take to prevent access by authorities of third countries to non-personal data held in the Union, or its transfer, where that would conflict with Union law or the law of the Member State concerned.

CantiereGo is provided by A176lab S.r.l., a company with its registered office in Italy, in Alcamo (TP), and the Terms of service are governed by Italian law. The infrastructure that stores the clients' data is in the AWS region of Milan (eu-south-1), in Italy: the app's server and its database, the stores of files and documents, the backups and the snapshots of the data disk, the technical logs. Emails and text messages are sent from the same region. None of this data is replicated to another region.

The pages of the site and of the app reach browsers through CloudFront, AWS's content delivery network, from the points of presence of the chosen price class, in Europe, Israel, Turkey, the United States, Mexico and Canada, and AWS notes: "From time to time, your content may be served from an edge location that is not included in your price class." A point of presence terminates the encrypted connection and does not keep the app's responses that carry the clients' data, which come from the server in Milan (section 7 of the Privacy notice).

The other providers that process data for the service include those that sections 6 and 7 of the Privacy notice list with where they process it — Sentry, for error reports, and Zoho Mail, for the info@cantierego.it mailbox, keep it in the European Union; Stripe, for payments, processes it in Ireland and may transfer it to the companies of its group in the United States — and Fatture in Cloud (TeamSystem S.p.A., Italy), through which A176lab issues and transmits its e-invoices to its clients, which keeps the data in the European Union. The client may also enable the Telegram and, from the Professional plan, WhatsApp channels for its workspace: the messages exchanged with the bot are processed by those channels' providers, Telegram and, for WhatsApp, Meta, under their own terms.

Technical measures

  • Encryption at rest: the server's disks, the database's included, are encrypted; the stores of the app's files and of the documents are encrypted under S3's AWS-managed key in AWS KMS, the other stores, the backups' included, with S3's server-side encryption.
  • Encryption in transit: browsers reach app.cantierego.it and cantierego.it only over TLS 1.3; the leg between CloudFront and the server is encrypted with at least TLS 1.2 (with only ECDHE and AES-GCM ciphers in TLS 1.2); the stores refuse every request that does not arrive encrypted.
  • Access to the infrastructure: the server holds no AWS access keys, since it signs each of its calls with the instance's role, whose permissions are limited to the stores and services the server uses; it accepts web connections only from CloudFront, which carries a secret the server checks, and has no SSH port open to the internet.

Organisational measures

  • Within A176lab only authorised staff access the data, bound to confidentiality and only as far as the service requires (Article 6.5 of the Terms of service).
  • A176lab's staff reach the AWS account through AWS IAM Identity Center, with the second factor at every sign-in, and the server over an encrypted private network, which asks for the identity again at regular intervals, or, in an emergency, through AWS itself.
  • A platform administrator acts only from a session verified with the second factor, which lasts at most twenty-four hours, and every change they make is recorded in the activity log.
  • The AWS account's management operations are recorded by AWS CloudTrail, in every region, with the logs' integrity validation.

Contractual measures

  • On the personal data the Client records, A176lab is the processor and processes it only on the Client's documented instructions, unless required to do so by Union or Member State law to which A176lab is subject; in that case it informs the Client of that legal requirement before processing, unless that law prohibits such information on important grounds of public interest (Article 6.4 of the Terms of service).
  • With A176lab and the providers listed in section 6 of the Privacy notice, the data is not transferred outside the European Union, except as section 7 of the Privacy notice describes (Article 6.4 of the Terms), which names the grounds: for the passage through CloudFront, AWS's data processing addendum and the standard contractual clauses the European Commission approved; for Stripe, the adequacy decision for the EU-US Data Privacy Framework, which Stripe, Inc. adheres to, and the standard contractual clauses. This commitment does not cover the messages of the chats with the Telegram and WhatsApp bots: A176lab's server exchanges them with Telegram and, through AWS, with Meta, which process them under their own terms.
  • Every provider listed in section 6 of the Privacy notice is bound by a data processing agreement; A176lab imposes on the sub-processors data protection obligations equivalent to those of Article 6 of the Terms, remains liable to the Client for them, and tells the Client in advance of the addition or replacement of a sub-processor, to which the Client may object (Article 6.7).

The detail is in the legal texts: