Resources 00 Resources
Resources
Resources are the reference data your organization keeps in Provision: suppliers, customers, products, product categories, equipment, chemicals, employees, areas, lots, recipes and documents. Each kind is one dataset in GET /v1/datasets, with a key that begins resources_, and each exports and pages through the same endpoint and cursor rules as every other dataset.
Unlike form datasets, their core columns are the same for every customer, so this page lists them. On top of those, every resource dataset except resources_documents adds a column for each custom attribute your organization defines for that kind of resource; see Custom attribute columns. Read the exact list from GET /v1/datasets, and treat this page as the meaning of each core column.
What every resource dataset shares
idis the resource's uuid, and the key to upsert on. The resource select columns in form datasets (<field>_id) join to it.last_modifiedis when the resource itself last changed, in UTC. It is the cursor column, as everywhere. No resource dataset has*_localtimestamps.- The dataset has no
displayName, and only some of its fixed columns have alabel— though every custom attribute column has one, the attribute's name. Fall back tokey. - Deleting a resource removes its row, silently; see Deleted records stop being exported. Archiving does not remove a document; see Documents.
- A name can be blank while the id beside it is filled.
site_name,site_path,product_nameand the other columns naming a related record are read from that record as it stands now, so deleting it empties the name and leaves the id in place — deliberately, so the row itself is not dropped. A roll-up keyed onsite_pathsilently loses those rows, and the id no longer joins to anything in the related dataset. - The CSV dialect is the same as for every dataset; see Reading the values.
- Two columns join a first and last name with a space (
contact_nameonresources_customers,nameonresources_employees). When both parts are missing they export as a single space, notNULLor an empty string, so trim before testing for emptiness.
Custom attribute columns
Every resource dataset except resources_documents adds one column per custom attribute defined for that kind of resource, after the core columns. The column is named from the attribute's key, following the naming rules, and a name that clashes with a core column gets a numeric suffix. Its label is the attribute's name.
| Custom attribute type | Column type | Value |
|---|---|---|
| Number | number | The number |
| Date | date | The date |
| DateTime | dateTime | The date and time |
| Document, SingleDocument | text | The names of the linked documents, joined by a comma and a space |
| Select | text | The option's stored key, not the label users see in Provision, and not affected by renaming the option |
| MultiSelect | text | A JSON array of the stored option keys, sorted: ["gmp","haccp"]. Not a comma-joined list — parse it as JSON |
| Any other type | text | The stored value |
A value that does not fit its type exports as empty. A Number attribute holding N/A or 12 kg, or a Date that is not written year first, exports as nothing, not as its original text. Only that cell is emptied; the rest of the row and the export are unaffected. The value is usually still intact in Provision and worth correcting there.
A custom attribute can take a name that means something elsewhere. What is reserved is the dataset's own core columns plus the four names the export owns — id, last_modified, row_index and row_seq, which is internal numbering the cursor is built on and never appears in a CSV — and those four are reserved whether or not the dataset publishes a column of that name. Everything else is fair game, so an attribute whose key is "Site Name" or "Record ID" on a resource without those columns becomes a site_name or record_id column holding the attribute's value, while one keyed "Row Index" gets a numeric suffix instead. See Site, status and record columns.
List columns
Where a resource links to many of something, the export flattens the links into one text column of names joined by a comma and a space:
| Dataset | List columns |
|---|---|
resources_suppliers | products, parent_names |
resources_customers | products |
resources_products | customers, sites, suppliers, allergens |
resources_chemicals | sites |
resources_employees | teams, sites |
resources_documents | sites, teams |
| Every dataset with custom attributes | each Document and SingleDocument custom attribute |
They hold names, not ids, so use them for display, not joins. A name containing a comma looks like two names, so splitting on , is unreliable. To model the relationships themselves, use the datasets on both sides.
A sites list is not a site column. It lists site names, and has nothing to do with the site_id / site_name / site_path columns that identify the one site a form row belongs to. Do not join it to a site dimension.
List columns go stale in an incremental sync. Renaming a product changes the products value of every supplier and customer listing it, without moving their last_modified, so the cursor never offers those rows again. Renaming a supplier does the same to the parent_names of every supplier beneath it. Re-read resource datasets in full on a schedule if these columns matter; see Upsert, do not append.
Site, status and record columns
Some column names mean one thing on form datasets and something else, or nothing reserved, here.
Site columns. Only three resource datasets have a site: resources_lots, resources_areas and resources_equipment. Each has site_id, site_name and site_path, read the same way as a form dataset's. On every other resource dataset those names are not reserved, so a column called site_name or site_path there is a custom attribute, and joining it to your sites produces nonsense.
status. On resources_suppliers, resources_lots and resources_recipes it is that resource's own status, unrelated to a submission's status.
record_id. No resource dataset has one, so a record_id column on a resource dataset is a custom attribute, and joining it to submission record codes produces nonsense.
row_index. Reserved here too. A custom attribute that slugifies to row_index does not take the name: it gets a numeric suffix (row_index_2) instead, so no resource dataset publishes a row_index column and afterRowIndex is rejected on all of them. Only grid datasets have one; see Terms these pages use.
Check the columns GET /v1/datasets reports for the handful you rely on, rather than assuming either way.
Suppliers
Dataset resources_suppliers. Adds custom attribute columns.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the supplier. |
name | text | Name of the supplier. |
description | text | Free-text description of the supplier. |
website_url | text | Supplier's website address. |
status | text | Status recorded for the supplier. |
market | text | Market recorded for the supplier. |
type | text | Type recorded for the supplier. |
street1 | text | First street line of the supplier's address. |
street2 | text | Second street line of the supplier's address. |
city | text | City of the supplier's address. |
region | text | State or region of the supplier's address. |
zip_code | text | Postal code of the supplier's address. |
country | text | Country of the supplier's address. |
products | text | Products linked to this supplier, as names joined by a comma and a space. |
parent_names | text | Parent suppliers this supplier sits under, as names joined by a comma and a space. |
last_modified | dateTime | When this supplier was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
Customers
Dataset resources_customers. Adds custom attribute columns.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the customer. |
name | text | Name of the customer. |
description | text | Free-text description of the customer. |
website_url | text | Customer's website address. |
phone_number | text | Customer's phone number. |
street1 | text | First street line of the customer's address. |
street2 | text | Second street line of the customer's address. |
city | text | City of the customer's address. |
region | text | State or region of the customer's address. |
zip_code | text | Postal code of the customer's address. |
country | text | Country of the customer's address. |
contact_name | text | Full name of the customer's contact, first and last name together. |
contact_email | text | Email address of the customer's contact. |
contact_phone | text | Phone number of the customer's contact. |
products | text | Products linked to this customer, as names joined by a comma and a space. |
last_modified | dateTime | When this customer was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
Products
Dataset resources_products. Adds custom attribute columns.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the product. |
name | text | Name of the product. |
description | text | Free-text description of the product. |
product_code | text | Code identifying the product. |
product_type | text | Product type: RawMaterial, WorkInProgress, FinishedGood or Packaging; anything else appears as its stored number. |
product_category_name | text | Name of the category the product is in. |
allergen_status | text | Allergen status recorded for the product. |
allergens | text | Allergens recorded for the product, joined by a comma and a space; empty when none are recorded. |
default_shelf_life_quantity | number | Default shelf life of the product, as a number counted in the unit below. |
default_shelf_life_unit | text | Unit the default shelf life is counted in. |
customers | text | Customers linked to this product, as names joined by a comma and a space. |
sites | text | Sites linked to this product, as names joined by a comma and a space. |
suppliers | text | Suppliers linked to this product, as names joined by a comma and a space. |
last_modified | dateTime | When this product was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
Product categories
Dataset resources_product_categories. Adds custom attribute columns.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the product category. |
name | text | Name of the product category. |
description | text | Free-text description of the product category. |
parent_name | text | Name of the category this category sits under. |
last_modified | dateTime | When this product category was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
Equipment
Dataset resources_equipment. Adds custom attribute columns.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the equipment. |
name | text | Name of the equipment. |
make | text | Make of the equipment. |
model | text | Model of the equipment. |
serial_number | text | Serial number of the equipment. |
site_name | text | Name of the site the equipment belongs to. |
site_id | uuid | Unique identifier of the site the equipment belongs to. |
site_path | text | Full position of that site in the site hierarchy, from the top level down. |
area_name | text | Name of the area the equipment is in. |
last_modified | dateTime | When this equipment was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
Chemicals
Dataset resources_chemicals. Adds custom attribute columns.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the chemical. |
name | text | Name of the chemical. |
type | text | Type recorded for the chemical. |
grade | text | Grade recorded for the chemical. |
production_method | text | Production method recorded for the chemical. |
sites | text | Sites linked to this chemical, as names joined by a comma and a space. |
last_modified | dateTime | When this chemical was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
Employees
Dataset resources_employees. Adds custom attribute columns.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the employee. |
name | text | Full name of the employee, first and last name together. |
job_title | text | Job title of the employee. |
access_level | text | The employee's access level in the organization. |
teams | text | Teams the employee belongs to, as names joined by a comma and a space. |
sites | text | Sites the employee is assigned to, as names joined by a comma and a space. |
last_modified | dateTime | When this employee was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
Areas
Dataset resources_areas. Adds custom attribute columns.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the area. |
name | text | Name of the area. |
description | text | Free-text description of the area. |
type | text | Type recorded for the area. |
size | number | Size of the area, counted in the unit below. |
size_unit | text | Unit the area size is counted in. |
area_path | text | Full position of the area in the area hierarchy, from the top level down. |
parent_name | text | Name of the area this area sits under. |
site_id | uuid | Unique identifier of the site the area belongs to. |
site_name | text | Name of the site the area belongs to. |
site_path | text | Full position of that site in the site hierarchy, from the top level down. |
last_modified | dateTime | When this area was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
area_path is the area hierarchy, not the site one. It is built by the same rule as site_path — the area's own id and every area above it, outermost first, joined by dots — so whatever parses one parses the other. This dataset carries both: read area_path for the area tree and site_path for the site tree, and do not join one to the other.
Lots
Dataset resources_lots. Adds custom attribute columns.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the lot. |
code | text | Code identifying the lot. |
description | text | Free-text description of the lot. |
status | text | Status recorded for the lot. |
quantity | number | Quantity held in the lot, counted in the unit below. |
unit | text | Unit the lot quantity is counted in. |
expiry_date | date | Date the lot expires. |
date_received | date | Date the lot was received. |
date_produced | date | Date the lot was produced. |
product_id | uuid | Unique identifier of the product this lot is of. |
product_name | text | Name of the product this lot is of. |
site_id | uuid | Unique identifier of the site the lot belongs to. |
site_name | text | Name of the site the lot belongs to. |
site_path | text | Full position of that site in the site hierarchy, from the top level down. |
last_modified | dateTime | When this lot was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
expiry_date, date_received and date_produced are date columns: a calendar day, with no time or offset.
Recipes
Dataset resources_recipes. Adds custom attribute columns.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the recipe. |
name | text | Name of the recipe. |
description | text | Free-text description of the recipe. |
status | text | Status recorded for the recipe. |
output_quantity | number | Quantity the recipe produces, counted in the unit below. |
output_unit | text | Unit the produced quantity is counted in. |
has_unpublished_changes | boolean | True when the recipe has changes that have not been published. |
output_product_id | uuid | Unique identifier of the product the recipe produces. |
output_product_name | text | Name of the product the recipe produces. |
last_modified | dateTime | When this recipe was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
Documents
Dataset resources_documents. Adds no custom attribute columns, so its columns are the same for every customer.
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the document. |
name | text | Name of the document. |
type | text | Type recorded for the document. |
about | text | Short note on what the document is about. |
version | text | Version label of the document's currently published version. |
is_archived | boolean | True when the document is archived. |
sites | text | Sites linked to this document, as names joined by a comma and a space. |
teams | text | Teams linked to this document, as names joined by a comma and a space. |
last_modified | dateTime | When this document was last changed. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
Archived documents keep exporting. Archiving a document leaves its row in place with is_archived t, so filter on that column for a list of current documents. It says only whether the document is archived: a draft, one in review and a rejected one all export with is_archived f, like a published document.
resources_documents is the only resource dataset with such a column. Others export their rows whatever state they are in — an archived employee is in resources_employees with nothing marking it — and lose a row only when the resource is deleted.
Resources 01 Deviations
Deviations
A deviation is a value flagged as out of spec on one field of one submission. The deviations dataset has one row per deviation: the field's key and label, the recorded value, a severity, and how, or whether, it was resolved, alongside the submission, step and site it came from.
Its columns are the same for every customer: it takes no custom attributes, and has no resources_ prefix. Deviations come from your forms, so the dataset moves with your submissions, and deleting a submission removes its deviations.
Columns
| Column | Type | Meaning |
|---|---|---|
id | uuid | Unique identifier of the deviation. |
code | text | Code identifying the deviation. |
severity | text | Severity recorded for the deviation. |
status | text | Deviation status: Resolved, InProgress or Open. |
is_resolved | boolean | True when the deviation has been resolved. |
resolution_type | text | How it was resolved: LinkedResolutionRecord or Comment; empty while unresolved. |
field_key | text | Identifier of the form field the deviation was raised on. |
field_label | text | Label of the form field the deviation was raised on. |
display_value | text | The value recorded in that field. |
row_id | text | Identifier of the grid row the deviation was raised on, for fields inside a grid. |
workflow_name | text | Name of the form; kept for existing dashboards and superseded by form_name. |
form_name | text | Name of the form the deviation was raised on. |
submission_id | uuid | Unique identifier of the submission the deviation was raised on. |
record_id | text | Code identifying the record the deviation was raised on. |
submission_status | text | Status of the submission the deviation was raised on. |
is_archived | boolean | True when the record holding the deviation is archived. |
step_name | text | Name of the form step the deviation was raised on. |
step_status | text | Status of that step. |
site_name | text | Name of the site the submission belongs to. |
site_id | uuid | Unique identifier of the site the submission belongs to. |
site_path | text | Full position of that site in the site hierarchy, from the top level down. |
assigned_to | text | Who the deviation is assigned to: the assigned person's name, or the assigned team's name when no person is set. |
assigned_user_name | text | Name of the person the deviation is assigned to. |
assigned_team_name | text | Name of the team the deviation is assigned to. |
supplier_name | text | Name of the supplier the deviation is attributed to. |
comment | text | Comment recorded with the deviation. |
resolution_comment | text | Comment recorded when the deviation was resolved. |
resolved_by | text | Name of the person who resolved the deviation. |
resolved_at | dateTime | When the deviation was resolved, in UTC. |
resolved_at_local | localDateTime | When the deviation was resolved, in the site's time zone. |
signed_by | text | Name of the person who signed the resolution. |
signed_at | dateTime | When the resolution was signed, in UTC. |
signed_at_local | localDateTime | When the resolution was signed, in the site's time zone. |
linked_record_code | text | Code of the record linked as the deviation's resolution. |
linked_record_status | text | Status of the record linked as the deviation's resolution. |
linked_record_source | text | Source of the record linked as the deviation's resolution. |
linked_resolution_comment | text | Comment recorded on the deviation about its linked resolution record. |
created_at | dateTime | When the deviation was created, in UTC. |
created_at_local | localDateTime | When the deviation was created, in the site's time zone. |
last_modified | dateTime | When the deviation was last changed, in UTC. |
last_modified_local | localDateTime | When the deviation was last changed, in the site's time zone. |
_cursor | text | This row’s paging position, added by the export rather than stored. Opaque: send it back as cursor and do not parse it. See the sync loop |
It borrows some columns from the submission, with the meanings the envelope columns give them: record_id, site_id, site_name, site_path and is_archived. Its *_local timestamps are its own, describing the deviation, read the same way as a form dataset's. id is the deviation's own id.
step_name and step_status are the recording step the flagged field sits on. A deviation is only ever raised on a recording step, so a verification step never appears here.
form_name and workflow_name are the same value — the name of the form the submission was made against. Workflow is our older word for a form, and workflow_name is kept only so dashboards built on it keep working; the two are filled from one expression and cannot differ. Build on form_name, and expect workflow_name to be retired in a later version.
comment is a standalone note on the deviation, which can be added, changed or removed at any time — not a record of what was said when the deviation was raised. linked_record_source says where the linked resolution record came from; treat its values as opaque unless we have told you otherwise.
Joining a deviation to its submission
Join on submission_id. It holds the same uuid as the submission dataset's id, and it is the only column that makes that join. record_id beside it is the human-readable record code: good for display, not the submission dataset's key.
row_id cannot reach a grid row. It is set when the deviation was raised on a field inside a grid, but no grid dataset publishes a matching id: a grid row is identified by id plus row_index, and row_index is a position. So row_id tells you that the deviation came from a grid row, not which one. Treat it as an opaque marker.
Status and resolution
statusis the deviation's state:Open,InProgressorResolved. The submission's own status issubmission_status. Every change to it moveslast_modified, so an incremental sync offers the row again: linking a record sets a column on the deviation row, and resolving writes its resolution to a table of its own and restamps the deviation, soOpen→InProgress→Resolvedall reach you. That holds however it was resolved — with a comment, by editing that comment, or by completing the linked record. Other columns are less well behaved; see Keeping deviations current.is_resolvedis the column that says whether it was resolved. Alinked_record_codeis set as soon as a record is linked as the intended resolution, before it resolves anything: such a row isInProgresswithis_resolvedf, possibly indefinitely. See Record and deviation codes.- A resolution can carry a signature, given when the deviation is resolved with a comment. That is where
signed_by,signed_atandsigned_at_localcome from. They are empty for a resolution without a signature, including one completed through a linked record, and an unresolved deviation never has them. codeis the deviation's own code, in its own format and on its own counter; see Record and deviation codes.
Keeping deviations current
A deviation's last_modified only follows the deviation row itself. Resolution is the exception worth knowing about, and it is handled: resolving writes to a table of its own and restamps the deviation, so status, is_resolved, resolution_type, resolution_comment, resolved_by and the resolved_at and signed_* timestamps all reach an incremental consumer. A deviation does not keep showing as open after someone resolved it.
What still escapes the cursor are the columns the deviation only displays: the submission, step and site columns, the linked record's linked_record_status, the names of the employee, team and supplier, and the standalone comment, which is stored beside the deviation rather than on it. Those belong to other records, and changing one of those records moves its own last_modified, not the deviation's. If you report on them, re-read deviations in full on a schedule, as Upsert, do not append describes for resource list columns.
A person's name can also be missing where a person really is set. assigned_user_name, resolved_by and signed_by are read from your employee directory, so someone with no live employee record there — a Provision Admin, or an employee since deleted — leaves them empty on a deviation that was assigned, resolved or signed. assigned_to then falls back to the team's name, so such a row reads as team-assigned although a person is assigned. Read assigned_user_name and assigned_team_name rather than inferring which of the two assigned_to holds, and treat an empty resolved_by on a resolved deviation as an unresolvable person, not as an unresolved deviation.