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
| Field | Meaning |
|---|---|
format | The archive's format. |
format.name | The format's name. |
format.version | The 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_id | The export's id. |
workspace | The workspace exported. |
workspace.id | The workspace's id. |
workspace.name | Name. |
workspace.slug | The short name in the app's address. |
workspace.legal_name | Legal name. |
generated_at | When the archive was written. |
day | The workspace's day on which the archive was written. |
timezone | The workspace's time zone. |
app_version | The version of the app that wrote the archive. |
read_mode | How the families of records were read: one after another, a page at a time. |
read_mode_note | What the sequential reading implies. |
read_mode_note.it | The text in Italian. |
read_mode_note.en | The text in English. |
files | For each JSON file the rows written, for each folder the files. |
files.workspace.json | Rows in workspace.json: always 1. |
files.members.json | Members plus invitations in members.json. |
files.employers.json | Employers in employers.json. |
files.sites.json | Sites in sites.json. |
files.workers.json | Workers in workers.json. |
files.vehicles.json | Vehicles in vehicles.json. |
files.documents.json | Documents in documents.json. |
files.attendance_events.json | Punches in attendance_events.json. |
files.presence.json | Intervals in presence.json. |
files.timesheet_sends.json | Sends in timesheet_sends.json. |
files.imports.json | Imports in imports.json. |
files.consents.json | Acknowledgements in consents.json. |
files.audit.json | Rows in audit.json. |
files.anagrafiche.xlsx | Rows 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. |
withheld | What the archive does not hold, for medical confidentiality. |
withheld.medical | The total of medical documents, outcomes and judgments withheld. |
withheld.decisions | Medical outcomes withheld. |
withheld.documents | Medical documents withheld. |
withheld.facts | Fitness judgments withheld because they select an outcome. |
withheld.audit_rows | Activity-log rows left out or masked for medical confidentiality. |
withheld.by_employer | The 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.note | What is withheld and where it is. |
withheld.note.it | The text in Italian. |
withheld.note.en | The text in English. |
missing | The files the archive should have held and did not find. |
missing.photos | Workers whose photo was missing. |
missing.originals | Revisions whose original was missing. |
missing.timesheets | The paths of the sent files that were missing. |
missing.originals_reason | Why 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
| Field | Meaning |
|---|---|
id | The workspace's id. |
slug | The workspace's short name in the app's address (/w/<slug>). |
name | The workspace's name. |
legal_name | Legal name. |
vat_number | VAT number. |
tax_code | The company's tax code. |
sdi_code | The SdI recipient code for electronic invoicing. |
pec | Certified email address (PEC). |
address | The company's registered office. |
address.street | Street and number. |
address.cap | Postcode, five digits. |
address.city | Municipality. |
address.province | Province code, two letters. |
address.country | Country, a two-letter ISO 3166-1 code (IT when not given). |
phone | Phone. |
timezone | The IANA time zone the workspace reads its days in (for example Europe/Rome). |
settings | The workspace's settings by section: an empty object until the office has saved one; each section present is written whole. |
settings.attendance | The attendance settings, saved by the office. |
settings.attendance.red_badge_policy | A self-service punch with a red badge: recorded or refused. |
settings.attendance.work_start | Start of the working hours (local time). |
settings.attendance.work_end | End of the working hours (local time). |
settings.attendance.break_minutes | Break minutes. |
settings.attendance.retention_years | Years attendance is kept after the end of the employment. |
settings.attendance.legal_hold | Whether the deletion of attendance is suspended by a legal obligation. |
settings.attendance.consultant | The payroll consultant the monthly hours are sent to, or null. |
settings.attendance.consultant.name | Name. |
settings.attendance.consultant.email | Email. |
settings.attendance.consultant.note | Note. |
settings.compliance | The compliance policy, saved by an owner. |
settings.compliance.strict | Whether missing requirements turn the badge red instead of staying warnings. |
created_by | The account that created the workspace. |
created_at | When it was created. |
updated_at | When it was last changed. |
suspended_at | When the platform suspended the workspace. |
suspended_reason | The reason for the suspension given to the owners. |
closed_at | When it was closed. |
closed_by | The account of the owner who closed the workspace. |
closed_source | Who closed the workspace: an owner or the platform. |
purge_after | The instant from which the closed workspace's deletion may begin. |
retention_policies | The document retention policies, every version the office wrote. |
retention_policies[].id | The policy's id. |
retention_policies[].version | The policy's version for its category. |
retention_policies[].employer_id | The employer, for a policy on medical documents; null otherwise. |
retention_policies[].category | The category of documents: ordinary, restricted, medical, vehicles'. |
retention_policies[].purpose | The purpose of keeping them. |
retention_policies[].trigger | The 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_trigger | For a superseded revision: the term runs from its replacement, or as for the current revision. |
retention_policies[].fallback | The fallback event when the main one does not come. |
retention_policies[].retention_months | Months kept after the event. |
retention_policies[].justification_ref | The legal or contractual reference. |
retention_policies[].review_on | The day the policy is to be reviewed. |
retention_policies[].created_by | The account that wrote it. |
retention_policies[].created_at | When it was created. |
bot_settings | The workspace's chat channels and the times of its two scheduled notices; null if they were never set. |
bot_settings.telegram_enabled | Whether the Telegram channel is open for the workspace. |
bot_settings.whatsapp_enabled | Whether the WhatsApp channel is open for the workspace. |
bot_settings.forgotten_exit_at | The time of the forgotten-exit notice. |
bot_settings.office_summary_at | The time of the office summary. |
bot_settings.updated_by | The account that set them. |
bot_settings.updated_at | When 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
| Field | Meaning |
|---|---|
members | The members, current and past. |
members[].user_id | The account's id. |
members[].full_name | The account's full name. |
members[].email | The account's email. |
members[].phone | The account's phone, in E.164 form. |
members[].role | The role in the workspace. |
members[].status | The membership's status. |
members[].created_at | When the account joined the workspace. |
members[].updated_at | When it was last changed. |
members[].ended_at | When the membership ended. |
invitations | The invitations, accepted, expired or pending. |
invitations[].id | The invitation's id. |
invitations[].role | The role offered. |
invitations[].email | The email the invitation was sent to. |
invitations[].phone | The phone the invitation was sent to. |
invitations[].worker_id | The worker the invitation links the account to. |
invitations[].expires_at | When the invitation expires. |
invitations[].accepted_at | When it was accepted. |
invitations[].accepted_user_id | The account that accepted. |
invitations[].invited_by | The account that invited. |
invitations[].created_at | When it was sent. |
bot_links | The Telegram or WhatsApp chats the members linked to the workspace. |
bot_links[].id | The link's id. |
bot_links[].user_id | The member's account. |
bot_links[].provider | The channel. |
bot_links[].member_enabled | Whether the member uses the channel in this workspace. |
bot_links[].office_enabled | Whether the office leaves the channel open to the member. |
bot_links[].worker_id | The worker the chat is tied to, if any. |
bot_links[].notice_version | The version of the channel's notice accepted. |
bot_links[].plan_held_at | When it was suspended by the plan's limits. |
bot_links[].created_at | When the chat was linked. |
bot_links[].updated_at | When it was last changed. |
bot_subscriptions | The notices the members asked for in the workspace, granted or not. |
bot_subscriptions[].id | The subscription's id. |
bot_subscriptions[].user_id | The member's account. |
bot_subscriptions[].provider | The channel. |
bot_subscriptions[].purpose | The notice: badges to watch, expiring documents, forgotten exit, the office summary. |
bot_subscriptions[].scope_id | The chat's link to the workspace. |
bot_subscriptions[].granted | Whether the notice is granted. |
bot_subscriptions[].notice_version | The version of the notice accepted. |
bot_subscriptions[].consent_event_id | The record of the consent. |
bot_subscriptions[].created_at | When it was created. |
bot_subscriptions[].updated_at | When it was last changed. |
bot_consents | Every grant or withdrawal of a notice in the workspace. |
bot_consents[].id | The record's id. |
bot_consents[].scope_id | The chat's link to the workspace. |
bot_consents[].user_id | The member's account. |
bot_consents[].provider | The channel. |
bot_consents[].action | A grant or a withdrawal of the notice. |
bot_consents[].purpose | The notice. |
bot_consents[].notice_version | The notice's version. |
bot_consents[].granted | Whether the notice was granted. |
bot_consents[].source | Where it was given: in the app, in the private chat, in the migration. |
bot_consents[].actor_user_id | The account that recorded it, if any. |
bot_consents[].created_at | The 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
| Field | Meaning |
|---|---|
id | The employer's id. |
kind | A company or a self-employed worker. |
is_own | true for the workspace's own company. |
legal_name | The legal name, or the self-employed worker's full name. |
vat_number | VAT number. |
tax_code | Tax code. |
address | Registered office. |
address.street | Street and number. |
address.cap | Postcode, five digits. |
address.city | Municipality. |
address.province | Province code, two letters. |
address.country | Country, a two-letter ISO 3166-1 code (IT when not given). |
email | Email. |
phone | Phone. |
notes | The office's notes. |
patente_status | The patente a crediti's status as the office recorded it. |
patente_exempt_reason | The 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_credits | The licence's credits. |
patente_number | The licence's number. |
patente_checked_on | The day the office checked the licence. |
patente_exception_until | The end of the exception on the credits. |
patente_exception_reason | The reason for the exception on the credits. |
patente_suspended_until | The end of the licence's suspension, when the office recorded it (only with the status suspended). |
soa_class | The class of the SOA qualification the exemption rests on (only with the ground soa_iii). |
soa_valid_until | The SOA qualification's expiry (only with the ground soa_iii). |
durc_status | The DURC's outcome as the office recorded it. |
durc_valid_until | The DURC's expiry. |
durc_document_id | The DURC document that supports the data. |
durc_revision_id | The DURC document's revision cited. |
durc_review_id | The office's review that applied the DURC. |
blocked_at | When the office blocked the employer. |
blocked_reason | Why it was blocked. |
blocked_by | The account that blocked the employer. |
deleted_at | When it was archived. |
plan_held_at | When it was suspended by the plan's limits. |
created_by | The account that created it. |
created_at | When it was created. |
updated_at | When it was last changed. |
document_policy | The employer's instruction on its restricted and medical documents: purpose, basis and activation; null if there is none. |
document_policy.purpose_ref | The purpose's reference. |
document_policy.basis_ref | The legal basis's reference. |
document_policy.controller_ack | Whether the controller confirmed the instruction. |
document_policy.outcome_aware_since | Since when the app records the outcomes of fitness judgments. |
document_policy.terms_version | The version of the terms accepted. |
document_policy.uploads_allowed | Whether medical documents can be uploaded. |
document_policy.review_on | The day the instruction is to be reviewed. |
document_policy.updated_by | The account that wrote it. |
document_policy.created_at | When it was created. |
document_policy.updated_at | When it was last changed. |
medical_access | Whom the employer named for its fitness judgments, withdrawn appointments included: the appointment, never a judgment. |
medical_access[].id | The appointment's id. |
medical_access[].user_id | The account appointed. |
medical_access[].can_write | Whether it can also record judgments. |
medical_access[].appointment_ref | The reference of the act of appointment. |
medical_access[].granted_by | The account that made the appointment. |
medical_access[].granted_at | When the appointment was made. |
medical_access[].revoked_by | The account that withdrew the appointment. |
medical_access[].revoked_at | When 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
| Field | Meaning |
|---|---|
id | The site's id. |
code | The site's code, which never changes. |
name | The site's name. |
address | Address. |
city | Municipality. |
province | Province code. |
postal_code | Postcode. |
client_name | The client. |
client_tax_code | The client's tax code or VAT number. |
works_director | The works director. |
safety_coordinator | The safety coordinator. |
starts_on | When the works start. |
planned_end_on | The planned end. |
actual_end_on | The actual end. |
contract_value | The contract value, in euro, written as text. |
status | The site's status. |
geo_lat | The geofence's latitude, as text. |
geo_lng | The geofence's longitude, as text. |
geo_radius_m | The geofence's radius, in metres. |
geofence_enabled | Whether self-service punches ask for the position. |
work_starts_at | Start of the working hours (local time); null: the workspace's. |
work_ends_at | End of the working hours (local time); null: the workspace's. |
break_minutes | Break minutes; null: the workspace's. |
notes | The office's notes. |
deleted_at | When it was archived. |
plan_held_at | When it was suspended by the plan's limits. |
created_by | The account that created it. |
created_at | When it was created. |
updated_at | When it was last changed. |
employers | The employers taking part in the site. |
employers[].id | The participation's id. |
employers[].employer_id | The employer. |
employers[].role | Role: contractor, subcontractor or self-employed worker. |
employers[].subcontract_authorization | The details of the subcontract authorisation. |
employers[].subcontract_authorized_on | The day of the subcontract authorisation. |
employers[].created_by | The account that recorded it. |
employers[].created_at | When it was created. |
employers[].updated_at | When it was last changed. |
assignments | The workers' assignments, past ones included. |
assignments[].id | The assignment's id. |
assignments[].worker_id | The worker. |
assignments[].role | The task on the site. |
assignments[].starts_on | The assignment's start. |
assignments[].ends_on | The assignment's end. |
assignments[].accredited | Whether the worker is accredited on the site. |
assignments[].accredited_at | When the worker was accredited. |
assignments[].accredited_by | The account that accredited the worker. |
assignments[].created_by | The account that recorded it. |
assignments[].created_at | When it was created. |
assignments[].updated_at | When it was last changed. |
gate_links | The site's delegated tablet links; their token is not here. |
gate_links[].id | The link's id. |
gate_links[].label | The tablet's name. |
gate_links[].expires_at | When it expires. |
gate_links[].revoked_at | When it was revoked. |
gate_links[].revoked_by | The account that revoked it. |
gate_links[].created_by | The account that created it. |
gate_links[].last_used_at | When it was last used. |
gate_links[].use_count | Uses. |
gate_links[].created_at | When it was created. |
gate_links[].updated_at | When it was last changed. |
access_grants | The site's inspector access codes, with the log of their use; the code is not here, nor any part of it. |
access_grants[].id | The code's id. |
access_grants[].kind | For whom: safety coordinator, labour inspectorate, local health authority (ASL), client, other. |
access_grants[].label | The holder's name, required for other. |
access_grants[].legacy | Whether it comes from the previous system. |
access_grants[].expires_at | When it expires. |
access_grants[].revoked_at | When it was revoked. |
access_grants[].revoked_by | The account that revoked it. |
access_grants[].created_by | The account that created it. |
access_grants[].use_count | Sessions opened with the code. |
access_grants[].last_used_at | When it was last used. |
access_grants[].created_at | When it was created. |
access_grants[].updated_at | When it was last changed. |
access_grants[].uses | The sessions opened with the code. |
access_grants[].uses[].id | The session's id. |
access_grants[].uses[].started_at | When it was opened. |
access_grants[].uses[].expires_at | When it expires. |
access_grants[].uses[].last_seen_at | When it was last read. |
access_grants[].uses[].ended_at | When it was closed. |
access_grants[].uses[].ended_reason | Why it closed: expired, code expired, code revoked, site archived. |
access_grants[].uses[].ip | The address it was opened from, masked. |
access_grants[].uses[].user_agent | The browser it was opened from. |
access_grants[].uses[].reads | Reads 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
| Field | Meaning |
|---|---|
id | The worker's id. |
employer_id | The employer the worker works for. |
user_id | The account linked to the worker, if any. |
first_name | First name. |
last_name | Surname. |
tax_code | Tax code, sixteen characters. |
birth_date | Date of birth. |
birth_place | Place of birth. |
nationality | Citizenship. |
address | Address. |
phone | Phone. |
email | Email. |
job_title | Job title. |
hired_on | Date of hiring. |
employment_ended_on | The end of the employment. |
employer_changed_at | When the worker last changed employer. |
badge_exempt | Whether the worker is exempt from the badge. |
badge_exempt_reason | Why the worker is exempt from the badge. |
photo_updated_at | When the photo was last changed. |
notes | The office's notes. |
training_basis | The training rule applied: previous combined record, specific training, or refresher. |
training_review_required | Whether the office must review the training before it is valid again. |
deleted_at | When the worker was deactivated. |
disposed_at | When the worker's data were erased. |
plan_held_at | When it was suspended by the plan's limits. |
created_by | The account that created it. |
created_at | When it was created. |
updated_at | When it was last changed. |
photo | The photo's path in the archive (photos/<id>.jpg), or null if there is none. |
facts | The worker's requirements. |
facts[].id | The requirement's id. |
facts[].kind | Kind: general, specific, refresher or foreman training, other training, basic training (previous combined record) or fitness judgment. |
facts[].title | The title of another training course (training_other). |
facts[].completed_on | The day it was obtained. |
facts[].expires_on | The day it expires; the requirement is valid through that day. |
facts[].reference | The reference of the certificate or of the document. |
facts[].note | The office's note. |
facts[].document_id | The document that supports the requirement, if there is one. |
facts[].created_at | When it was created. |
facts[].updated_at | When it was last changed. |
facts[].source | Origin: entered by hand, applied by the review of a document, or imported from the previous system. null on a withheld judgment. |
facts[].document_revision_id | The document revision cited as evidence. |
facts[].document_review_id | The office's review that applied the requirement. |
facts[].claim_id | The review's claim that wrote the requirement. |
facts[].decision_id | Always null in this archive: a judgment that selects an outcome is withheld, and the outcomes leave through the medical archive. |
facts[].version | The requirement's version, which grows with every change. |
facts[].provider | The training body that delivered the course (training only). |
facts[].hours | The course's length in hours, above zero, at most one decimal (training only), written as text. |
facts[].delivery_mode | How the course was delivered (training only). |
facts[].place | Where the course took place (training only). |
facts[].created_by | The account that recorded the requirement; it stays on a withheld judgment too. |
facts[].withheld | true if the requirement is a fitness judgment that selects an outcome, withheld from this archive. |
training_reviews | The training reviews the office made. |
training_reviews[].id | The review's id. |
training_reviews[].kind | Kind: conversion, review, withdrawal or correction. |
training_reviews[].basis_before | The training rule before. |
training_reviews[].basis_after | The training rule after. |
training_reviews[].general_fact_id | The general training requirement considered. |
training_reviews[].specific_fact_id | The specific training requirement considered. |
training_reviews[].refresher_fact_id | The refresher requirement considered. |
training_reviews[].snapshot | The training requirements at the time of the review: a free-form object, in the shape the app used at the time. |
training_reviews[].applicability_ref | The reference the rule applied rests on. |
training_reviews[].reviewer_id | The account that made the review. |
training_reviews[].reviewed_at | When the review was made. |
identity_checks | The identity checks the office made before the worker's app shows one of the worker's restricted or medical documents. |
identity_checks[].id | The check's id. |
identity_checks[].user_id | The account of the worker checked. |
identity_checks[].scope | For which documents. |
identity_checks[].employer_id | The employer, for medical documents. |
identity_checks[].custodian_id | The account that made the check. |
identity_checks[].verified_at | When the check was made. |
identity_checks[].verification_ref | The document shown or the check's reference; null for a check of medical documents, whose reference leaves through the medical archive. |
identity_checks[].revoked_by | The account that withdrew it. |
identity_checks[].revoked_at | When it was withdrawn. |
badge | The worker's badge, or null if it was never issued. The token that opens it is a credential and is not here. |
badge.id | The badge's id. |
badge.issued_at | When it was issued. |
badge.expires_at | When it expires. |
badge.revoked_at | When it was revoked. |
badge.revoked_reason | Why it was revoked. |
badge.revoked_by | The account that revoked it. |
badge.suspended_at | When it was suspended. |
badge.suspended_reason | Why it was suspended. |
badge.suspended_by | The account that suspended it. |
badge.blocked_at | When it was blocked. |
badge.blocked_reason | Why it was blocked. |
badge.blocked_by | The account that blocked it. |
badge.override_until | The end of the override in force. |
badge.override_reason | Why the override was granted. |
badge.override_by | The account that granted the override. |
badge.state | Verdict: green, red or suspended. |
badge.state_reasons | The verdict's reasons. |
badge.state_reasons[].code | The rule's code (for example missing_durc, training_specific_expired, override_active); overridden:<code> for a reason an override lifted. |
badge.state_reasons[].detail | The text that goes with the reason, where the rule carries one. |
badge.state_reasons[].until | The day the reason refers to: an expiry or the end of an override. |
badge.warnings | The verdict's warnings. |
badge.warnings[].code | The rule's code (for example missing_durc, training_specific_expired, override_active); overridden:<code> for a reason an override lifted. |
badge.warnings[].detail | The text that goes with the reason, where the rule carries one. |
badge.warnings[].until | The day the reason refers to: an expiry or the end of an override. |
badge.state_computed_at | When the verdict was computed. |
badge.created_at | When it was created. |
badge.updated_at | When it was last changed. |
badge_history | The badge's changes of state. |
badge_history[].id | The change's id. |
badge_history[].from_state | The state before. |
badge_history[].to_state | The state after. |
badge_history[].reasons | The reasons for the new state. |
badge_history[].reasons[].code | The rule's code (for example missing_durc, training_specific_expired, override_active); overridden:<code> for a reason an override lifted. |
badge_history[].reasons[].detail | The text that goes with the reason, where the rule carries one. |
badge_history[].reasons[].until | The day the reason refers to: an expiry or the end of an override. |
badge_history[].cause | What moved the verdict. |
badge_history[].actor_user_id | The account that acted, if any. |
badge_history[].created_at | When 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
| Field | Meaning |
|---|---|
id | The vehicle's id. |
kind | Kind: registered vehicle, unregistered, rented, machine, equipment, other. |
name | Name. |
plate | Number plate. |
make | Make. |
model | Model. |
year | Year. |
serial | Serial number. |
site_id | The site it is on. |
inspection_kind | Inspection: with an expiry, not recorded, not applicable. |
inspection_expires_on | The inspection's expiry. |
inspection_revision_id | The document revision that proves it. |
insurance_kind | Insurance: with an expiry, not recorded, not applicable. |
insurance_expires_on | The insurance's expiry. |
insurance_revision_id | The document revision that proves it. |
rental_company | Rental company. |
rental_ref | The rental contract's reference. |
rental_starts_on | The rental's start. |
rental_ends_on | The rental's end. |
rental_contract_document_id | The rental contract's document. |
notes | The office's notes. |
archived_at | When it was archived. |
plan_held_at | When it was suspended by the plan's limits. |
created_by | The account that created it. |
created_at | When it was created. |
updated_at | When 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
| Field | Meaning |
|---|---|
id | The document's id. |
site_id | The site it belongs to, if it is a site's. |
worker_id | The worker it belongs to, if it is a worker's. |
employer_id | The employer it belongs to, if it is an employer's. |
vehicle_id | The vehicle it belongs to, if it is a vehicle's. |
kind | The document's kind. |
custom_kind | The kind the office wrote for an other document. |
sensitivity | Sensitivity: ordinary or restricted. |
current_revision_id | The current revision. |
current_review_id | The office's review in force. |
status | The document's status. |
archived_at | When it was archived. |
created_by | The account that created it. |
created_at | When it was created. |
updated_at | When it was last changed. |
revisions | The document's revisions, in order. |
revisions[].id | The revision's id. |
revisions[].ordinal | The revision's number within the document. |
revisions[].original_filename | The name of the uploaded file. |
revisions[].content_type | The file's type (for example application/pdf). |
revisions[].byte_length | The file's size, in bytes. |
revisions[].sha256 | The file's SHA-256 digest, in hexadecimal. |
revisions[].availability | Whether the original is available. |
revisions[].use_state | Use: candidate, reviewed, superseded, withdrawn. |
revisions[].ready_at | When the original was accepted. |
revisions[].retired_at | When the revision went out of use. |
revisions[].document_date | The document's date. |
revisions[].validity_kind | Validity: with an expiry, without one, not recorded. |
revisions[].expires_on | The document's expiry. |
revisions[].note | The office's note. |
revisions[].retention_policy_id | The retention policy applied. |
revisions[].retention_trigger | The policy's event the term runs from, as applied. |
revisions[].retention_trigger_on | The day of that event. |
revisions[].retention_deadline | The day until which it is to be kept. |
revisions[].created_at | When it was created. |
revisions[].updated_at | When it was last changed. |
revisions[].file | The original's path in the archive (documents/<document>/<revision>.<extension>), or null when the original is not available. |
revisions[].retention_decisions | The office's retention decisions on the revision. |
revisions[].retention_decisions[].id | The decision's id. |
revisions[].retention_decisions[].decision | Apply the policy, keep, delete. |
revisions[].retention_decisions[].policy_before | The policy before. |
revisions[].retention_decisions[].policy_after | The policy after. |
revisions[].retention_decisions[].deadline_before | The term before. |
revisions[].retention_decisions[].deadline_after | The term after. |
revisions[].retention_decisions[].reason | The reason. |
revisions[].retention_decisions[].decided_by | The account that decided. |
revisions[].retention_decisions[].decided_at | When it was decided. |
revisions[].retention_conflicts | The retention conflicts found on the revision, resolved ones included. |
revisions[].retention_conflicts[].id | The conflict's id. |
revisions[].retention_conflicts[].reason | Why: the revision proves a requirement, the subject became active again, the event is not plausible. |
revisions[].retention_conflicts[].created_at | When it was found. |
revisions[].retention_conflicts[].resolved_at | When it was resolved. |
revisions[].retention_conflicts[].decision_id | The decision that resolved it. |
holds | The holds on deletion placed on the document, released ones included. |
holds[].id | The hold's id. |
holds[].authority_ref | The reference of the authority or of the act. |
holds[].placed_by | The account that placed it. |
holds[].placed_at | When it was placed. |
holds[].review_on | The day it is to be reviewed. |
holds[].released_by | The account that released it. |
holds[].released_at | When it was released. |
reviews | The office's reviews of the document, with what each one asserted. |
reviews[].id | The review's id. |
reviews[].revision_id | The document revision reviewed. |
reviews[].previous_review_id | The office's previous review. |
reviews[].reviewer_id | The account that made it. |
reviews[].reviewed_at | When the review was made. |
reviews[].scope | What it did: filing, data, classification, withdrawal, restoration. |
reviews[].snapshot | The document's data as the review saw them: a free-form object, in the shape the app used at the time. |
reviews[].share_with_worker | Whether the document is shown to the worker in the worker's app. |
reviews[].claims | What the review asserted of the subject (a requirement, the DURC, a vehicle's expiry). |
reviews[].claims[].id | The claim's id. |
reviews[].claims[].worker_id | The worker, if the subject is a worker. |
reviews[].claims[].employer_id | The employer, if the subject is an employer. |
reviews[].claims[].vehicle_id | The vehicle, if the subject is a vehicle. |
reviews[].claims[].claim_kind | What it asserts. |
reviews[].claims[].values | The 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
| Field | Meaning |
|---|---|
id | The punch's id. |
worker_id | The worker. |
site_id | The site. |
kind | In or out. |
occurred_at | When the punch happened. |
device_at | The instant by the device's clock, where it differs. |
clock_offset_s | The device clock's offset, in seconds. |
offline | Whether it was recorded offline and sent later. |
channel | Channel: gate, worker's app, delegated tablet, manual entry, WhatsApp, Telegram, import. |
identified_by | How the worker was identified. |
gate_link_id | The delegated tablet's link, if one was used. |
actor_user_id | The account that recorded the punch, if any. |
badge_state | The badge's verdict at that instant. |
badge_reasons | The reasons for that verdict. |
badge_reasons[].code | The rule's code (for example missing_durc, training_specific_expired, override_active); overridden:<code> for a reason an override lifted. |
badge_reasons[].detail | The text that goes with the reason, where the rule carries one. |
badge_reasons[].until | The day the reason refers to: an expiry or the end of an override. |
accuracy_m | The position's accuracy, in metres. |
distance_m | The distance from the site, in metres. |
geofence_enforced | Whether the geofence was applied. |
justification | The justification of a manual entry or of one by tax code. |
justification_doc | The reference of the supporting document. |
note | Note. |
presumed | Whether it is an entry or an exit the app presumed. |
flags | Flags on the punch (for example clock_skew, late_sync). |
voided_at | When it was voided. |
voided_reason | Why it was voided. |
voided_by | The account that voided it. |
ip | The address the punch came from. |
user_agent | The browser or app it came from. |
created_at | When it was recorded. |
updated_at | When 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
| Field | Meaning |
|---|---|
id | The interval's id. |
worker_id | The worker. |
site_id | The site. |
starts_at | The interval's start. |
ends_at | The interval's end; null while it is still open. |
in_event_id | The entry punch. |
out_event_id | The exit punch. |
presumed_in | Whether the entry is presumed. |
presumed_out | Whether the exit is presumed. |
presumed_cause | Why an end is presumed: a change of site, the end of the day, a missing entry. |
minutes | The length in minutes; null while it is still open. |
flags | Flags 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
| Field | Meaning |
|---|---|
id | The send's id. |
period_month | The month, as its first day. |
site_id | The site, if the send covers one alone. |
recipient_email | The consultant's email. |
worker_count | Workers on the sheet. |
hours_total | Total presence hours, as text. |
presumed_count | Presumed ends on the sheet. |
anomaly_count | Anomalies on the sheet. |
status | The send's status. |
error | The error code of a failed send. |
is_resend | Whether it is a new send of the same month. |
sent_by | The account that sent it. |
sent_at | When it was sent. |
created_at | When it was requested. |
updated_at | When it was last changed. |
files | The 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
| Field | Meaning |
|---|---|
id | The import's id. |
created_by | The account that uploaded the file. |
file_name | The name of the uploaded file. |
file_size | The file's size, in bytes. |
mode | Dry run or applied. |
options | The options chosen. |
options.update_existing | Whether existing rows were to be updated. |
status | Status. |
started_at | When it started. |
finished_at | When it finished. |
summary | The 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.notices | The notices from reading the file (for example a sheet skipped, and why). |
summary.* | The sheet's row counts by outcome. |
summary.*.created | Rows that created. |
summary.*.updated | Rows that updated. |
summary.*.skipped | Rows skipped. |
summary.*.failed | Rows in error. |
summary.*.refused | Rows refused by the plan's limits (absent from the summaries written before 19 October 2026). |
error | The error code of a failed import. |
created_at | When the file was uploaded. |
updated_at | When it was last changed. |
rows | The outcome of every row read. |
rows[].id | The report row's id. |
rows[].sheet | The sheet. |
rows[].row_number | The row's number in the sheet. |
rows[].key | The row's key (for example a tax code). |
rows[].outcome | Outcome. |
rows[].code | The outcome's code. |
rows[].message | The message, in Italian. |
rows[].entity_type | The kind of row written. |
rows[].entity_id | The row written. |
rows[].created_at | The 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
| Field | Meaning |
|---|---|
id | The acknowledgement's id. |
worker_id | The worker linked to the account that gave it. |
version | The notice's version. |
granted | Whether it was given (true) or withdrawn (false). |
created_at | The 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
| Field | Meaning |
|---|---|
id | The row's id. |
action | The action (for example worker.created, badge.issued). |
entity_type | The kind of thing acted on. |
entity_id | The thing acted on. |
actor_user_id | The account that acted, if any. |
actor_kind | Who acted: an account, the system, an external service, the platform. |
before | The state before: a free-form object, shaped by the action; null if there is none. |
after | The state after: a free-form object, shaped by the action; null if there is none. |
created_at | The 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_dataandformazione_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'idcolumn, 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
IMPRESEsheet 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_pivaanddatore_cfcolumns 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 theIMPRESEsheet 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
| Field | Meaning |
|---|---|
format | The archive's format. |
format.name | The format's name. |
format.version | The format's version, under the same rule as the workspace archive's. |
export_id | The export's id. |
workspace | The workspace exported. |
workspace.id | The workspace's id. |
workspace.name | Name. |
workspace.slug | The short name in the app's address. |
workspace.legal_name | Legal name. |
requested_by | The account that requested the archive. |
employers | The employers whose judgments the requester's named access covered. |
employers[].id | The employer's id. |
employers[].legal_name | Legal name. |
generated_at | When the archive was written. |
day | The workspace's day on which the archive was written. |
timezone | The workspace's time zone. |
app_version | The version of the app that wrote the archive. |
read_mode | How the records were read: one after another, a page at a time. |
read_mode_note | What the sequential reading implies. |
read_mode_note.it | The text in Italian. |
read_mode_note.en | The text in English. |
files | For each JSON file the rows written, for each folder the files. |
files.workers.json | Workers in workers.json. |
files.schemas/ | Schemas in the schemas/ folder. |
files.documents/ | Originals in the documents/ folder. |
missing | The files the archive should have held and did not find. |
missing.originals | Revisions whose original was missing. |
missing.originals_reason | Why 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
| Field | Meaning |
|---|---|
worker | The worker. |
worker.id | The worker's id. |
worker.employer_id | The employer the worker works for. |
worker.first_name | First name. |
worker.last_name | Surname. |
worker.tax_code | Tax code, sixteen characters. |
worker.deleted_at | When the worker was deactivated. |
worker.disposed_at | When the worker's data were erased. |
facts | The worker's fitness judgments. |
facts[].id | The requirement's id. |
facts[].kind | Kind: fitness judgment. |
facts[].title | The title of another training course (training_other). |
facts[].completed_on | The day it was obtained. |
facts[].expires_on | The day it expires; the requirement is valid through that day. |
facts[].reference | The reference of the certificate or of the document. |
facts[].note | The office's note. |
facts[].document_id | The document that supports the requirement, if there is one. |
facts[].created_at | When it was created. |
facts[].updated_at | When it was last changed. |
facts[].source | Origin: entered by hand, applied by the review of a document, or imported from the previous system. null on a withheld judgment. |
facts[].document_revision_id | The document revision cited as evidence. |
facts[].document_review_id | The office's review that applied the requirement. |
facts[].claim_id | The review's claim that wrote the requirement. |
facts[].decision_id | The outcome the judgment selects. |
facts[].version | The requirement's version, which grows with every change. |
facts[].provider | The training body that delivered the course (training only). |
facts[].hours | The course's length in hours, above zero, at most one decimal (training only), written as text. |
facts[].delivery_mode | How the course was delivered (training only). |
facts[].place | Where the course took place (training only). |
facts[].created_by | The account that recorded the requirement; it stays on a withheld judgment too. |
facts[].withheld | Always false: in this archive no judgment is withheld. |
decisions | The medical outcomes recorded. |
decisions[].id | The outcome's id. |
decisions[].employer_id | The employer the judgment is answerable to. |
decisions[].previous_decision_id | The outcome this one replaces. |
decisions[].outcome | Outcome: fit, fit with restrictions, temporarily unfit, permanently unfit, not classified (previous system). |
decisions[].assessed_on | The day of the examination. |
decisions[].valid_until | The judgment's validity. |
decisions[].next_review_on | The next examination. |
decisions[].eligibility_until | Until when the worker is admitted. |
decisions[].boundary_basis | What the admission limit rests on. |
decisions[].boundary_ref | The limit's reference. |
decisions[].context_version | The version of the context it was recorded in. |
decisions[].applicability | Whether the outcome is confirmed or to be reviewed. |
decisions[].review_ref | The reference of the review of the restrictions. |
decisions[].retention_policy_id | The retention policy applied. |
decisions[].document_id | The medical document that proves it. |
decisions[].revision_id | The document's revision. |
decisions[].review_id | The office's review. |
decisions[].correction_of | The outcome this one corrects. |
decisions[].correction_ref | The correction's reference. |
decisions[].actor_id | The account that recorded it. |
decisions[].created_at | When it was recorded. |
decisions[].policy_review_overdue | Whether the retention policy was due for review. |
documents | The medical documents not deleted. |
documents[].id | The document's id. |
documents[].kind | The document's kind. |
documents[].custom_kind | The kind the office wrote for an other document. |
documents[].sensitivity | Sensitivity: always medical. |
documents[].medical_employer_id | The employer the document is answerable to. |
documents[].current_revision_id | The current revision. |
documents[].current_review_id | The office's review in force. |
documents[].status | The document's status. |
documents[].archived_at | When it was archived. |
documents[].created_by | The account that created it. |
documents[].created_at | When it was created. |
documents[].updated_at | When it was last changed. |
documents[].revisions | The document's revisions, in order. |
documents[].revisions[].id | The revision's id. |
documents[].revisions[].ordinal | The revision's number within the document. |
documents[].revisions[].original_filename | The name of the uploaded file. |
documents[].revisions[].content_type | The file's type (for example application/pdf). |
documents[].revisions[].byte_length | The file's size, in bytes. |
documents[].revisions[].sha256 | The file's SHA-256 digest, in hexadecimal. |
documents[].revisions[].availability | Whether the original is available. |
documents[].revisions[].use_state | Use: candidate, reviewed, superseded, withdrawn. |
documents[].revisions[].ready_at | When the original was accepted. |
documents[].revisions[].retired_at | When the revision went out of use. |
documents[].revisions[].document_date | The document's date. |
documents[].revisions[].validity_kind | Validity: with an expiry, without one, not recorded. |
documents[].revisions[].expires_on | The document's expiry. |
documents[].revisions[].note | The office's note. |
documents[].revisions[].retention_policy_id | The retention policy applied. |
documents[].revisions[].retention_trigger | The policy's event the term runs from, as applied. |
documents[].revisions[].retention_trigger_on | The day of that event. |
documents[].revisions[].retention_deadline | The day until which it is to be kept. |
documents[].revisions[].created_at | When it was created. |
documents[].revisions[].updated_at | When it was last changed. |
documents[].revisions[].file | The original's path in the archive (documents/<document>/<revision>.<extension>), or null when the original is not available. |
documents[].revisions[].retention_decisions | The office's retention decisions on the revision. |
documents[].revisions[].retention_decisions[].id | The decision's id. |
documents[].revisions[].retention_decisions[].decision | Apply the policy, keep, delete. |
documents[].revisions[].retention_decisions[].policy_before | The policy before. |
documents[].revisions[].retention_decisions[].policy_after | The policy after. |
documents[].revisions[].retention_decisions[].deadline_before | The term before. |
documents[].revisions[].retention_decisions[].deadline_after | The term after. |
documents[].revisions[].retention_decisions[].reason | The reason. |
documents[].revisions[].retention_decisions[].decided_by | The account that decided. |
documents[].revisions[].retention_decisions[].decided_at | When it was decided. |
documents[].revisions[].retention_conflicts | The retention conflicts found on the revision, resolved ones included. |
documents[].revisions[].retention_conflicts[].id | The conflict's id. |
documents[].revisions[].retention_conflicts[].reason | Why: the revision proves a requirement, the subject became active again, the event is not plausible. |
documents[].revisions[].retention_conflicts[].created_at | When it was found. |
documents[].revisions[].retention_conflicts[].resolved_at | When it was resolved. |
documents[].revisions[].retention_conflicts[].decision_id | The decision that resolved it. |
documents[].holds | The holds on deletion placed on the document, released ones included. |
documents[].holds[].id | The hold's id. |
documents[].holds[].authority_ref | The reference of the authority or of the act. |
documents[].holds[].placed_by | The account that placed it. |
documents[].holds[].placed_at | When it was placed. |
documents[].holds[].review_on | The day it is to be reviewed. |
documents[].holds[].released_by | The account that released it. |
documents[].holds[].released_at | When it was released. |
documents[].reviews | The office's reviews of the document, with what each one asserted. |
documents[].reviews[].id | The review's id. |
documents[].reviews[].revision_id | The document revision reviewed. |
documents[].reviews[].previous_review_id | The office's previous review. |
documents[].reviews[].reviewer_id | The account that made it. |
documents[].reviews[].reviewed_at | When the review was made. |
documents[].reviews[].scope | What it did: filing, data, classification, withdrawal, restoration. |
documents[].reviews[].snapshot | The 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_worker | Whether the document is shown to the worker in the worker's app. |
documents[].reviews[].claims | What the review asserted of the subject, the fitness judgment included. |
documents[].reviews[].claims[].id | The claim's id. |
documents[].reviews[].claims[].worker_id | The worker, if the subject is a worker. |
documents[].reviews[].claims[].employer_id | The employer, if the subject is an employer. |
documents[].reviews[].claims[].vehicle_id | The vehicle, if the subject is a vehicle. |
documents[].reviews[].claims[].claim_kind | What it asserts. |
documents[].reviews[].claims[].values | The values asserted (dates, references): a free-form object, in the shape of the claim's kind. |
identity_checks | The 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[].id | The check's id. |
identity_checks[].user_id | The account of the worker checked. |
identity_checks[].scope | For which documents: always the medical ones. |
identity_checks[].employer_id | The employer whose medical documents the check opens. |
identity_checks[].custodian_id | The account that made the check. |
identity_checks[].verified_at | When the check was made. |
identity_checks[].verification_ref | The document shown or the check's reference. |
identity_checks[].revoked_by | The account that withdrew it. |
identity_checks[].revoked_at | When 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_modefield.
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.
| Table | In the archive | Columns 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
manifest.schema.jsonworkspace.schema.jsonmembers.schema.jsonemployers.schema.jsonsites.schema.jsonworkers.schema.jsonvehicles.schema.jsondocuments.schema.jsonattendance_events.schema.jsonpresence.schema.jsontimesheet_sends.schema.jsonimports.schema.jsonconsents.schema.jsonaudit.schema.json
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: