{
  "schema": "si-demand-engine/1",
  "site_id": "backoffice",
  "origin": "https://backoffice.syntheticindustry.ai",
  "records": [
    {
      "id": "airtable-automation-fails-or-skips-records",
      "type": "outcome",
      "title": "Make one Airtable automation run on the records it should",
      "description": "Synthetic records covering three agreed cases make one Airtable automation run successfully or skip deliberately as documented, and a failed run reaches a named person.",
      "url": "/services/airtable-automation-fails-or-skips-records/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "airtable"
      ],
      "related": [
        "airtable-wide-table-to-linked-tables",
        "auto-failure-alerts-reach-a-named-owner",
        "make-scenario-stops-on-one-bad-record"
      ],
      "provenance": [
        {
          "label": "Airtable documents failure causes, reading automation history, that a rerun uses the original configuration, the 30-second script network timeout, that failure emails go to the person who turned the automation on, and that only collaborators with Creator permission or higher on the base can be added as notification subscribers.",
          "url": "https://support.airtable.com/articles/6756755850-troubleshooting-airtable-automations"
        },
        {
          "label": "Airtable documents that both failed and successful automation attempts count against the workspace's monthly run allowance and that turned-off automations still occupy a slot.",
          "url": "https://support.airtable.com/docs/getting-started-with-airtable-automations"
        }
      ],
      "incoming": [
        "airtable-wide-table-to-linked-tables",
        "auto-failure-alerts-reach-a-named-owner",
        "buyer-operations-manager-inherited-automations",
        "guide-airtable-automation-failed-runs-and-limits",
        "platform-airtable"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "airtable-automation-fails-or-skips-records",
          "source": "/services/airtable-automation-fails-or-skips-records/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "airtable-automation-fails-or-skips-records",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/airtable-automation-fails-or-skips-records/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=airtable-automation-fails-or-skips-records&source=%2Fservices%2Fairtable-automation-fails-or-skips-records%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=airtable-automation-fails-or-skips-records&body=SI-Outcome%3A%20airtable-automation-fails-or-skips-records%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fairtable-automation-fails-or-skips-records%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "airtable-wide-table-to-linked-tables",
      "type": "outcome",
      "title": "Split one wide Airtable table into linked tables without losing a record",
      "description": "On a duplicate of your base, one table becomes a parent and a child table that reconcile as at a recorded snapshot. You then adopt the duplicate, and we list what that changes.",
      "url": "/services/airtable-wide-table-to-linked-tables/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "airtable"
      ],
      "related": [
        "airtable-automation-fails-or-skips-records"
      ],
      "provenance": [
        {
          "label": "Airtable documents that pasting or converting into a linked record field matches by the linked table's primary field, exactly, creates new records for unmatched values, splits comma-separated values and cannot create records when the primary field is a formula.",
          "url": "https://support.airtable.com/docs/converting-existing-fields-to-linked-records"
        },
        {
          "label": "Airtable documents linked records, lookup fields, and that deleting a linked record field leaves the opposite field as text and can be restored from the base trash.",
          "url": "https://support.airtable.com/docs/linking-records-in-airtable"
        },
        {
          "label": "Airtable documents duplicating a base: the duplicate is found on the home screen with the original name plus (Copy) unless renamed, interfaces are duplicated with it, and a synced table keeps its records but loses its connection to the sync source, which has to be reconfigured. The page does not say whether automations, forms or share links carry over.",
          "url": "https://support.airtable.com/docs/duplicating-an-existing-base"
        }
      ],
      "incoming": [
        "airtable-automation-fails-or-skips-records",
        "guide-airtable-split-table-linked-records",
        "platform-airtable"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "airtable-wide-table-to-linked-tables",
          "source": "/services/airtable-wide-table-to-linked-tables/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "airtable-wide-table-to-linked-tables",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/airtable-wide-table-to-linked-tables/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=airtable-wide-table-to-linked-tables&source=%2Fservices%2Fairtable-wide-table-to-linked-tables%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=airtable-wide-table-to-linked-tables&body=SI-Outcome%3A%20airtable-wide-table-to-linked-tables%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fairtable-wide-table-to-linked-tables%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "archive-digitise-and-normalise-one-archive",
      "type": "composed",
      "title": "Turn one scanned and old-format file archive into an organised, searchable collection",
      "description": "You scan the paper. We turn the scans and your old Office files into one organised, searchable collection, with a manifest and a spreadsheet index, for an agreed inventory.",
      "url": "/services/archive-digitise-and-normalise-one-archive/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "file-archives",
        "pdf-documents",
        "microsoft-word",
        "microsoft-excel"
      ],
      "related": [
        "archive-rename-organise-with-manifest",
        "convert-legacy-office-files-batch"
      ],
      "provenance": [
        {
          "label": "RFC 8493 (BagIt) describes a manifest of checksums and relative paths for checking that a set of files is complete and unchanged, the idea the project manifest follows.",
          "url": "https://www.rfc-editor.org/rfc/rfc8493"
        },
        {
          "label": "The Library of Congress format description says level B PDF/A files made from scanned page images do not necessarily support indexing of the text.",
          "url": "https://www.loc.gov/preservation/digital/formats/fdd/fdd000318.shtml"
        }
      ],
      "incoming": [
        "archive-rename-organise-with-manifest",
        "convert-legacy-office-files-batch",
        "pdf-scanned-batch-to-searchable-text",
        "example-archive-rename-manifest",
        "buyer-doc-office-manager-paper-and-pdf-backlog",
        "guide-convert-legacy-office-fidelity",
        "guide-archive-rename-manifest-path-limits",
        "guide-pdf-scan-ocr-search-check"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "archive-digitise-and-normalise-one-archive",
          "source": "/services/archive-digitise-and-normalise-one-archive/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "archive-digitise-and-normalise-one-archive",
          "SI-Mode": "quote",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/archive-digitise-and-normalise-one-archive/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=archive-digitise-and-normalise-one-archive&source=%2Fservices%2Farchive-digitise-and-normalise-one-archive%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=archive-digitise-and-normalise-one-archive&body=SI-Outcome%3A%20archive-digitise-and-normalise-one-archive%0ASI-Mode%3A%20quote%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Farchive-digitise-and-normalise-one-archive%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "archive-rename-organise-with-manifest",
      "type": "outcome",
      "title": "Rename and sort one folder of files into a clear structure, with a manifest",
      "description": "A folder of badly named files, handed over by an agreed secure route, is copied into an agreed naming scheme after you approve a dry run. A manifest has a row for every file and shows none changed.",
      "url": "/services/archive-rename-organise-with-manifest/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "file-archives"
      ],
      "related": [
        "convert-legacy-office-files-batch",
        "pdf-scanned-batch-to-searchable-text",
        "archive-digitise-and-normalise-one-archive"
      ],
      "provenance": [
        {
          "label": "Microsoft documents the characters and reserved names that Windows file names cannot use, that names should not end in a space or period, and that Windows should not be assumed to be case-sensitive.",
          "url": "https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file"
        },
        {
          "label": "Microsoft documents the 260-character MAX_PATH limit and the opt-in route to longer paths.",
          "url": "https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation"
        },
        {
          "label": "RFC 8493 (BagIt) describes a payload manifest of checksums and relative file paths used to check that a set of files is complete and unchanged.",
          "url": "https://www.rfc-editor.org/rfc/rfc8493"
        }
      ],
      "incoming": [
        "archive-digitise-and-normalise-one-archive",
        "convert-legacy-office-files-batch",
        "pdf-scanned-batch-to-searchable-text",
        "example-archive-rename-manifest",
        "buyer-doc-office-manager-paper-and-pdf-backlog",
        "guide-archive-rename-manifest-path-limits"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "archive-rename-organise-with-manifest",
          "source": "/services/archive-rename-organise-with-manifest/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "archive-rename-organise-with-manifest",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/archive-rename-organise-with-manifest/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=archive-rename-organise-with-manifest&source=%2Fservices%2Farchive-rename-organise-with-manifest%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=archive-rename-organise-with-manifest&body=SI-Outcome%3A%20archive-rename-organise-with-manifest%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Farchive-rename-organise-with-manifest%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "auto-failure-alerts-reach-a-named-owner",
      "type": "outcome",
      "title": "Hook up failure alerts for up to five automations so a named person is told",
      "description": "A deliberate synthetic failure in each of up to five automations raises an alert that reaches a shared mailbox or channel read by a named owner and a named backup.",
      "url": "/services/auto-failure-alerts-reach-a-named-owner/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "zapier",
        "make",
        "n8n",
        "airtable"
      ],
      "related": [
        "make-scenario-stops-on-one-bad-record",
        "airtable-automation-fails-or-skips-records"
      ],
      "provenance": [
        {
          "label": "Airtable documents that failed run notifications go to the person who turned the automation on, or to workspace owners if that person has left, and that only collaborators with Creator permission or higher on the base can be added as notification subscribers.",
          "url": "https://support.airtable.com/articles/6756755850-troubleshooting-airtable-automations"
        },
        {
          "label": "Zapier documents that when autoreplay is enabled, error emails and Zapier Manager triggers wait until the final autoreplay attempt fails.",
          "url": "https://help.zapier.com/hc/en-us/articles/19220226086797-What-is-replay"
        },
        {
          "label": "Zapier documents custom error handling as available on Professional, Team and Enterprise plans, that no error notification email is sent when a handler runs, and that publishing a Zap with error handling turns its autoreplay off.",
          "url": "https://help.zapier.com/hc/en-us/articles/22495436062605-Set-up-custom-error-handling"
        },
        {
          "label": "Make documents that an error route does not need an error handler, that its example for being notified is a Slack message module in the route, that Make skips the error if no module outputs an error in the route, and that the Skip handler marks the run as successful.",
          "url": "https://help.make.com/Overview-of-error-handling"
        },
        {
          "label": "n8n documents that an error workflow starting with the Error Trigger node runs when an execution fails and can send an email or Slack alert.",
          "url": "https://docs.n8n.io/flow-logic/error-handling/"
        }
      ],
      "incoming": [
        "airtable-automation-fails-or-skips-records",
        "crm-web-lead-pipeline-repair",
        "lead-routing-new-leads-unassigned-or-wrong-owner",
        "make-scenario-stops-on-one-bad-record",
        "webhook-receiver-verified-signature-duplicate-safe",
        "workflow-api-polling-no-gaps-no-repeats",
        "buyer-operations-manager-inherited-automations",
        "collection-automation-failure-diagnosis-order",
        "guide-airtable-automation-failed-runs-and-limits",
        "guide-automation-failure-alerts-reach-someone",
        "guide-zapier-errored-halted-and-replay",
        "guide-make-error-handlers-and-incomplete-executions",
        "guide-scheduled-report-time-zone-and-period",
        "guide-n8n-webhook-test-and-production-urls",
        "platform-zapier",
        "platform-make",
        "platform-airtable"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "auto-failure-alerts-reach-a-named-owner",
          "source": "/services/auto-failure-alerts-reach-a-named-owner/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "auto-failure-alerts-reach-a-named-owner",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/auto-failure-alerts-reach-a-named-owner/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=auto-failure-alerts-reach-a-named-owner&source=%2Fservices%2Fauto-failure-alerts-reach-a-named-owner%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=auto-failure-alerts-reach-a-named-owner&body=SI-Outcome%3A%20auto-failure-alerts-reach-a-named-owner%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fauto-failure-alerts-reach-a-named-owner%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "convert-legacy-office-files-batch",
      "type": "outcome",
      "title": "Convert a batch of old .doc and .xls files to current formats and check them",
      "description": "Every old Word and Excel file in one folder is converted to a current format in a parallel folder structure, with a log of what converted, what did not and a sampled check against the original.",
      "url": "/services/convert-legacy-office-files-batch/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "microsoft-excel",
        "microsoft-word"
      ],
      "related": [
        "archive-rename-organise-with-manifest",
        "pdf-scanned-batch-to-searchable-text",
        "archive-digitise-and-normalise-one-archive"
      ],
      "provenance": [
        {
          "label": "Microsoft documents that Excel opens Excel 97-2003 files in Compatibility Mode, that File, Info, Convert replaces the original with a current-format copy, and that Compatibility Checker lists features that do not carry over.",
          "url": "https://support.microsoft.com/en-us/office/use-excel-with-earlier-versions-of-excel-2fd9ffcb-6fce-485b-85af-fecfd651a5ac"
        },
        {
          "label": "Microsoft documents that workbooks with macros should be saved as macro-enabled .xlsm, and advises checking the converted file for changes.",
          "url": "https://support.microsoft.com/en-us/excel/saving-xls-to-xlsx-xlsm"
        },
        {
          "label": "Microsoft documents that Word documents with macros can be converted to the macro-enabled .docm format, that the Developer tab lists macros, and advises keeping a backup and verifying the content or layout after conversion.",
          "url": "https://support.microsoft.com/en-us/word/saving-doc-to-docx-or-dorm"
        }
      ],
      "incoming": [
        "archive-digitise-and-normalise-one-archive",
        "archive-rename-organise-with-manifest",
        "buyer-doc-office-manager-paper-and-pdf-backlog",
        "guide-convert-legacy-office-fidelity"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "convert-legacy-office-files-batch",
          "source": "/services/convert-legacy-office-files-batch/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "convert-legacy-office-files-batch",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/convert-legacy-office-files-batch/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=convert-legacy-office-files-batch&source=%2Fservices%2Fconvert-legacy-office-files-batch%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=convert-legacy-office-files-batch&body=SI-Outcome%3A%20convert-legacy-office-files-batch%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fconvert-legacy-office-files-batch%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "crm-duplicate-contacts-from-web-forms",
      "type": "outcome",
      "title": "Stop web-form leads creating duplicate HubSpot contacts and companies",
      "description": "Synthetic submissions through one HubSpot form or API integration give the agreed counts: one contact for a repeat email, one company for two people at one domain, and no merging of different people.",
      "url": "/services/crm-duplicate-contacts-from-web-forms/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "hubspot",
        "web-forms"
      ],
      "related": [
        "lead-routing-new-leads-unassigned-or-wrong-owner",
        "hubspot-salesforce-synced-field-reverts"
      ],
      "provenance": [
        {
          "label": "HubSpot documents that contacts are de-duplicated by email, companies by domain, and that companies created through the API are not de-duplicated by domain.",
          "url": "https://knowledge.hubspot.com/records/deduplication-of-records"
        },
        {
          "label": "HubSpot documents that with the create-contacts-from-unknown-emails setting off, matching falls back to browser cookies, which may overwrite contacts.",
          "url": "https://knowledge.hubspot.com/forms/create-and-edit-forms"
        },
        {
          "label": "HubSpot documents that a form with no email field identifies submissions by browser cookie, which cannot be changed, and that on a shared computer the cookie can update or overwrite the cookied contact's properties, including email.",
          "url": "https://knowledge.hubspot.com/account/how-does-hubspot-track-visitors"
        },
        {
          "label": "HubSpot documents that merged contacts cannot be unmerged.",
          "url": "https://knowledge.hubspot.com/records/merge-records"
        },
        {
          "label": "HubSpot documents that for non-HubSpot forms all submissions associated with the same cookie are added to the same contact record, that the form must be a static HTML form with the tracking code loading and not in an iframe, and that a Forms API or form integration is the alternative.",
          "url": "https://knowledge.hubspot.com/forms/use-non-hubspot-forms"
        }
      ],
      "incoming": [
        "crm-web-lead-pipeline-repair",
        "hubspot-salesforce-synced-field-reverts",
        "lead-routing-new-leads-unassigned-or-wrong-owner",
        "buyer-marketing-manager-web-lead-flow",
        "collection-web-lead-to-owner-checks",
        "guide-hubspot-form-duplicates-contacts-companies",
        "platform-hubspot",
        "guide-integrate-pipedrive-person-lead-handoff-and-limits"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "crm-duplicate-contacts-from-web-forms",
          "source": "/services/crm-duplicate-contacts-from-web-forms/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "crm-duplicate-contacts-from-web-forms",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/crm-duplicate-contacts-from-web-forms/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=crm-duplicate-contacts-from-web-forms&source=%2Fservices%2Fcrm-duplicate-contacts-from-web-forms%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=crm-duplicate-contacts-from-web-forms&body=SI-Outcome%3A%20crm-duplicate-contacts-from-web-forms%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fcrm-duplicate-contacts-from-web-forms%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "crm-web-lead-pipeline-repair",
      "type": "composed",
      "title": "Repair the path from web form to salesperson, end to end",
      "description": "Up to three web-form paths into HubSpot are made to create one contact, find one owner, raise an alert on failure and sync agreed fields, proved with a synthetic lead through each.",
      "url": "/services/crm-web-lead-pipeline-repair/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "hubspot",
        "salesforce",
        "web-forms"
      ],
      "related": [],
      "provenance": [
        {
          "label": "HubSpot documents form-submission contact matching by email, company matching by domain, and that companies created through the API are not de-duplicated by domain.",
          "url": "https://knowledge.hubspot.com/records/deduplication-of-records"
        },
        {
          "label": "HubSpot documents the rotate-to-owner workflow action and its subscription requirement.",
          "url": "https://knowledge.hubspot.com/workflows/assign-and-rotate-record-owners-using-workflows"
        },
        {
          "label": "HubSpot documents the four Salesforce sync rules and field type compatibility.",
          "url": "https://knowledge.hubspot.com/salesforce/map-hubspot-properties-to-salesforce-fields"
        },
        {
          "label": "HubSpot documents the Salesforce sync error types and their fixes, and that the Sync Health tab lists them.",
          "url": "https://knowledge.hubspot.com/salesforce/resolve-salesforce-integration-sync-errors"
        }
      ],
      "incoming": [
        "buyer-marketing-manager-web-lead-flow",
        "collection-web-lead-to-owner-checks",
        "guide-hubspot-form-duplicates-contacts-companies",
        "guide-hubspot-salesforce-sync-rules-and-errors",
        "platform-hubspot",
        "guide-integrate-pipedrive-person-lead-handoff-and-limits"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "crm-web-lead-pipeline-repair",
          "source": "/services/crm-web-lead-pipeline-repair/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "crm-web-lead-pipeline-repair",
          "SI-Mode": "quote",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/crm-web-lead-pipeline-repair/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=crm-web-lead-pipeline-repair&source=%2Fservices%2Fcrm-web-lead-pipeline-repair%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=crm-web-lead-pipeline-repair&body=SI-Outcome%3A%20crm-web-lead-pipeline-repair%0ASI-Mode%3A%20quote%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fcrm-web-lead-pipeline-repair%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "extract-pdf-batches-every-month",
      "type": "recurring",
      "title": "Have each month's batch of same-layout PDFs turned into checked spreadsheet rows",
      "description": "Each month's PDFs in one agreed layout are extracted into your workbook with a page or file reconciliation and an exceptions list, so nobody retypes them.",
      "url": "/services/extract-pdf-batches-every-month/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "pdf-documents",
        "microsoft-excel"
      ],
      "related": [
        "pdf-scanned-batch-to-searchable-text",
        "report-pack-every-period"
      ],
      "provenance": [
        {
          "label": "The pdfplumber project says its table extraction works best on machine-generated PDFs and offers no text recognition, which is why image-only pages are set aside and listed.",
          "url": "https://github.com/jsvine/pdfplumber"
        },
        {
          "label": "Microsoft documents a PDF connector for Excel that returns tables found in a PDF and says rows spread over several lines may need cleaning afterwards.",
          "url": "https://learn.microsoft.com/en-us/power-query/connectors/pdf"
        }
      ],
      "incoming": [
        "extract-pdf-form-fields-to-spreadsheet",
        "extract-pdf-price-lists-to-spreadsheet",
        "platform-pdf-documents",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "guide-extract-reconciliation-method",
        "guide-pdf-form-field-data-export"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "extract-pdf-batches-every-month",
          "source": "/services/extract-pdf-batches-every-month/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "extract-pdf-batches-every-month",
          "SI-Mode": "quote",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/extract-pdf-batches-every-month/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=extract-pdf-batches-every-month&source=%2Fservices%2Fextract-pdf-batches-every-month%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=extract-pdf-batches-every-month&body=SI-Outcome%3A%20extract-pdf-batches-every-month%0ASI-Mode%3A%20quote%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fextract-pdf-batches-every-month%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "extract-pdf-form-fields-to-spreadsheet",
      "type": "outcome",
      "title": "Collect the answers from a batch of filled-in PDF forms into one sheet",
      "description": "The values typed into a batch of fillable PDF forms from one template arrive as one spreadsheet, one row per form, checked against the field list, with unreadable files listed.",
      "url": "/services/extract-pdf-form-fields-to-spreadsheet/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "pdf-documents",
        "microsoft-excel"
      ],
      "related": [
        "extract-pdf-price-lists-to-spreadsheet",
        "pdf-scanned-batch-to-searchable-text",
        "extract-pdf-batches-every-month"
      ],
      "provenance": [
        {
          "label": "The pypdf documentation describes form fields stored in a PDF's interactive form data, with values read by field name. It also notes that an XFA form entry can override the page content.",
          "url": "https://pypdf.readthedocs.io/en/stable/user/forms.html"
        }
      ],
      "incoming": [
        "extract-pdf-batches-every-month",
        "extract-pdf-price-lists-to-spreadsheet",
        "platform-pdf-documents",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "guide-extract-reconciliation-method",
        "guide-pdf-form-field-data-export"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "extract-pdf-form-fields-to-spreadsheet",
          "source": "/services/extract-pdf-form-fields-to-spreadsheet/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "extract-pdf-form-fields-to-spreadsheet",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/extract-pdf-form-fields-to-spreadsheet/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=extract-pdf-form-fields-to-spreadsheet&source=%2Fservices%2Fextract-pdf-form-fields-to-spreadsheet%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=extract-pdf-form-fields-to-spreadsheet&body=SI-Outcome%3A%20extract-pdf-form-fields-to-spreadsheet%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fextract-pdf-form-fields-to-spreadsheet%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "extract-pdf-price-lists-to-spreadsheet",
      "type": "outcome",
      "title": "Turn a text-based PDF price list into one checked spreadsheet",
      "description": "One PDF price list in one layout becomes one spreadsheet whose row counts match the source page by page. Every row we could not read with confidence is listed for you to decide.",
      "url": "/services/extract-pdf-price-lists-to-spreadsheet/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "pdf-documents",
        "microsoft-excel"
      ],
      "related": [
        "extract-pdf-form-fields-to-spreadsheet",
        "pdf-scanned-batch-to-searchable-text",
        "extract-pdf-batches-every-month"
      ],
      "provenance": [
        {
          "label": "Microsoft documents a PDF connector for Excel and Power Query that returns the tables it finds in a PDF, and says rows that run over several lines may need cleaning afterwards.",
          "url": "https://learn.microsoft.com/en-us/power-query/connectors/pdf"
        },
        {
          "label": "The pdfplumber project says its table extraction works best on machine-generated PDFs, offers no text recognition and is weak on tables from scanned documents. This is why a text layer is an eligibility test.",
          "url": "https://github.com/jsvine/pdfplumber"
        },
        {
          "label": "Docparser advertises plan prices and a per-layout setup fee for parsing PDFs into spreadsheets. This is a competing seller ask, not a paid price or a buyer request.",
          "url": "https://docparser.com/pricing/",
          "checked": "2026-10-11"
        }
      ],
      "incoming": [
        "extract-pdf-batches-every-month",
        "extract-pdf-form-fields-to-spreadsheet",
        "pdf-scanned-batch-to-searchable-text",
        "example-pdf-price-list-reconciliation",
        "platform-pdf-documents",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "guide-pdf-text-layer-or-scan-check",
        "guide-extract-reconciliation-method",
        "tool-pack-unit-price-calculator"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "extract-pdf-price-lists-to-spreadsheet",
          "source": "/services/extract-pdf-price-lists-to-spreadsheet/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "extract-pdf-price-lists-to-spreadsheet",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/extract-pdf-price-lists-to-spreadsheet/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=extract-pdf-price-lists-to-spreadsheet&source=%2Fservices%2Fextract-pdf-price-lists-to-spreadsheet%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=extract-pdf-price-lists-to-spreadsheet&body=SI-Outcome%3A%20extract-pdf-price-lists-to-spreadsheet%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fextract-pdf-price-lists-to-spreadsheet%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "hubspot-salesforce-synced-field-reverts",
      "type": "outcome",
      "title": "Make one HubSpot and Salesforce field sync the way you agreed",
      "description": "For one mapped field, a change on a synthetic test record crosses in the agreed direction, stays after the sync settles, and raises no sync error.",
      "url": "/services/hubspot-salesforce-synced-field-reverts/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "hubspot",
        "salesforce"
      ],
      "related": [
        "lead-routing-new-leads-unassigned-or-wrong-owner",
        "crm-duplicate-contacts-from-web-forms",
        "zapier-facebook-leads-to-salesforce"
      ],
      "provenance": [
        {
          "label": "HubSpot documents the four sync rules, that existing values do not sync retroactively, that a first sync uses the current Salesforce value as the baseline, and which field types are compatible.",
          "url": "https://knowledge.hubspot.com/salesforce/map-hubspot-properties-to-salesforce-fields"
        },
        {
          "label": "HubSpot documents sync error types and fixes, including mismatched picklist options, restricted picklists, field permissions and the manual resync cap of 100 errors at a time.",
          "url": "https://knowledge.hubspot.com/salesforce/resolve-salesforce-integration-sync-errors"
        },
        {
          "label": "HubSpot warns that changing an existing property's field type can invalidate the current values stored in the property, and recommends exporting all information before editing a property's field type.",
          "url": "https://knowledge.hubspot.com/properties/create-and-edit-properties"
        }
      ],
      "incoming": [
        "crm-duplicate-contacts-from-web-forms",
        "crm-web-lead-pipeline-repair",
        "lead-routing-new-leads-unassigned-or-wrong-owner",
        "buyer-marketing-manager-web-lead-flow",
        "collection-web-lead-to-owner-checks",
        "guide-hubspot-salesforce-sync-rules-and-errors",
        "platform-hubspot"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "hubspot-salesforce-synced-field-reverts",
          "source": "/services/hubspot-salesforce-synced-field-reverts/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "hubspot-salesforce-synced-field-reverts",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/hubspot-salesforce-synced-field-reverts/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=hubspot-salesforce-synced-field-reverts&source=%2Fservices%2Fhubspot-salesforce-synced-field-reverts%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=hubspot-salesforce-synced-field-reverts&body=SI-Outcome%3A%20hubspot-salesforce-synced-field-reverts%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fhubspot-salesforce-synced-field-reverts%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "lead-routing-new-leads-unassigned-or-wrong-owner",
      "type": "outcome",
      "title": "Make every new HubSpot lead land with the owner your rules name",
      "description": "Synthetic leads land with the owners your written rules name, an unmatched lead lands with a named fallback owner, and a user removed from the pool receives none.",
      "url": "/services/lead-routing-new-leads-unassigned-or-wrong-owner/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "hubspot"
      ],
      "related": [
        "crm-duplicate-contacts-from-web-forms",
        "hubspot-salesforce-synced-field-reverts",
        "auto-failure-alerts-reach-a-named-owner"
      ],
      "provenance": [
        {
          "label": "HubSpot documents the rotate-to-owner action, its subscription requirement, that for most record types assignment counts reset when owners are added or removed (and that these rules do not apply to lead-based or ticket-based workflows), the exclusion of deactivated users and the no-one option leaving records unassigned. It also warns that if the owner property syncs to Salesforce, Salesforce may try to update HubSpot with the previous owner.",
          "url": "https://knowledge.hubspot.com/workflows/assign-and-rotate-record-owners-using-workflows"
        },
        {
          "label": "HubSpot documents that form notifications go only to owners when a contact has one, and that a contact with no owner receives no owner notification.",
          "url": "https://knowledge.hubspot.com/forms/create-and-edit-forms"
        }
      ],
      "incoming": [
        "crm-duplicate-contacts-from-web-forms",
        "crm-web-lead-pipeline-repair",
        "hubspot-salesforce-synced-field-reverts",
        "buyer-marketing-manager-web-lead-flow",
        "collection-web-lead-to-owner-checks",
        "guide-hubspot-lead-owner-rotation-fallbacks",
        "platform-hubspot"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "lead-routing-new-leads-unassigned-or-wrong-owner",
          "source": "/services/lead-routing-new-leads-unassigned-or-wrong-owner/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "lead-routing-new-leads-unassigned-or-wrong-owner",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/lead-routing-new-leads-unassigned-or-wrong-owner/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=lead-routing-new-leads-unassigned-or-wrong-owner&source=%2Fservices%2Flead-routing-new-leads-unassigned-or-wrong-owner%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=lead-routing-new-leads-unassigned-or-wrong-owner&body=SI-Outcome%3A%20lead-routing-new-leads-unassigned-or-wrong-owner%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Flead-routing-new-leads-unassigned-or-wrong-owner%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "make-scenario-stops-on-one-bad-record",
      "type": "outcome",
      "title": "Set one bad record aside in a Make scenario and tell a named person",
      "description": "On a copy fed by a disposable source, one bad record in a synthetic batch is held or skipped as you agree and a named person is alerted. The good records are processed once and the next run is tested.",
      "url": "/services/make-scenario-stops-on-one-bad-record/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "make"
      ],
      "related": [
        "auto-failure-alerts-reach-a-named-owner",
        "workflow-api-polling-no-gaps-no-repeats"
      ],
      "provenance": [
        {
          "label": "Make documents that without a handler and with incomplete executions off, rollback is the default, and a scenario is deactivated after the configured number of consecutive errors (default three). It also documents that an error route with no handler skips the error, that the Skip handler marks the run as successful, and that when an incomplete execution is created Make postpones the next scenario run until it is resolved or the Retry handler resolves it.",
          "url": "https://help.make.com/Overview-of-error-handling"
        },
        {
          "label": "Make documents that the Retry error handler pulls the failing bundle out of the flow, processes the rest of the bundles, stores an incomplete execution, and requires incomplete executions to be enabled in the scenario settings.",
          "url": "https://help.make.com/retry-error-handler"
        },
        {
          "label": "Make documents incomplete executions as off by default, enabled with Store incomplete executions in the scenario settings.",
          "url": "https://help.make.com/incomplete-executions"
        },
        {
          "label": "Make lists the Skip, Retry, Resume, Commit and Rollback error handlers.",
          "url": "https://help.make.com/error-handlers"
        },
        {
          "label": "Make documents that, with processing in order on, unresolved incomplete executions hold back new runs, and that Keep data confidential means Make does not keep the processed data.",
          "url": "https://help.make.com/scenario-settings"
        }
      ],
      "incoming": [
        "airtable-automation-fails-or-skips-records",
        "auto-failure-alerts-reach-a-named-owner",
        "workflow-api-polling-no-gaps-no-repeats",
        "buyer-operations-manager-inherited-automations",
        "guide-make-error-handlers-and-incomplete-executions",
        "platform-make"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "make-scenario-stops-on-one-bad-record",
          "source": "/services/make-scenario-stops-on-one-bad-record/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "make-scenario-stops-on-one-bad-record",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/make-scenario-stops-on-one-bad-record/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=make-scenario-stops-on-one-bad-record&source=%2Fservices%2Fmake-scenario-stops-on-one-bad-record%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=make-scenario-stops-on-one-bad-record&body=SI-Outcome%3A%20make-scenario-stops-on-one-bad-record%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fmake-scenario-stops-on-one-bad-record%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "pdf-scanned-batch-to-searchable-text",
      "type": "outcome",
      "title": "Make a batch of scanned PDFs searchable and test it with terms you choose",
      "description": "Image-only PDFs in one folder get an added text layer, with originals kept. A search test of terms you choose is held to a floor agreed in writing beforehand, and poor pages are listed.",
      "url": "/services/pdf-scanned-batch-to-searchable-text/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "pdf-documents"
      ],
      "related": [
        "extract-pdf-price-lists-to-spreadsheet",
        "archive-rename-organise-with-manifest",
        "archive-digitise-and-normalise-one-archive"
      ],
      "provenance": [
        {
          "label": "The OCRmyPDF documentation says it adds a text layer to scanned PDFs, can produce a minimally altered PDF, defaults to PDF/A-2b, may not match commercial accuracy, depends on input quality and cannot recognise handwriting.",
          "url": "https://ocrmypdf.readthedocs.io/en/latest/introduction.html"
        },
        {
          "label": "The Tesseract documentation says it works best on images of at least 300 dpi, and that heavy skew, noise and borders reduce recognition quality.",
          "url": "https://tesseract-ocr.github.io/tessdoc/ImproveQuality.html"
        },
        {
          "label": "The Library of Congress format description says level B PDF/A files made from scanned page images do not necessarily support indexing of the document text.",
          "url": "https://www.loc.gov/preservation/digital/formats/fdd/fdd000318.shtml"
        }
      ],
      "incoming": [
        "archive-digitise-and-normalise-one-archive",
        "archive-rename-organise-with-manifest",
        "convert-legacy-office-files-batch",
        "extract-pdf-batches-every-month",
        "extract-pdf-form-fields-to-spreadsheet",
        "extract-pdf-price-lists-to-spreadsheet",
        "platform-pdf-documents",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "guide-pdf-text-layer-or-scan-check",
        "guide-pdf-scan-ocr-search-check"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "pdf-scanned-batch-to-searchable-text",
          "source": "/services/pdf-scanned-batch-to-searchable-text/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "pdf-scanned-batch-to-searchable-text",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/pdf-scanned-batch-to-searchable-text/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=pdf-scanned-batch-to-searchable-text&source=%2Fservices%2Fpdf-scanned-batch-to-searchable-text%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=pdf-scanned-batch-to-searchable-text&body=SI-Outcome%3A%20pdf-scanned-batch-to-searchable-text%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fpdf-scanned-batch-to-searchable-text%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "quote-pack-from-customer-brief",
      "type": "outcome",
      "title": "Produce a batch of customer quotations from your price list and briefs",
      "description": "Each customer brief (a reference, item codes and quantities) becomes one checked quotation in your template, with totals worked from your price list and rules and a proof sheet of every calculation.",
      "url": "/services/quote-pack-from-customer-brief/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "microsoft-excel",
        "microsoft-word"
      ],
      "related": [
        "spreadsheet-repair-broken-formulas",
        "report-pack-every-period",
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "Microsoft documents that for a Word mail merge the column names in the spreadsheet should match the field names, that all the data to be merged should be on the first sheet and that Preview Results steps through the records.",
          "url": "https://support.microsoft.com/en-us/word/mail-merge-using-an-excel-spreadsheet"
        },
        {
          "label": "Microsoft documents that merged numbers come through without currency or percent symbols, so symbols are added in the Word document, and that a percentage format multiplies the cell value by 100.",
          "url": "https://support.microsoft.com/en-us/word/prepare-your-excel-data-source-for-a-word-mail-merge"
        },
        {
          "label": "Microsoft documents that Excel follows the IEEE 754 specification with 15 digits of precision and offers the ROUND function to compensate for rounding errors.",
          "url": "https://learn.microsoft.com/en-us/troubleshoot/microsoft-365-apps/excel/floating-point-arithmetic-inaccurate-result"
        }
      ],
      "incoming": [
        "report-pack-every-period",
        "example-quote-line-rounding",
        "platform-microsoft-excel",
        "buyer-report-operations-lead-weekly-pack",
        "guide-mailmerge-fields-and-number-formats"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "quote-pack-from-customer-brief",
          "source": "/services/quote-pack-from-customer-brief/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "quote-pack-from-customer-brief",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/quote-pack-from-customer-brief/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=quote-pack-from-customer-brief&source=%2Fservices%2Fquote-pack-from-customer-brief%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=quote-pack-from-customer-brief&body=SI-Outcome%3A%20quote-pack-from-customer-brief%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fquote-pack-from-customer-brief%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "report-pack-every-period",
      "type": "recurring",
      "title": "Get your weekly or monthly report pack built from your standard export",
      "description": "Each period we turn the export you send into the same formatted report pack, check its totals against the export and note anything odd, month after month.",
      "url": "/services/report-pack-every-period/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "microsoft-excel",
        "csv"
      ],
      "related": [
        "quote-pack-from-customer-brief",
        "spreadsheet-flatten-merged-cell-report"
      ],
      "provenance": [
        {
          "label": "Microsoft says a PivotTable works from a snapshot of its source and must be refreshed when the source data changes, and that an Excel table as the source includes newly added rows on refresh.",
          "url": "https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576"
        }
      ],
      "incoming": [
        "extract-pdf-batches-every-month",
        "quote-pack-from-customer-brief",
        "platform-microsoft-excel",
        "buyer-report-operations-lead-weekly-pack",
        "guide-report-pack-repeatable-export"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "report-pack-every-period",
          "source": "/services/report-pack-every-period/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "report-pack-every-period",
          "SI-Mode": "quote",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/report-pack-every-period/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=report-pack-every-period&source=%2Fservices%2Freport-pack-every-period%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=report-pack-every-period&body=SI-Outcome%3A%20report-pack-every-period%0ASI-Mode%3A%20quote%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Freport-pack-every-period%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "spreadsheet-flatten-merged-cell-report",
      "type": "outcome",
      "title": "Turn a merged-cell Excel report into a flat table you can sort and pivot",
      "description": "One sheet of merged cells, repeated headings and subtotal rows becomes a flat table with one row per record, whose row count and totals reconcile with the original.",
      "url": "/services/spreadsheet-flatten-merged-cell-report/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "microsoft-excel"
      ],
      "related": [
        "spreadsheet-repair-broken-formulas",
        "spreadsheet-normalise-mixed-dates-and-numbers",
        "spreadsheet-make-workbook-set-import-ready"
      ],
      "provenance": [
        {
          "label": "Microsoft says Excel does not sort data in a column that contains merged cells, and describes how to find them.",
          "url": "https://support.microsoft.com/en-us/office/find-merged-cells-d02b2a5a-a08d-4641-8d4d-b3f233daca2c"
        },
        {
          "label": "Microsoft says merging keeps only the upper-left cell's contents, and that unmerging moves the data to the left cell.",
          "url": "https://support.microsoft.com/en-us/office/merge-and-unmerge-cells-5cbd15d5-9375-4540-907f-c673a93fcedf"
        },
        {
          "label": "Microsoft says a PivotTable source should avoid double header rows and merged cells and have no blank rows or columns.",
          "url": "https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576"
        }
      ],
      "incoming": [
        "report-pack-every-period",
        "spreadsheet-make-workbook-set-import-ready",
        "spreadsheet-normalise-mixed-dates-and-numbers",
        "spreadsheet-remove-duplicate-contacts",
        "spreadsheet-repair-broken-formulas",
        "example-merged-report-flattened",
        "platform-microsoft-excel",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-excel-merged-cells-and-tidy-tables"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "spreadsheet-flatten-merged-cell-report",
          "source": "/services/spreadsheet-flatten-merged-cell-report/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "spreadsheet-flatten-merged-cell-report",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/spreadsheet-flatten-merged-cell-report/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=spreadsheet-flatten-merged-cell-report&source=%2Fservices%2Fspreadsheet-flatten-merged-cell-report%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=spreadsheet-flatten-merged-cell-report&body=SI-Outcome%3A%20spreadsheet-flatten-merged-cell-report%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fspreadsheet-flatten-merged-cell-report%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "spreadsheet-make-workbook-set-import-ready",
      "type": "composed",
      "title": "Make one set of messy workbooks clean enough to import, analyse or hand over",
      "description": "A defined set of inherited workbooks is repaired, flattened and date-fixed into consistent tables that pass a column-by-column readiness check you can use before any import.",
      "url": "/services/spreadsheet-make-workbook-set-import-ready/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "microsoft-excel",
        "csv"
      ],
      "related": [
        "spreadsheet-repair-broken-formulas",
        "spreadsheet-flatten-merged-cell-report",
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "Microsoft says a PivotTable source should have one header row, no merged cells, no blank rows or columns and one data type per column; the readiness sheet applies the same ideas to any import.",
          "url": "https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576"
        },
        {
          "label": "PostgreSQL documents that in CSV input an unquoted empty string is read as NULL by default, one example of why blank cells need an explicit rule before a load.",
          "url": "https://www.postgresql.org/docs/current/sql-copy.html"
        }
      ],
      "incoming": [
        "spreadsheet-flatten-merged-cell-report",
        "spreadsheet-normalise-mixed-dates-and-numbers",
        "spreadsheet-remove-duplicate-contacts",
        "platform-microsoft-excel",
        "buyer-report-operations-lead-weekly-pack",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-excel-error-values-trace",
        "guide-excel-merged-cells-and-tidy-tables",
        "guide-excel-date-serials-and-regional-order",
        "guide-spreadsheet-contact-duplicate-match-rules",
        "guide-spreadsheet-import-readiness"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "spreadsheet-make-workbook-set-import-ready",
          "source": "/services/spreadsheet-make-workbook-set-import-ready/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "spreadsheet-make-workbook-set-import-ready",
          "SI-Mode": "quote",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/spreadsheet-make-workbook-set-import-ready/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=spreadsheet-make-workbook-set-import-ready&source=%2Fservices%2Fspreadsheet-make-workbook-set-import-ready%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=spreadsheet-make-workbook-set-import-ready&body=SI-Outcome%3A%20spreadsheet-make-workbook-set-import-ready%0ASI-Mode%3A%20quote%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fspreadsheet-make-workbook-set-import-ready%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "spreadsheet-normalise-mixed-dates-and-numbers",
      "type": "outcome",
      "title": "Fix mixed date and decimal formats in one spreadsheet or CSV export",
      "description": "Dates typed as text, day-first and month-first entries mixed together, and comma or point decimals become real dates and numbers. Ambiguous rows are listed, not guessed.",
      "url": "/services/spreadsheet-normalise-mixed-dates-and-numbers/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "microsoft-excel",
        "csv"
      ],
      "related": [
        "spreadsheet-repair-broken-formulas",
        "spreadsheet-flatten-merged-cell-report",
        "spreadsheet-make-workbook-set-import-ready"
      ],
      "provenance": [
        {
          "label": "Microsoft documents that Excel stores dates as serial numbers in either a 1900 or a 1904 system 1,462 days apart, and that pasting dates between workbooks that use different systems can shift them.",
          "url": "https://support.microsoft.com/en-us/excel/date-systems-in-excel"
        },
        {
          "label": "Microsoft documents text dates (left-aligned) and conversion with DATEVALUE. It says that a date with a two-digit year entered in a text-formatted cell, or as a text argument in a function, is read as 2000 to 2029 for 00 to 29 and as 1930 to 1999 for 30 to 99, and that in Windows the cutoff can be changed for all programs.",
          "url": "https://support.microsoft.com/en-us/office/change-the-date-system-format-or-two-digit-year-interpretation-aaa2159b-4ae8-4651-8bce-d4707bc9fb9f"
        },
        {
          "label": "The W3C note on date and time formats gives YYYY-MM-DD as an unambiguous date representation.",
          "url": "https://www.w3.org/TR/NOTE-datetime"
        }
      ],
      "incoming": [
        "archive-digitise-and-normalise-one-archive",
        "report-pack-every-period",
        "spreadsheet-flatten-merged-cell-report",
        "spreadsheet-make-workbook-set-import-ready",
        "spreadsheet-remove-duplicate-contacts",
        "spreadsheet-repair-broken-formulas",
        "guide-etl-erp-export-numbers-and-dates-to-import-format",
        "example-date-ambiguity-matrix",
        "platform-microsoft-excel",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-excel-date-serials-and-regional-order"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "spreadsheet-normalise-mixed-dates-and-numbers",
          "source": "/services/spreadsheet-normalise-mixed-dates-and-numbers/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "spreadsheet-normalise-mixed-dates-and-numbers",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/spreadsheet-normalise-mixed-dates-and-numbers/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=spreadsheet-normalise-mixed-dates-and-numbers&source=%2Fservices%2Fspreadsheet-normalise-mixed-dates-and-numbers%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=spreadsheet-normalise-mixed-dates-and-numbers&body=SI-Outcome%3A%20spreadsheet-normalise-mixed-dates-and-numbers%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fspreadsheet-normalise-mixed-dates-and-numbers%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "spreadsheet-remove-duplicate-contacts",
      "type": "outcome",
      "title": "Merge duplicate rows in one product, part, asset or company list, with a review file",
      "description": "One list of items, parts, assets or companies, with no personal data, is checked against written rules. Clear duplicates are merged with a log; uncertain pairs stay separate for you to decide.",
      "url": "/services/spreadsheet-remove-duplicate-contacts/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "microsoft-excel",
        "csv"
      ],
      "related": [
        "spreadsheet-normalise-mixed-dates-and-numbers",
        "spreadsheet-flatten-merged-cell-report",
        "spreadsheet-make-workbook-set-import-ready"
      ],
      "provenance": [
        {
          "label": "Microsoft says Remove Duplicates judges duplicates by the value displayed in the cell, deletes rows permanently and removes data from all columns, and recommends copying the original data first.",
          "url": "https://support.microsoft.com/en-us/office/filter-for-unique-values-or-remove-duplicate-values-ccf664b0-81d6-449b-bbe1-8daaec1e83c2"
        },
        {
          "label": "Microsoft documents that Power Query's Remove Duplicates works on the columns you select, so a different selection can give a different result.",
          "url": "https://support.microsoft.com/en-us/office/keep-or-remove-duplicate-rows-power-query-d9cffc69-dc5d-4d94-8b66-72779688874d"
        }
      ],
      "incoming": [
        "spreadsheet-make-workbook-set-import-ready",
        "platform-microsoft-excel",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-spreadsheet-contact-duplicate-match-rules"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "spreadsheet-remove-duplicate-contacts",
          "source": "/services/spreadsheet-remove-duplicate-contacts/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "spreadsheet-remove-duplicate-contacts",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/spreadsheet-remove-duplicate-contacts/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=spreadsheet-remove-duplicate-contacts&source=%2Fservices%2Fspreadsheet-remove-duplicate-contacts%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=spreadsheet-remove-duplicate-contacts&body=SI-Outcome%3A%20spreadsheet-remove-duplicate-contacts%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fspreadsheet-remove-duplicate-contacts%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "spreadsheet-repair-broken-formulas",
      "type": "outcome",
      "title": "Repair the broken formulas in one Excel workbook so its totals check out",
      "description": "The error values and wrong totals in one workbook are traced and fixed until your agreed check cases return the answers you calculated by hand, with every change listed.",
      "url": "/services/spreadsheet-repair-broken-formulas/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "microsoft-excel"
      ],
      "related": [
        "spreadsheet-flatten-merged-cell-report",
        "spreadsheet-normalise-mixed-dates-and-numbers",
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "Microsoft documents #REF! as a formula that points at deleted or overwritten cells, and #NAME? as most often a misspelt function or undefined name; it says not to use IFERROR to mask the #NAME? error.",
          "url": "https://support.microsoft.com/en-us/excel/how-to-correct-a-ref-error"
        },
        {
          "label": "Microsoft documents Excel's error checking rules, including inconsistent formulas and numbers stored as text, and states that the rules do not guarantee an error-free worksheet.",
          "url": "https://support.microsoft.com/en-us/excel/detect-formula-errors-in-excel"
        },
        {
          "label": "Microsoft documents that Manual calculation mode updates formulas only when you recalculate, and that Automatic is the default.",
          "url": "https://support.microsoft.com/en-us/office/change-formula-recalculation-iteration-or-precision-in-excel-73fc7dac-91cf-4d36-86e8-67124f6bcce4"
        }
      ],
      "incoming": [
        "quote-pack-from-customer-brief",
        "report-pack-every-period",
        "spreadsheet-flatten-merged-cell-report",
        "spreadsheet-make-workbook-set-import-ready",
        "spreadsheet-normalise-mixed-dates-and-numbers",
        "platform-microsoft-excel",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-excel-error-values-trace",
        "guide-excel-stale-or-text-numbers"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "spreadsheet-repair-broken-formulas",
          "source": "/services/spreadsheet-repair-broken-formulas/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "spreadsheet-repair-broken-formulas",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/spreadsheet-repair-broken-formulas/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=spreadsheet-repair-broken-formulas&source=%2Fservices%2Fspreadsheet-repair-broken-formulas%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=spreadsheet-repair-broken-formulas&body=SI-Outcome%3A%20spreadsheet-repair-broken-formulas%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fspreadsheet-repair-broken-formulas%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "webhook-receiver-verified-signature-duplicate-safe",
      "type": "outcome",
      "title": "A webhook receiver that checks signatures and handles repeats and replays safely",
      "description": "One signed webhook event is accepted only with a valid signature, answered fast from a durable record, and acted on once in effect when delivered twice or replayed from a captured copy.",
      "url": "/services/webhook-receiver-verified-signature-duplicate-safe/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "webhooks",
        "hmac"
      ],
      "related": [
        "workflow-api-polling-no-gaps-no-repeats",
        "auto-failure-alerts-reach-a-named-owner"
      ],
      "provenance": [
        {
          "label": "GitHub documents HMAC-SHA256 signature verification with the X-Hub-Signature-256 header, a constant-time comparison, and verifying before further processing. Its published test vector derives the signature from the secret and the payload alone.",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries"
        },
        {
          "label": "GitHub documents responding with a 2XX within 10 seconds and queuing work so the server can acknowledge first, names the X-GitHub-Delivery header as a defence against replay attacks, and says a redelivery keeps the same X-GitHub-Delivery value.",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/best-practices-for-using-webhooks"
        },
        {
          "label": "Shopify documents a base64 HMAC-SHA256 over the raw request body, a short response deadline, and de-duplicating deliveries by the X-Shopify-Webhook-Id header or processing idempotently.",
          "url": "https://shopify.dev/docs/apps/build/webhooks/subscribe/https"
        },
        {
          "label": "Stripe documents that its signed payload is the timestamp, a full stop and the body, that its libraries have a default tolerance of 5 minutes between the timestamp and the current time as protection against replay attacks, and that duplicate events are guarded against by logging processed event IDs.",
          "url": "https://docs.stripe.com/webhooks"
        }
      ],
      "incoming": [
        "crm-web-lead-pipeline-repair",
        "workflow-api-polling-no-gaps-no-repeats",
        "example-webhook-signature-test-vectors",
        "example-webhook-delivery-ledger-repeat-safe",
        "guide-api-polling-checkpoint-overlap-keys",
        "guide-webhook-signature-raw-body",
        "guide-webhook-timeouts-retries-missed-events",
        "guide-n8n-webhook-test-and-production-urls",
        "example-api-webhook-signature-test-vector",
        "guide-api-webhook-retries-idempotency",
        "example-slack-signature-and-replay-window-cases",
        "guide-slack-events-signature-timestamp-and-three-second-ack"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "webhook-receiver-verified-signature-duplicate-safe",
          "source": "/services/webhook-receiver-verified-signature-duplicate-safe/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "webhook-receiver-verified-signature-duplicate-safe",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/webhook-receiver-verified-signature-duplicate-safe/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=webhook-receiver-verified-signature-duplicate-safe&source=%2Fservices%2Fwebhook-receiver-verified-signature-duplicate-safe%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=webhook-receiver-verified-signature-duplicate-safe&body=SI-Outcome%3A%20webhook-receiver-verified-signature-duplicate-safe%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fwebhook-receiver-verified-signature-duplicate-safe%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "workflow-api-polling-no-gaps-no-repeats",
      "type": "outcome",
      "title": "Poll an API on a schedule without missing a record or acting on one twice",
      "description": "A scheduled poll of one documented API processes every new or changed record once in effect, including a late arrival and a kill at every step, proved on a synthetic copy.",
      "url": "/services/workflow-api-polling-no-gaps-no-repeats/",
      "updated": "2026-10-11",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "api-polling",
        "scheduled-jobs"
      ],
      "related": [
        "webhook-receiver-verified-signature-duplicate-safe",
        "make-scenario-stops-on-one-bad-record",
        "auto-failure-alerts-reach-a-named-owner"
      ],
      "provenance": [
        {
          "label": "Zapier documents that polling triggers need a unique id, newest-first ordering, and that it stores the ids it has seen to avoid repeating items.",
          "url": "https://docs.zapier.com/platform/build/deduplication"
        },
        {
          "label": "HubSpot documents a search page size of 200, a cap of 10,000 total results per query and a short delay before new or changed records appear in search results.",
          "url": "https://developers.hubspot.com/docs/api/crm/search"
        },
        {
          "label": "RFC 9110 section 9.2.2 says a client should not automatically retry a non-idempotent request unless it can know the request is effectively idempotent or was not applied, which is why the job makes the action safe to repeat for the same key instead of retrying blindly.",
          "url": "https://www.rfc-editor.org/rfc/rfc9110.html"
        }
      ],
      "incoming": [
        "make-scenario-stops-on-one-bad-record",
        "webhook-receiver-verified-signature-duplicate-safe",
        "example-polling-checkpoint-strategies",
        "guide-api-polling-checkpoint-overlap-keys",
        "guide-webhook-timeouts-retries-missed-events"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "workflow-api-polling-no-gaps-no-repeats",
          "source": "/services/workflow-api-polling-no-gaps-no-repeats/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "workflow-api-polling-no-gaps-no-repeats",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/workflow-api-polling-no-gaps-no-repeats/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=workflow-api-polling-no-gaps-no-repeats&source=%2Fservices%2Fworkflow-api-polling-no-gaps-no-repeats%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=workflow-api-polling-no-gaps-no-repeats&body=SI-Outcome%3A%20workflow-api-polling-no-gaps-no-repeats%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fworkflow-api-polling-no-gaps-no-repeats%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "zapier-facebook-leads-to-salesforce",
      "type": "outcome",
      "title": "Make new Facebook leads appear in Salesforce again",
      "description": "A new synthetic lead submitted to the agreed Facebook form starts one Zap and creates one Salesforce test lead with the agreed fields.",
      "url": "/services/zapier-facebook-leads-to-salesforce/",
      "updated": "2026-10-09",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "zapier",
        "facebook-lead-ads",
        "salesforce"
      ],
      "related": [
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "Commercial hypothesis for this bounded outcome, not verified buyer demand or a tested price. The 9 October direction permits reasonable offers without inventing prior sales. Earlier unverified marketplace and community quotations are not used in this release."
        }
      ],
      "incoming": [
        "hubspot-salesforce-synced-field-reverts",
        "buyer-finance-manager-invoice-integration",
        "guide-zapier-field-mapping",
        "tool-workflow-value",
        "platform-zapier",
        "guide-integrate-pipedrive-person-lead-handoff-and-limits"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "zapier-facebook-leads-to-salesforce",
          "source": "/services/zapier-facebook-leads-to-salesforce/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "zapier-facebook-leads-to-salesforce",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/zapier-facebook-leads-to-salesforce/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=zapier-facebook-leads-to-salesforce&source=%2Fservices%2Fzapier-facebook-leads-to-salesforce%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=zapier-facebook-leads-to-salesforce&body=SI-Outcome%3A%20zapier-facebook-leads-to-salesforce%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fzapier-facebook-leads-to-salesforce%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "zapier-sheets-to-xero-missing-columns",
      "type": "outcome",
      "title": "Send every mapped Google Sheets invoice field to Xero",
      "description": "One disposable row with the agreed columns makes one draft Xero invoice containing every agreed line item and amount.",
      "url": "/services/zapier-sheets-to-xero-missing-columns/",
      "updated": "2026-10-09",
      "version": 1,
      "lifecycle": "candidate",
      "platforms": [
        "zapier",
        "google-sheets",
        "xero"
      ],
      "related": [],
      "provenance": [
        {
          "label": "Commercial hypothesis for this bounded outcome, not verified buyer demand or a tested price. The 9 October direction permits reasonable offers without inventing prior sales. Earlier unverified marketplace and community quotations are not used in this release."
        }
      ],
      "incoming": [
        "quote-pack-from-customer-brief",
        "spreadsheet-make-workbook-set-import-ready",
        "spreadsheet-repair-broken-formulas",
        "zapier-facebook-leads-to-salesforce",
        "buyer-finance-manager-invoice-integration",
        "guide-zapier-field-mapping",
        "guide-sheets-xero-line-item-shape",
        "tool-workflow-value",
        "platform-zapier",
        "platform-xero",
        "tool-invoice-rounding-bench",
        "example-invoice-tax-rounding-reconciliation",
        "example-invoice-retry-identity-matrix",
        "collection-finance-invoice-integrity-checks",
        "guide-sheets-invoice-value-rendering",
        "guide-invoice-tax-amount-basis",
        "guide-invoice-create-unknown-outcome"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "zapier-sheets-to-xero-missing-columns",
          "source": "/services/zapier-sheets-to-xero-missing-columns/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "zapier-sheets-to-xero-missing-columns",
          "SI-Mode": "request",
          "SI-Page": "https://backoffice.syntheticindustry.ai/services/zapier-sheets-to-xero-missing-columns/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=zapier-sheets-to-xero-missing-columns&source=%2Fservices%2Fzapier-sheets-to-xero-missing-columns%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=zapier-sheets-to-xero-missing-columns&body=SI-Outcome%3A%20zapier-sheets-to-xero-missing-columns%0ASI-Mode%3A%20request%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fservices%2Fzapier-sheets-to-xero-missing-columns%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "buyer-finance-manager-invoice-integration",
      "type": "buyer",
      "title": "For a finance manager: make an invoice integration complete and safe to repeat",
      "description": "Define authoritative fields, duplicate rules and reconciliation before repairing a spreadsheet-to-accounting automation.",
      "slug": "finance-manager-invoice-integration",
      "url": "/buyers/finance-manager-invoice-integration/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-10",
      "provenance": [
        {
          "label": "Zapier: action troubleshooting",
          "url": "https://help.zapier.com/hc/en-us/articles/8496037690637-Test-your-Zap-actions",
          "checked": "2026-10-10",
          "claims": [
            "The retrieved official page describes inspecting step errors, response details and outbound requests, and resolving missing values, formatting or permission problems before retry."
          ]
        }
      ],
      "related": [
        "zapier-sheets-to-xero-missing-columns",
        "guide-zapier-field-mapping",
        "tool-workflow-value",
        "zapier-facebook-leads-to-salesforce"
      ],
      "sections": [
        {
          "heading": "Specify the record, not just the connection",
          "body": [
            "Write an example invoice using synthetic values: supplier/customer reference, dates, currency, line items, amounts and the agreed accounting codes. Identify which system is authoritative for each field. A connection test can succeed while the business record is incomplete or assigned to the wrong organisation."
          ],
          "items": [
            "No real supplier bank details, invoice documents or customer records in an enquiry.",
            "Name mandatory fields and validation rules.",
            "Separate account mapping from tax or accounting advice, which is not included."
          ]
        },
        {
          "heading": "Decide duplicate behaviour before retrying",
          "body": [
            "If a run fails after creating a target record, retrying can create another one. Agree a stable source reference and the intended create-versus-update behaviour. The finance owner should define how exceptions are reconciled; a provider error message does not prove that no write occurred."
          ],
          "items": [
            "Test with disposable records on an authorised route.",
            "Inspect target state before replaying a failed action.",
            "Do not bulk replay historical invoices as a diagnostic step."
          ]
        },
        {
          "heading": "What acceptance should include",
          "body": [
            "Inspect the agreed synthetic invoice in the destination with every required field, expected totals and correct organisation. Repeat the same source input and verify the agreed duplicate rule. Test a missing mandatory field and ensure it is visibly rejected or routed for review instead of silently creating incomplete accounting data."
          ],
          "items": [
            "Keep redacted source-to-target results and exception evidence.",
            "Human approval stays with the finance owner for posting or consequential live changes."
          ]
        },
        {
          "heading": "Evaluate value honestly",
          "body": [
            "Estimate time saved using your own measured case volume and effort, including checking and exceptions. Time value is not automatically cash savings: staff may use freed capacity elsewhere rather than reduce payroll. The workflow calculator uses your inputs, not a promised provider ROI or our service quote."
          ],
          "items": []
        }
      ],
      "incoming": [
        "guide-zapier-field-mapping",
        "guide-sheets-xero-line-item-shape",
        "tool-workflow-value",
        "tool-invoice-rounding-bench",
        "example-invoice-retry-identity-matrix",
        "collection-finance-invoice-integrity-checks",
        "guide-invoice-tax-amount-basis",
        "guide-invoice-create-unknown-outcome"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/buyers/finance-manager-invoice-integration/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/buyers/finance-manager-invoice-integration/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fbuyers%2Ffinance-manager-invoice-integration%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fbuyers%2Ffinance-manager-invoice-integration%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-zapier-field-mapping",
      "type": "guide",
      "title": "A Zap runs, but fields are missing: trace the value from trigger to destination",
      "description": "Inspect one synthetic record through each step and separate missing input, transformation, permissions and destination validation.",
      "slug": "zapier-field-mapping",
      "url": "/guides/zapier-field-mapping/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-10",
      "provenance": [
        {
          "label": "Zapier: action troubleshooting",
          "url": "https://help.zapier.com/hc/en-us/articles/8496037690637-Test-your-Zap-actions",
          "checked": "2026-10-10",
          "claims": [
            "The retrieved official article describes inspecting failed steps, logs, response details and outbound requests.",
            "Missing values, formatting, permissions, expired credentials and limits can cause action problems."
          ]
        }
      ],
      "related": [
        "zapier-sheets-to-xero-missing-columns",
        "zapier-facebook-leads-to-salesforce",
        "buyer-finance-manager-invoice-integration",
        "tool-workflow-value"
      ],
      "sections": [
        {
          "heading": "Follow one value across the boundaries",
          "body": [
            "Choose one authorised synthetic source record with known values. Check what the trigger provided, what each transformation returned, what the action sent and what the destination stored. A workflow marked successful is not enough to prove all intended fields were mapped."
          ],
          "items": [
            "Name the destination organisation, object and field.",
            "Record whether the source value was absent, blank, transformed or rejected.",
            "Do not export real lead or invoice payloads for an enquiry."
          ]
        },
        {
          "heading": "Read the step evidence",
          "body": [
            "Zapier's retrieved troubleshooting article describes inspecting the affected step's response and outgoing request. Missing inputs may mean useful logs are absent. Distinguish an absent source field from a stale mapping, invalid format or account permission problem; those require different changes."
          ],
          "items": [
            "Compare field names and expected types, not just display labels.",
            "Resolve expired access with the account holder without sending its credential.",
            "Check the destination after a failure before assuming it contains no record."
          ]
        },
        {
          "heading": "Testing is not assumed to be a dry run",
          "body": [
            "Do not assume a button labelled Test or Replay leaves connected systems unchanged. Agree a disposable destination and review the specific action's effects before invoking it. Lead creation, invoice creation and notification can have consequences even with made-up input. The operator needs written authority for the named test route."
          ],
          "items": [
            "Use a stable synthetic reference to identify created records.",
            "Test the create-versus-update or duplicate rule.",
            "Avoid bulk replay until reconciliation and recovery are agreed."
          ]
        },
        {
          "heading": "Prove the complete business record",
          "body": [
            "Acceptance should show the required values in the intended target record, correct object/account mapping and a controlled missing-field case. Keep redacted before-and-after step evidence and any remaining exclusions. Credentials, client access and purchases are not part of first-contact diagnosis; scope is confirmed before private work."
          ],
          "items": []
        }
      ],
      "platforms": [
        "zapier"
      ],
      "incoming": [
        "buyer-finance-manager-invoice-integration",
        "guide-sheets-xero-line-item-shape",
        "tool-workflow-value",
        "collection-automation-failure-diagnosis-order",
        "guide-zapier-duplicate-records-five-causes",
        "platform-zapier",
        "tool-csv-header-diff",
        "guide-sheets-invoice-value-rendering",
        "guide-invoice-create-unknown-outcome"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/zapier-field-mapping/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/zapier-field-mapping/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fzapier-field-mapping%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fzapier-field-mapping%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-sheets-xero-line-item-shape",
      "type": "guide",
      "title": "One Sheets row becomes one Xero invoice line: check the line-item shape",
      "description": "Distinguish missing spreadsheet headers from multiple products mapped into one invoice line, using a disposable two-product acceptance case.",
      "slug": "sheets-xero-line-item-shape",
      "url": "/guides/sheets-xero-line-item-shape/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "zapier",
        "google-sheets",
        "xero"
      ],
      "provenance": [
        {
          "label": "Canonical SI offer: five mapped fields and one draft invoice, not arbitrary workbook transformation",
          "checked": "2026-10-11",
          "claims": [
            "The existing offer excludes variable many-to-many transformation and live invoice sending. The guide's two-product arithmetic and acceptance checklist are authored proposals derived from this scope, not an executed integration or a provider-documented configuration recipe."
          ],
          "url": "https://backoffice.syntheticindustry.ai/services/zapier-sheets-to-xero-missing-columns/"
        }
      ],
      "related": [
        "zapier-sheets-to-xero-missing-columns",
        "guide-zapier-field-mapping",
        "buyer-finance-manager-invoice-integration",
        "tool-workflow-value"
      ],
      "sections": [
        {
          "heading": "Identify which of three different things is missing",
          "body": [
            "If a column is absent from the trigger sample, inspect the header and sample before editing Xero. If the trigger contains both products but the action exposes only one product's values, inspect the transformation and action mapping. If the action receives both products but the draft invoice has one merged description or the wrong amount, inspect how those values are represented as invoice lines. A successful Zap run does not distinguish these cases."
          ],
          "items": [
            "Record whether your source is one row with several product columns, several rows for one invoice, or already structured line items.",
            "Write down the number of invoices and lines you expect before changing the Zap.",
            "Do not assume a comma-separated description is two invoice lines, or that looping over two products should create two invoices."
          ]
        },
        {
          "heading": "Use a small example that makes a collapsed line obvious",
          "body": [
            "This is an invented test specification, not a tested integration or accounting advice. In an authorised demo context, use product A with quantity 2 and unit amount 10, and product B with quantity 1 and unit amount 7. With tax excluded solely for this synthetic arithmetic example, the expected line amounts are 20 and 7 and the total is 27. The result should be one unsent draft with two separate lines, not one line worth 27 and not two invoices. Your finance owner specifies actual currency, account and tax rules separately."
          ],
          "items": [
            "Use two clearly different descriptions and quantities so misaligned positions are visible.",
            "Compare each description with its own quantity, unit amount and resulting line amount; a correct grand total can conceal swapped lines.",
            "Use the finance owner's authorised demo or disposable draft route. Test buttons can create records; do not connect this example to real customers."
          ]
        },
        {
          "heading": "Keep the line-item attributes aligned",
          "body": [
            "This checklist specifies the business result; it does not establish which current Zapier action or formatter supports your source layout. The authorised implementer must confirm that separately before agreeing the repair. Inspect the proposed output before the invoice step: each description must remain paired with the intended quantity, amount and agreed accounting attributes. A correct total alone cannot show whether two products retained their separate identities."
          ],
          "items": [
            "A blank second description paired with a second amount should be caught rather than silently omitted.",
            "Variable-length product groups, several source rows per invoice or grouping several customers are larger transformation problems; do not squeeze them into a five-field remapping quote.",
            "Do not bulk replay runs to test the transformation. Inspect destination state first and agree how repeated source references are handled."
          ]
        },
        {
          "heading": "What a useful enquiry contains",
          "body": [
            "For the existing Sheets-to-Xero mapping service, send synthetic headers, whether one row already triggers the Zap, expected invoice and line counts, and the point where the second product disappears. State who owns the Xero organisation, can approve a disposable draft test and can decide on the posted price. We confirm whether the bounded field-mapping offer fits; variable line-item transformation needs a separate scope, not a hidden extension of that price."
          ],
          "items": [
            "Do not send customer names, bank details, invoice exports, credentials or access invitations initially.",
            "Keep the expected and actual line counts in the enquiry even if the Zap shows success.",
            "After agreement, acceptance also needs the agreed repeated-input and missing-field checks; no live invoice is sent by this diagnostic guide."
          ]
        }
      ],
      "incoming": [
        "tool-csv-header-diff",
        "example-invoice-tax-rounding-reconciliation",
        "collection-finance-invoice-integrity-checks",
        "guide-sheets-invoice-value-rendering"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/sheets-xero-line-item-shape/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/sheets-xero-line-item-shape/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fsheets-xero-line-item-shape%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fsheets-xero-line-item-shape%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "tool-workflow-value",
      "type": "tool",
      "tool_kind": "workflow-value",
      "title": "Workflow time-value calculator: show the assumptions behind automation payback",
      "description": "Use your own time, volume, cost and running-cost inputs to calculate simple monthly value and payback; not a quote or guaranteed saving.",
      "slug": "workflow-value",
      "url": "/tools/workflow-value/",
      "version": 3,
      "lifecycle": "candidate",
      "updated": "2026-10-10",
      "provenance": [
        {
          "label": "Calculator implementation",
          "url": "https://syntheticindustry.ai/demand.js",
          "checked": "2026-10-10",
          "claims": [
            "Hours equal minutes per case times cases per month divided by 60.",
            "Gross monthly time value equals those hours times hourly cost; net subtracts monthly running cost.",
            "Payback is setup cost divided by positive net monthly value; otherwise there is no positive payback.",
            "Inputs must be finite non-negative numbers no greater than 100000000."
          ]
        }
      ],
      "related": [
        "buyer-finance-manager-invoice-integration",
        "guide-zapier-field-mapping",
        "zapier-sheets-to-xero-missing-columns",
        "zapier-facebook-leads-to-salesforce"
      ],
      "sections": [
        {
          "heading": "Enter savings, not total workflow duration",
          "body": [
            "Use the minutes you expect to remove from each case, not all minutes currently spent if checking and exceptions will remain. Supply cases per month, hourly cost, one-time setup cost and monthly running cost. All monetary values must use the same currency. Your inputs are assumptions; the page has no measured volume, provider benchmark or quoted service price."
          ],
          "items": [
            "Use observed baseline timing when available.",
            "Subtract expected exception-handling and review effort from the minutes saved.",
            "Include provider fees in running cost where applicable."
          ]
        },
        {
          "heading": "The arithmetic",
          "body": [
            "Monthly hours = minutes saved per case × monthly cases ÷ 60. Gross monthly time value = hours × hourly cost. Net monthly value = gross value − monthly running cost. Simple payback = setup cost ÷ positive net monthly value. A zero or negative net value has no positive payback under these assumptions."
          ],
          "items": [
            "Worked arithmetic, not an observed case: 10 minutes × 120 cases ÷ 60 = 20 hours.",
            "At 30 per hour, gross value is 600; running cost of 100 leaves 500.",
            "Setup cost of 1500 divided by 500 gives three months; none of these inputs is our quote or a proven saving."
          ]
        },
        {
          "heading": "Time value is not cash received",
          "body": [
            "Freed time may allow other work without reducing payroll. Taxes, financing, changing volumes, implementation delays and new ongoing exceptions are not modelled. A positive result does not prove that an integration can be safely delivered or that a buyer will pay for it. Test the complete business record and duplicate behaviour separately."
          ],
          "items": [
            "Compare conservative and optimistic input assumptions.",
            "Measure the actual workflow after an authorised delivery before claiming savings."
          ]
        },
        {
          "heading": "Local calculation and its limits",
          "body": [
            "The inspected calculator computes from numeric browser inputs; it does not submit or store these values. Do not enter confidential figures if you do not want them visible on your screen. An enquiry is separate from this calculator, not an automatic payment or quote."
          ],
          "items": []
        }
      ],
      "incoming": [
        "buyer-finance-manager-invoice-integration",
        "guide-zapier-field-mapping",
        "guide-sheets-xero-line-item-shape"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/tools/workflow-value/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/tools/workflow-value/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Ftools%2Fworkflow-value%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Ftools%2Fworkflow-value%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "buyer-operations-manager-inherited-automations",
      "type": "buyer",
      "title": "For an operations manager who inherited automations: find out what runs, who owns it and who is told",
      "description": "Inventory first, then overlaps, alerts and a keep, merge or retire decision for each automation, before anyone rebuilds anything.",
      "url": "/buyers/operations-manager-inherited-automations/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Zapier: how Zapier handles duplicate data",
          "url": "https://help.zapier.com/hc/en-us/articles/8496260269965-How-Zapier-handles-duplicate-data-in-Zap-workflows",
          "checked": "2026-10-11",
          "claims": [
            "De-duplication checks only within one Zap, so two Zaps built on one trigger both fire."
          ]
        },
        {
          "label": "Airtable: getting started with automations",
          "url": "https://support.airtable.com/docs/getting-started-with-airtable-automations",
          "checked": "2026-10-11",
          "claims": [
            "Turning an Airtable automation off does not free its slot against the per-base limit; deleting it does."
          ]
        },
        {
          "label": "Airtable: troubleshooting automations",
          "url": "https://support.airtable.com/articles/6756755850-troubleshooting-airtable-automations",
          "checked": "2026-10-11",
          "claims": [
            "Failure emails go to the last person who turned the automation on, or to workspace owners if that person has left."
          ]
        },
        {
          "label": "Make: overview of error handling",
          "url": "https://help.make.com/Overview-of-error-handling",
          "checked": "2026-10-11",
          "claims": [
            "A Make scenario is deactivated after repeated consecutive errors, and a webhook-triggered scenario is disabled immediately on an error."
          ]
        },
        {
          "label": "Zapier: how Zap triggers work",
          "url": "https://help.zapier.com/hc/en-us/articles/8496244568589-How-Zap-triggers-work",
          "checked": "2026-10-11",
          "claims": [
            "A Zap does not fire for data created in the app before the Zap was turned on."
          ]
        }
      ],
      "related": [
        "auto-failure-alerts-reach-a-named-owner",
        "make-scenario-stops-on-one-bad-record",
        "airtable-automation-fails-or-skips-records",
        "guide-automation-failure-alerts-reach-someone",
        "guide-zapier-duplicate-records-five-causes",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "Start with an inventory, not a rebuild",
          "body": [
            "An automation you did not build is a black box that something depends on. Before changing any of them, list them. For each, record the tool and its name, what starts it, which apps it reads and writes, when it last ran, who owns it and who is told when it fails. Most of this is visible in each platform's own list and run history. Anything you cannot fill in is itself a finding: an automation with no owner and no alert is the first one to look at."
          ],
          "items": [
            "Name, platform and the account it lives in.",
            "Trigger and the apps it touches.",
            "Last run date, and how often it normally runs.",
            "Owner, and who is told on failure.",
            "What breaks for the business if it stops."
          ]
        },
        {
          "heading": "Look for overlaps and loops",
          "body": [
            "Two automations that start from the same event will both fire, because Zapier's de-duplication checks only within one Zap. An automation that writes back to the record type it watches can trigger itself. Two tools doing the same job, for example a Zap and an Airtable automation both emailing on a status change, double every message. These are the first targets for consolidation. Note them without deleting anything yet."
          ],
          "items": []
        },
        {
          "heading": "Check what happens when each one fails",
          "body": [
            "Failure alerts follow settings someone chose once. Airtable sends the failure email to the last person who turned the automation on, or to the workspace owners if that person has left. Make deactivates a scenario after repeated consecutive errors, three by default, and a webhook-triggered one at once. For each automation that matters, find out who would be told and test it with a deliberate failure on a copy. If the answer is one person's inbox, fix that first."
          ],
          "items": []
        },
        {
          "heading": "Decide keep, merge or retire, and switch off rather than delete",
          "body": [
            "For each overlap, someone with authority decides: keep one, merge two into one, or retire one. Retire by switching off, not deleting, and keep it for an agreed observation period so a dependency nobody remembered can show itself and the automation can come back. Switching it back on does not catch up on what happened while it was off: Zapier says a Zap does not fire for data created before it was turned on. Note when you switch each one off and what to compare in the source app afterwards. Note that in Airtable a switched-off automation still occupies a slot against the base's cap, and only deleting it frees the slot, so deleting is a decision to make after the observation period, not before."
          ],
          "items": [
            "Write each decision and who made it.",
            "Switch off, note the time, wait, then delete.",
            "Give every remaining automation an owner and an alert."
          ]
        },
        {
          "heading": "Which paid job fits",
          "body": [
            "If one automation duplicates records, halts on a bad record or fails in Airtable, the matching one-off outcome repairs it on a copy and proves the fix with synthetic data. If nobody is told when automations fail, the alerts outcome covers up to five. If you want someone watching named automations week by week, the standing service reviews run history and prepares corrected copies, with no response-time promise. If the whole set is a tangle, the project inventories it, settles each overlap with you and returns a smaller set with an owner, an alert and a runbook. You switch every change on."
          ],
          "items": []
        }
      ],
      "incoming": [
        "guide-automation-failure-alerts-reach-someone"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/buyers/operations-manager-inherited-automations/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/buyers/operations-manager-inherited-automations/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fbuyers%2Foperations-manager-inherited-automations%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fbuyers%2Foperations-manager-inherited-automations%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "buyer-marketing-manager-web-lead-flow",
      "type": "buyer",
      "title": "For a marketing manager: make every web lead arrive once, reach an owner and be noticed if the form breaks",
      "description": "Write the lead's path, then check matching, ownership, the Salesforce sync and an alert, using invented leads rather than real ones.",
      "url": "/buyers/marketing-manager-web-lead-flow/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "HubSpot: deduplicate records",
          "url": "https://knowledge.hubspot.com/records/deduplication-of-records",
          "checked": "2026-10-11",
          "claims": [
            "HubSpot de-duplicates contacts by email and companies by domain, and companies created through the API are not de-duplicated by domain."
          ]
        },
        {
          "label": "HubSpot: create and edit forms",
          "url": "https://knowledge.hubspot.com/forms/create-and-edit-forms",
          "checked": "2026-10-11",
          "claims": [
            "If a contact has no owner, no owner notification will be sent."
          ]
        },
        {
          "label": "HubSpot: assign and rotate record owners using workflows",
          "url": "https://knowledge.hubspot.com/workflows/assign-and-rotate-record-owners-using-workflows",
          "checked": "2026-10-11",
          "claims": [
            "Rotation counts reset when owners are added or removed, and the No one option leaves records unassigned."
          ]
        },
        {
          "label": "HubSpot: map properties to Salesforce fields",
          "url": "https://knowledge.hubspot.com/salesforce/map-hubspot-properties-to-salesforce-fields",
          "checked": "2026-10-11",
          "claims": [
            "Each mapped field has a sync rule deciding which system wins."
          ]
        }
      ],
      "related": [
        "crm-web-lead-pipeline-repair",
        "crm-duplicate-contacts-from-web-forms",
        "lead-routing-new-leads-unassigned-or-wrong-owner",
        "hubspot-salesforce-synced-field-reverts",
        "guide-hubspot-form-duplicates-contacts-companies",
        "guide-hubspot-lead-owner-rotation-fallbacks",
        "collection-web-lead-to-owner-checks"
      ],
      "sections": [
        {
          "heading": "Write the path of one lead",
          "body": [
            "You pay for web leads, so write down what should happen to one of them. Which form or tool captures it? Where does it land in HubSpot, and is it matched to an existing contact and company? Who owns it, by what rule, and who is told? Does anything sync to Salesforce, and which system should win? A paragraph for each stage is enough. Gaps in the paragraph are where leads are lost."
          ],
          "items": []
        },
        {
          "heading": "Check matching and ownership with invented leads",
          "body": [
            "Test with leads you made up, never real people. Submit the same synthetic email twice, two people from one domain, and two different people from one browser, and count contacts and companies. Then check ownership: one synthetic lead per routing rule, one for each edge, and one that matches nothing. HubSpot sends no owner notification for a contact with no owner, so an unowned lead can sit unnoticed unless a view or alert watches for it. Make sure test leads cannot email or notify real people."
          ],
          "items": [
            "Same email twice: one contact, two submissions.",
            "Two people at one domain: two contacts, one company.",
            "A lead that matches no rule: the named fallback owner, with an alert."
          ]
        },
        {
          "heading": "Watch the sync and the team",
          "body": [
            "If HubSpot syncs with Salesforce, each mapped field has a sync rule, and a value that keeps reverting is often the rule doing its job. Rotation counts reset when owners are added or removed, so every change to the sales team is also a change to routing, and should trigger a retest. Make one person responsible for the lead path, and give them a weekly view of unowned leads, new duplicates and sync errors."
          ],
          "items": []
        },
        {
          "heading": "Which paid job fits",
          "body": [
            "If one form path creates duplicates, the outcome for one path changes it and proves it with six synthetic submissions. If leads have no owner or the wrong one, the routing outcome rebuilds one workflow from your approved rule table and tests it with synthetic leads for every rule, edge case and the fallback, and a pool change. If one field changes back, the sync outcome covers one mapped field pair. If several are broken at once, the project tests up to three paths from form to owner. If you want it kept true, the standing service checks weekly and repairs on a copy or with synthetic records. No outcome promises how quickly a lead is contacted."
          ],
          "items": []
        }
      ],
      "incoming": [
        "collection-web-lead-to-owner-checks",
        "guide-hubspot-form-duplicates-contacts-companies",
        "guide-hubspot-lead-owner-rotation-fallbacks"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/buyers/marketing-manager-web-lead-flow/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/buyers/marketing-manager-web-lead-flow/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fbuyers%2Fmarketing-manager-web-lead-flow%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fbuyers%2Fmarketing-manager-web-lead-flow%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "collection-automation-failure-diagnosis-order",
      "type": "category",
      "title": "Automation failure diagnosis in order: from did it run to who was told",
      "description": "A seven-step order for finding out why an automation misbehaved, stopping at the first step that explains it, with a guide for each step.",
      "url": "/collections/automation-failure-diagnosis-order/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Zapier: how Zap triggers work",
          "url": "https://help.zapier.com/hc/en-us/articles/8496244568589-How-Zap-triggers-work",
          "checked": "2026-10-11",
          "claims": [
            "Polling triggers fetch data on a schedule, instant triggers receive it when it happens, and test samples may not match live data."
          ]
        },
        {
          "label": "Make: scenario settings",
          "url": "https://help.make.com/scenario-settings",
          "checked": "2026-10-11",
          "claims": [
            "Make's settings control whether failed runs are stored, how many errors in a row deactivate a scenario, and whether data is kept confidential."
          ]
        },
        {
          "label": "n8n: Webhook node",
          "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/",
          "checked": "2026-10-11",
          "claims": [
            "A test URL and a production URL are registered at different times, and production runs appear under Executions."
          ]
        },
        {
          "label": "Airtable: troubleshooting automations",
          "url": "https://support.airtable.com/articles/6756755850-troubleshooting-airtable-automations",
          "checked": "2026-10-11",
          "claims": [
            "Airtable's automation history shows run statuses and the error for a failed run."
          ]
        }
      ],
      "related": [
        "guide-zapier-errored-halted-and-replay",
        "guide-n8n-webhook-test-and-production-urls",
        "guide-zapier-field-mapping",
        "guide-zapier-duplicate-records-five-causes",
        "guide-webhook-timeouts-retries-missed-events",
        "guide-api-polling-checkpoint-overlap-keys",
        "guide-make-error-handlers-and-incomplete-executions",
        "guide-airtable-automation-failed-runs-and-limits",
        "guide-automation-failure-alerts-reach-someone",
        "auto-failure-alerts-reach-a-named-owner"
      ],
      "sections": [
        {
          "heading": "How to use this order",
          "body": [
            "Work through the steps in order and stop at the first one that explains what you see. Each step names the question, what to look at and the guide that goes deeper. Change nothing on a live automation while diagnosing, and do not replay runs to see what happens: replay can repeat steps that already succeeded. This is an authored process map, not a vendor procedure, and it does not claim to cover every failure."
          ],
          "items": []
        },
        {
          "heading": "Steps one to three: did it run, what arrived, what was sent",
          "body": [
            "Step 1, did it run? Look at the run history or executions list for the time in question. No run means a trigger problem; a failed run means read its error. For n8n webhooks, check the production executions, not the editor. Step 2, what did it receive? Compare the trigger data with the source record, remembering that test samples may differ from live data, and that polling and instant triggers behave differently. Step 3, what did it send? Compare the data the action sent with what the destination needed, using the field-mapping guide for dropped or reshaped values."
          ],
          "items": [
            "Step 1: run history, and the n8n production-executions guide.",
            "Step 2: the trigger's data, polling or instant, test versus live.",
            "Step 3: the action's inputs and the destination's required fields."
          ]
        },
        {
          "heading": "Steps four to six: what the destination holds, repeated or lost, who was told",
          "body": [
            "Step 4, what does the destination hold? Count records for one source event: zero, one or several. Several points to the duplicate guide. Step 5, was something repeated or lost? Check for retries, replays and overlapping windows, and read the webhook and polling guides for senders that retry and jobs that ask since the last run. Step 6, who was told? For each failure, find who received an alert, how late and whether it would have been acted on; the failure-alerts guide and the platform guides for Zapier, Make and Airtable show the documented defaults."
          ],
          "items": [
            "Step 4: counts per source event, then the duplicate guide.",
            "Step 5: retries, replays, overlaps, handled keys.",
            "Step 6: alert recipients, delays and suppressions."
          ]
        },
        {
          "heading": "Step seven: decide what to buy, if anything",
          "body": [
            "If the cause is one Zap, one scenario or one automation, the matching one-off outcome repairs it on a copy and proves it with synthetic data. If nobody is told when things fail, the alerts outcome hooks up to five automations to a shared place and two named people. If you would rather someone watched named automations weekly, the standing service does that and prepares corrected copies for you to switch on, with no response-time promise. Many problems need none of these: the guides above are written to be useful on their own."
          ],
          "items": []
        }
      ],
      "incoming": [
        "buyer-operations-manager-inherited-automations",
        "guide-airtable-automation-failed-runs-and-limits",
        "guide-airtable-split-table-linked-records",
        "guide-api-polling-checkpoint-overlap-keys",
        "guide-automation-failure-alerts-reach-someone",
        "guide-webhook-signature-raw-body",
        "guide-webhook-timeouts-retries-missed-events",
        "guide-zapier-duplicate-records-five-causes",
        "guide-zapier-errored-halted-and-replay",
        "guide-make-error-handlers-and-incomplete-executions",
        "guide-scheduled-report-time-zone-and-period",
        "guide-n8n-webhook-test-and-production-urls"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/collections/automation-failure-diagnosis-order/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/collections/automation-failure-diagnosis-order/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fcollections%2Fautomation-failure-diagnosis-order%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fcollections%2Fautomation-failure-diagnosis-order%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "collection-web-lead-to-owner-checks",
      "type": "category",
      "title": "Web lead to owner: six checks in order, with synthetic leads",
      "description": "An ordered review of capture, matching, ownership, sync, alert and weekly check for leads that arrive through web forms into HubSpot.",
      "url": "/collections/web-lead-to-owner-checks/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "HubSpot: deduplicate records",
          "url": "https://knowledge.hubspot.com/records/deduplication-of-records",
          "checked": "2026-10-11",
          "claims": [
            "Contacts are matched by email and companies by domain, and API-created companies are not de-duplicated by domain."
          ]
        },
        {
          "label": "HubSpot: assign and rotate record owners using workflows",
          "url": "https://knowledge.hubspot.com/workflows/assign-and-rotate-record-owners-using-workflows",
          "checked": "2026-10-11",
          "claims": [
            "Rotation resets when owners change, deactivated users are excluded and records can be left unassigned."
          ]
        },
        {
          "label": "HubSpot: manage Salesforce integration sync errors",
          "url": "https://knowledge.hubspot.com/salesforce/resolve-salesforce-integration-sync-errors",
          "checked": "2026-10-11",
          "claims": [
            "Sync errors are listed under Sync Health, with documented fixes by type."
          ]
        }
      ],
      "related": [
        "guide-hubspot-form-duplicates-contacts-companies",
        "guide-hubspot-lead-owner-rotation-fallbacks",
        "guide-hubspot-salesforce-sync-rules-and-errors",
        "guide-automation-failure-alerts-reach-someone",
        "crm-web-lead-pipeline-repair",
        "crm-duplicate-contacts-from-web-forms",
        "lead-routing-new-leads-unassigned-or-wrong-owner",
        "hubspot-salesforce-synced-field-reverts",
        "buyer-marketing-manager-web-lead-flow"
      ],
      "sections": [
        {
          "heading": "How to use this order",
          "body": [
            "Take one form path and check six things in order, stopping at the first that fails. Use leads you invented, and make sure that no test lead can email, task or notify a real person. This is an authored review sequence, not a HubSpot procedure, and it assumes you can see the form settings, the workflows and, if used, the Salesforce integration. Record expected and actual results side by side."
          ],
          "items": []
        },
        {
          "heading": "Checks one to three: capture, matching, ownership",
          "body": [
            "Check 1, capture: does the lead arrive at all? Submit one synthetic lead and find the contact. Check 2, matching: the same email twice should give one contact; two people at one domain should give two contacts and one company; two people on one browser should stay two contacts. See the duplicates guide for the four routes and the cookie risk. Check 3, ownership: each synthetic lead should reach the owner your written rule table names, a lead matching nothing should reach the named fallback with an alert, and a pool change should be tested again."
          ],
          "items": [
            "1: one synthetic lead creates one contact.",
            "2: counts of contacts and companies match the expectation.",
            "3: owner equals the rule table, including the fallback."
          ]
        },
        {
          "heading": "Checks four to six: sync, alert, weekly check",
          "body": [
            "Check 4, sync: for each mapped field, edit on the side that should win, wait the agreed window, and confirm the value holds and Sync Health shows no new error; read the sync-rules guide first. Check 5, alert: cause a deliberate failure on a copy of the path and confirm that a shared place and two named people are told. Check 6, weekly check: put a saved view of unowned new leads, new duplicates and sync errors in front of a named person on a schedule, because a lead without an owner produces no owner notification."
          ],
          "items": [
            "4: the sync rule matches who edits the field, and the value holds.",
            "5: a deliberate failure reaches two named people.",
            "6: a weekly view of unowned leads, duplicates and sync errors."
          ]
        },
        {
          "heading": "What to buy, if anything",
          "body": [
            "One broken stage: the matching one-off outcome, whether duplicates on one form path, routing for one workflow, or one mapped field pair. Several broken at once: the project tests up to three paths from form to owner. Keeping it true: the standing weekly check, with repairs on a copy or with synthetic records. None promises how fast a lead is contacted, and none changes real records without your approval."
          ],
          "items": []
        }
      ],
      "incoming": [
        "buyer-marketing-manager-web-lead-flow",
        "guide-hubspot-form-duplicates-contacts-companies",
        "guide-hubspot-lead-owner-rotation-fallbacks",
        "guide-hubspot-salesforce-sync-rules-and-errors"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/collections/web-lead-to-owner-checks/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/collections/web-lead-to-owner-checks/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fcollections%2Fweb-lead-to-owner-checks%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fcollections%2Fweb-lead-to-owner-checks%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-webhook-signature-test-vectors",
      "type": "example",
      "title": "Synthetic webhook signature test vectors: six deliveries, one accepted",
      "description": "Invented secret, invented body and real HMAC-SHA256 digests show exactly which small changes make a signature check fail, with a verifier you can run.",
      "url": "/examples/webhook-signature-test-vectors/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "GitHub: validating webhook deliveries",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries",
          "checked": "2026-10-11",
          "claims": [
            "GitHub's published test uses the secret It's a Secret to Everybody and the payload Hello, World!, giving a header beginning sha256=757107ea and ending 3e17, which the verifier below reproduces."
          ]
        },
        {
          "label": "Node.js: crypto module",
          "url": "https://nodejs.org/api/crypto.html",
          "checked": "2026-10-11",
          "claims": [
            "createHmac(algorithm, key) with update() and digest('hex') computes an HMAC digest, as in the page's own example; digest('base64') gives the base64 form, and timingSafeEqual throws if its inputs differ in byte length."
          ]
        },
        {
          "label": "Shopify: HTTPS webhook subscriptions",
          "url": "https://shopify.dev/docs/apps/build/webhooks/subscribe/https",
          "checked": "2026-10-11",
          "claims": [
            "Shopify's X-Shopify-Hmac-SHA256 header is a base64-encoded HMAC signature made with the app's client secret and the raw request body."
          ]
        },
        {
          "label": "Stripe: receive events in your webhook endpoint",
          "url": "https://docs.stripe.com/webhooks",
          "checked": "2026-10-11",
          "claims": [
            "Stripe signs the timestamp, a full stop and the request body with HMAC-SHA256, and its libraries have a default tolerance of 5 minutes between the timestamp and the current time."
          ]
        }
      ],
      "related": [
        "webhook-receiver-verified-signature-duplicate-safe",
        "guide-webhook-signature-raw-body",
        "example-webhook-delivery-ledger-repeat-safe",
        "guide-webhook-timeouts-retries-missed-events"
      ],
      "sections": [
        {
          "heading": "What this example is",
          "body": [
            "Everything here is synthetic. The two secrets are invented for this page and belong to no sender, the event is made up, and no real service, receiver or customer is involved. The digests are real HMAC-SHA256 values. Every input is printed below, and the script on this page regenerates every value in the table when run with Node.js; it was run on Node.js v24 on 11 October 2026 and printed exactly what is shown. As a cross-check, the same function accepts the header GitHub publishes for its own test input. The example is a specification for testing a receiver, not evidence that any receiver passes it."
          ],
          "items": []
        },
        {
          "heading": "The fixtures and the six cases",
          "body": [
            "The sender signs the exact 87 bytes of body A with the first secret. The receiver recomputes the digest over the bytes it received and compares it with the header. Cases B to F each change one thing, and only the unchanged delivery should be accepted. The bodies for B, C and D and the second secret used for E are printed here so each row can be reproduced."
          ],
          "items": [],
          "code": "Secret (invented)            : demo-secret-not-a-real-key\nSecond secret (invented)     : another-demo-secret   (used only for case E)\n\nBody A, exactly as sent (87 bytes):\n{\"event\":\"task.created\",\"delivery\":\"dlv_demo_001\",\"title\":\"Call back Ada\",\"priority\":2}\nHeader the sender attaches, sha256= plus HMAC-SHA256 of body A with the first secret:\nsha256=967c95481a9684fc158c35e7474bb0cb79845611bfea3f3d6ec03bb7372c0320\n\nBody B (87 bytes): A with \"priority\":2 changed to \"priority\":3\nBody C (96 bytes): A re-saved with a space after each colon and comma and inside the braces:\n{ \"event\": \"task.created\", \"delivery\": \"dlv_demo_001\", \"title\": \"Call back Ada\", \"priority\": 2 }\nBody D (88 bytes): A followed by one newline character\nHeader E: sha256= plus HMAC-SHA256 of body A with the second secret\n\ncase | what arrives                           | receiver's digest (first 16) | header presented  | result\nA    | body A, right secret (87 bytes)        | 967c95481a9684fc             | 967c95481a9684fc  | accept\nB    | body B, priority changed (87)          | 44a0a4f3241d3529             | 967c95481a9684fc  | reject\nC    | body C, same data with spaces (96)     | 158efa5845904a61             | 967c95481a9684fc  | reject\nD    | body D, trailing newline added (88)    | 4afcfad89f0ace96             | 967c95481a9684fc  | reject\nE    | body A, header made with second secret | 967c95481a9684fc             | 4f5b159f5fe97500  | reject\nF    | body A, header missing                 | not computed                 | none              | reject"
        },
        {
          "heading": "Why C and D matter most",
          "body": [
            "Cases B, E and F are what people expect a signature to catch: an altered value, a wrong secret and a missing header. Cases C and D are the ones that break working receivers. In C the data is identical, but a framework that parsed the JSON and wrote it out again has changed the bytes, so the recomputed digest differs from the one the sender made. In D a single trailing newline does the same. The lesson is to capture the raw request body before any parser touches it, and hash that."
          ],
          "items": [
            "Case C: parsed and re-serialised JSON no longer matches the signed bytes.",
            "Case D: one extra byte is enough to change the digest completely.",
            "A receiver that accepts C or D is checking something other than the signature, and one that rejects A has probably modified the body."
          ]
        },
        {
          "heading": "A verifier you can run",
          "body": [
            "This Node.js script applies the rules from the guide: it checks the prefix, compares in constant time, and checks the lengths first because the comparison function throws when its inputs differ in length. Save it as a .cjs file and run it with node. It rebuilds the six cases from the inputs above and prints one line per case: the name, the body length in bytes, the first 16 characters of the receiver's digest, the first 16 of the header presented, and the verdict. Only A should be accepted. The next two lines show the same body signed in two other shapes, described after the output, and the last line checks the verifier against the test input GitHub publishes."
          ],
          "items": [],
          "code": "const crypto = require('node:crypto');\nconst hmac = (secret, data, enc = 'hex') => crypto.createHmac('sha256', secret).update(data).digest(enc);\n\nfunction verify(secret, rawBody, header) {\n  if (!header || !header.startsWith('sha256=')) return false;\n  const expected = Buffer.from('sha256=' + hmac(secret, rawBody));\n  const received = Buffer.from(header);\n  return expected.length === received.length && crypto.timingSafeEqual(expected, received);\n}\n\nconst secret = 'demo-secret-not-a-real-key';\nconst other = 'another-demo-secret';\nconst A = '{\"event\":\"task.created\",\"delivery\":\"dlv_demo_001\",\"title\":\"Call back Ada\",\"priority\":2}';\nconst header = 'sha256=' + hmac(secret, A);\nconst cases = {\n  A: [A, header],\n  B: [A.replace('\"priority\":2', '\"priority\":3'), header],\n  C: ['{ \"event\": \"task.created\", \"delivery\": \"dlv_demo_001\", \"title\": \"Call back Ada\", \"priority\": 2 }', header],\n  D: [A + '\\n', header],\n  E: [A, 'sha256=' + hmac(other, A)],\n  F: [A, undefined],\n};\nfor (const [name, [body, hdr]] of Object.entries(cases)) {\n  const mine = hdr ? hmac(secret, body).slice(0, 16) : 'not computed';\n  const theirs = hdr ? hdr.slice(7, 23) : 'none';\n  console.log(name, Buffer.byteLength(body), mine, theirs, verify(secret, body, hdr) ? 'accept' : 'reject');\n}\nconsole.log('base64 of A', hmac(secret, A, 'base64'));\nconsole.log('timestamp.body', hmac(secret, '1760000000.' + A));\nconst ghHeader = 'sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17';\nconsole.log('GitHub published input', verify(\"It's a Secret to Everybody\", 'Hello, World!', ghHeader) ? 'accept' : 'reject');\n\n// Printed on Node.js v24, 11 October 2026:\n// A 87 967c95481a9684fc 967c95481a9684fc accept\n// B 87 44a0a4f3241d3529 967c95481a9684fc reject\n// C 96 158efa5845904a61 967c95481a9684fc reject\n// D 88 4afcfad89f0ace96 967c95481a9684fc reject\n// E 87 967c95481a9684fc 4f5b159f5fe97500 reject\n// F 87 not computed none reject\n// base64 of A lnyVSBqWhPwVjDXnR0uwy3mEVhG/6j89bsA7tzcsAyA=\n// timestamp.body 2fc2a022ded71da36e1def26da4ab2421efde6d67ed71596fcf16b3a5253a35d\n// GitHub published input accept"
        },
        {
          "heading": "Other senders sign other things",
          "body": [
            "This verifier handles only the GitHub-style header, sha256= followed by a hex digest of the body. The base64 and timestamp.body output lines show why that is not universal. The base64 line is the same digest as case A written in base64, which is the encoding Shopify documents for its header. The timestamp.body line signs the characters 1760000000, a full stop and then the body, which is the shape Stripe documents; it is a different digest from case A although the secret and the body are the same. Slack signs v0, a timestamp and the body joined with colons. A receiver for any of them has to rebuild the exact signed bytes and use the exact encoding the sender documents. Where a timestamp is signed, it should also reject deliveries outside a tolerance, which the guide on signatures explains."
          ],
          "items": []
        },
        {
          "heading": "What was and was not exercised",
          "body": [
            "Exercised: the digests above, the verdicts for the six cases and for GitHub's published input, and the base64 and timestamp lines, by running the script in Node.js v24 on 11 October 2026. Not exercised: any real sender, any web framework's raw-body handling, a proxy in front of a server, key rotation, replays or concurrent deliveries. Your own receiver, and your own sender's documented scheme, may differ in header name, prefix, encoding and signed bytes.",
            "If you want a receiver built and tested against cases like these, the paid outcome covers one signed event type, with tests for valid, altered, wrong-secret, missing, repeated, simultaneous and replayed deliveries and for a stop between storing and acting. Send the sender's documentation link, never a secret."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-webhook-delivery-ledger-repeat-safe",
        "guide-webhook-signature-raw-body"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/webhook-signature-test-vectors/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/webhook-signature-test-vectors/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Fwebhook-signature-test-vectors%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Fwebhook-signature-test-vectors%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-webhook-delivery-ledger-repeat-safe",
      "type": "example",
      "title": "Synthetic delivery ledger: the same event arrives three times and the action runs once",
      "description": "A made-up sender delivers six requests, including retries, a tampered copy and an unsigned one; the ledger shows which are accepted and how many actions run.",
      "url": "/examples/webhook-delivery-ledger-repeat-safe/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Shopify: HTTPS webhook subscriptions",
          "url": "https://shopify.dev/docs/apps/build/webhooks/subscribe/https",
          "checked": "2026-10-11",
          "claims": [
            "Shopify retries failed deliveries 8 times over 4 hours and recommends de-duplicating by the X-Shopify-Webhook-Id header or processing idempotently."
          ]
        },
        {
          "label": "GitHub: best practices for using webhooks",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/best-practices-for-using-webhooks",
          "checked": "2026-10-11",
          "claims": [
            "GitHub expects a 2XX response within 10 seconds and suggests queuing payloads so a server can acknowledge before processing."
          ]
        },
        {
          "label": "GitHub: validating webhook deliveries",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries",
          "checked": "2026-10-11",
          "claims": [
            "The signature should be verified before any further processing."
          ]
        }
      ],
      "related": [
        "webhook-receiver-verified-signature-duplicate-safe",
        "guide-webhook-timeouts-retries-missed-events",
        "guide-webhook-signature-raw-body",
        "example-webhook-signature-test-vectors"
      ],
      "sections": [
        {
          "heading": "The scenario",
          "body": [
            "A made-up sender signs each delivery with a shared secret and gives every delivery an identifier. Our made-up receiver follows one rule order: verify the signature first, then check whether the identifier has been handled, then record it, then act, then answer. Six requests arrive. This is a simulation written for this page: it uses an in-memory set and a single process, and it was run on 11 October 2026 to produce the table below."
          ],
          "items": []
        },
        {
          "heading": "The ledger",
          "body": [
            "Reading down the table: the first delivery acts once. A retry after a timeout and a redelivery the next day carry the same identifier, so they return success without acting. A new event with identical content but a new identifier is a different event and acts. A copy of an event with its title altered fails the signature and does nothing. An unsigned request does nothing."
          ],
          "items": [],
          "code": "#  delivery                              signature  id handled before  response  actions so far\n1  first attempt for dlv_demo_101         ok         no                 200       1\n2  sender retries 101 after a timeout     ok         yes                200       1\n3  redelivery of 101 the next day         ok         yes                200       1\n4  new event dlv_demo_102, same title     ok         no                 200       2\n5  101 with the title altered             bad        -                  401       2\n6  dlv_demo_103 with no signature         bad        -                  401       2\n\nFinal: 2 actions, handled ids dlv_demo_101 and dlv_demo_102"
        },
        {
          "heading": "What the simulation leaves out",
          "body": [
            "A real receiver has problems this one does not. Two copies of the same delivery can arrive at the same moment, so recording the identifier has to be one atomic step, not a check followed by a separate write. A crash between recording the identifier and performing the action loses the action unless the two are recorded together in one durable step, or the action is queued from the same record. A store that forgets identifiers after a time lets a late redelivery act again. An in-memory set is lost when the process restarts. The identifier here is treated as part of what the signature covers; if a real sender puts it only in an unsigned header, a captured request can be replayed with the header changed, and the signature guide explains how to key on the signed bytes instead.",
            "Do not assume an ordering guarantee between events unless the sender's documentation states one. The example shows the rule order, not a production design."
          ],
          "items": [
            "Record the identifier in one atomic step.",
            "Queue the work from the same durable record.",
            "Decide how long handled identifiers are kept, and what happens to an older repeat."
          ]
        },
        {
          "heading": "Using it as an acceptance check",
          "body": [
            "Rows 1 to 6 translate directly into tests: act once; repeat on timeout returns success without acting; a redelivery acts once; a different identifier acts; an altered body and a missing signature do not act. Add two more for a real receiver: the same identifier delivered twice at the same instant, and the process killed between recording and acting. The paid outcome for one signed event type is accepted by tests of exactly this kind against synthetic deliveries. It does not claim that no live event is ever lost."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-webhook-signature-test-vectors",
        "example-polling-checkpoint-strategies",
        "guide-webhook-timeouts-retries-missed-events",
        "guide-zapier-duplicate-records-five-causes",
        "example-slack-signature-and-replay-window-cases",
        "guide-slack-events-signature-timestamp-and-three-second-ack"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/webhook-delivery-ledger-repeat-safe/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/webhook-delivery-ledger-repeat-safe/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Fwebhook-delivery-ledger-repeat-safe%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Fwebhook-delivery-ledger-repeat-safe%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-polling-checkpoint-strategies",
      "type": "example",
      "title": "Synthetic polling trace: five strategies over the same five records",
      "description": "One invented record is slow to appear and one run crashes; five ways of remembering your place give five different results, from a lost record to nine actions for five records.",
      "url": "/examples/polling-checkpoint-strategies/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "HubSpot: CRM search API",
          "url": "https://developers.hubspot.com/docs/api/crm/search",
          "checked": "2026-10-11",
          "claims": [
            "New or changed records may take a few moments to appear in search results, which is the kind of delay modelled here by the visible column."
          ]
        },
        {
          "label": "Zapier: polling trigger de-duplication",
          "url": "https://docs.zapier.com/platform/build/deduplication",
          "checked": "2026-10-11",
          "claims": [
            "Zapier stores the ids it has seen for a polling trigger so that the same item does not run twice."
          ]
        },
        {
          "label": "IETF RFC 9110, section 9.2.2",
          "url": "https://www.rfc-editor.org/rfc/rfc9110.html",
          "checked": "2026-10-11",
          "claims": [
            "A client should not automatically retry a non-idempotent request unless it knows it is effectively idempotent or was not applied."
          ]
        }
      ],
      "related": [
        "workflow-api-polling-no-gaps-no-repeats",
        "guide-api-polling-checkpoint-overlap-keys",
        "guide-webhook-timeouts-retries-missed-events",
        "example-webhook-delivery-ledger-repeat-safe"
      ],
      "sections": [
        {
          "heading": "The fixture",
          "body": [
            "Five invented records carry an updated time, in minutes, and a visible time, the minute from which a list call can first see them. Record R3 is updated at minute 9 but is visible only from minute 12, which models a delay before a new record appears. The job runs at minutes 10, 20 and 30 and asks for records updated after its starting point. In the two crash cases the job is restarted at once, still in the minute-20 run, and the minute-30 run follows. Everything here is synthetic, and this simulation was run on 11 October 2026; it is not a trace of any real API."
          ],
          "items": [],
          "code": "id  updated  visible-from\nR1  3        3\nR2  8        8\nR3  9        12      (slow to appear)\nR4  14       14\nR5  18       18\nRuns at minutes 10, 20, 30. Where used, the overlap is 5 minutes."
        },
        {
          "heading": "Five strategies, same records",
          "body": [
            "Strategy A asks for everything since the last run and moves its checkpoint to the run time. It never sees R3, because at minute 10 R3 was not yet visible and at minute 20 it asks only for records updated after 10. Strategy B adds a five-minute overlap and catches R3, but now reads R2 and R5 twice and acts on them twice. Strategy C adds a record of handled keys, so the overlap causes no repeats. D and E repeat C and B with a crash after R3 at the minute-20 run, before the checkpoint is saved."
          ],
          "items": [],
          "code": "A  since last run, no keys\n   10: found R1,R2        processed R1,R2        checkpoint 10\n   20: found R4,R5        processed R4,R5        checkpoint 20\n   30: found -            processed -            checkpoint 30\n   missed R3 | repeated none | 4 actions\n\nB  overlap 5, no keys\n   10: found R1,R2        processed R1,R2\n   20: found R2,R3,R4,R5  processed R2,R3,R4,R5\n   30: found R5           processed R5\n   missed none | repeated R2 x2, R5 x2 | 7 actions\n\nC  overlap 5, handled keys\n   10: found R1,R2        processed R1,R2\n   20: found R2,R3,R4,R5  processed R3,R4,R5\n   30: found R5           processed -\n   missed none | repeated none | 5 actions\n\nD  as C, crash after R3 at minute 20\n   missed none | repeated none | 5 actions\nE  as B (no keys), same crash\n   missed none | repeated R2 x3, R3 x2, R5 x2 | 9 actions"
        },
        {
          "heading": "What each result teaches",
          "body": [
            "A shows that a single timestamp can silently lose a record, with no error anywhere. B shows that fixing the loss with an overlap alone trades it for repeats. C shows the pairing that works: overlap to catch the late record, keys to stop the repeats. D shows that the pairing also survives a crash, because the checkpoint stayed behind and the keys prevented repeats on the rerun. E shows the cost of skipping the keys: nine actions for five records. None of this depends on the made-up numbers; it depends on the structure."
          ],
          "items": [
            "Overlap protects against missing records.",
            "Handled keys protect against repeats.",
            "Advancing the checkpoint only after the work is recorded protects against loss on a crash."
          ]
        },
        {
          "heading": "What the example does not show",
          "body": [
            "It uses a single process and an in-memory set of keys. A real job needs the keys and the checkpoint in durable storage, keys the API guarantees are unique, complete paging, an overlap longer than the real delay, and handling for changed records, not only new ones. It does not model rate limits, result caps or pages. The paid outcome builds a job with these pieces against a synthetic stand-in for one documented API, and accepts it on tests like these. It does not promise that the live API behaves as documented."
          ],
          "items": []
        }
      ],
      "incoming": [
        "guide-api-polling-checkpoint-overlap-keys"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/polling-checkpoint-strategies/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/polling-checkpoint-strategies/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Fpolling-checkpoint-strategies%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Fpolling-checkpoint-strategies%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-airtable-automation-failed-runs-and-limits",
      "type": "guide",
      "title": "An Airtable automation failed or did not run: read the history, then check five causes",
      "description": "Find the failing step in the run history, understand why records already matching never trigger, and check inputs, scripts, limits and who gets told.",
      "url": "/guides/airtable-automation-failed-runs-and-limits/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Airtable: troubleshooting automations",
          "url": "https://support.airtable.com/articles/6756755850-troubleshooting-airtable-automations",
          "checked": "2026-10-11",
          "claims": [
            "Automation history shows runs with statuses Ran successfully, Pending, Run cancelled and Failed run, and a rerun uses the configuration the run first started with.",
            "Records already meeting the trigger conditions when an automation is switched on do not trigger it until they satisfy the conditions again; a record entering a view fires each time it re-enters, and simultaneous triggers have no guaranteed order.",
            "Listed failure causes include an empty email recipient input, typed maths in a number field, an attachment still processing, a formula primary field in a linked table and a script network timeout of 30 seconds, with remedies such as requesting only needed fields and batching updates.",
            "The failure email goes to the person who last turned the automation on, or to workspace owners if that person has left, and an authentication change can switch an automation off."
          ]
        },
        {
          "label": "Airtable: getting started with automations",
          "url": "https://support.airtable.com/docs/getting-started-with-airtable-automations",
          "checked": "2026-10-11",
          "claims": [
            "Per-workspace monthly run limits stated are 100 on Free, 25,000 on Team, 100,000 on Business and 500,000 on Enterprise Scale, reset on the first of each month, and failed runs count.",
            "Per-base automation limits stated are 75, 100, 150 and 200 by the same plans, and turning an automation off does not free its slot; deleting it does."
          ]
        }
      ],
      "related": [
        "airtable-automation-fails-or-skips-records",
        "platform-airtable",
        "auto-failure-alerts-reach-a-named-owner",
        "guide-airtable-split-table-linked-records",
        "guide-automation-failure-alerts-reach-someone",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "Read the history before you change the automation",
          "body": [
            "Open the base, choose Automations, pick the automation and open its history. Airtable's troubleshooting article shows four statuses: ran successfully, pending, run cancelled and failed run. Open a failed run and expand its error message, and note which action failed and what the error says. One trap is worth knowing at once: a rerun uses the configuration the run first started with, not your latest fix, so rerunning is not a test of a repair. Trigger a fresh run on a test record instead."
          ],
          "items": [
            "Record the action that failed and the exact error text.",
            "Note whether the run was cancelled because someone switched the automation off while it was pending.",
            "Retest the trigger and each action separately on a record that meets all the conditions."
          ]
        },
        {
          "heading": "Why some records never trigger",
          "body": [
            "Triggers fire on a change, not on a state. Airtable says that records already meeting the conditions when you switch an automation on will not trigger it unless they satisfy the conditions again. A trigger for a record entering a view fires each time a record re-enters that view, and restoring a deleted record can fire triggers too. Several automations triggered at once have no guaranteed order. If a batch of old records was expected to run and none did, this is the first thing to check. The fix is usually to cause the change deliberately, for example by moving the field away from and back to the matching value on a test record, or to run the old records through a separate one-off step."
          ],
          "items": []
        },
        {
          "heading": "Inputs that resolve to nothing",
          "body": [
            "Most failed runs trace to an action input with nothing in it. The article lists an email recipient that resolves to nothing, such as an empty linked record or a computed field that has not calculated yet; a number field that receives typed maths; an attachment that was not finished processing when the automation fired; permission limits on a field or table; and a formula primary field in a linked table. Adding an is-not-empty condition on the fields an action depends on stops some of these runs from starting at all. That also means some records will deliberately not run, so list them rather than letting people wonder."
          ],
          "items": []
        },
        {
          "heading": "Scripts, limits and slots",
          "body": [
            "A script action has a 30-second network timeout described in the article, which large tables, long loops and slow fetch calls can hit. The suggested remedies are to request only the fields you need, to use a repeating group instead of one large loop, and to batch updates. Airtable also meters runs: the pages checked on 11 October 2026 state monthly run limits of 100 on the free plan, 25,000 on Team, 100,000 on Business and 500,000 on Enterprise Scale for each workspace, reset on the first of the month, and say that failed runs count as well as successful ones. A base also has a cap on the number of automations, and switching one off does not free its slot, only deleting does. Plans change, so confirm the numbers for your own account."
          ],
          "items": [
            "Check the workspace's usage before a test that will cause many runs.",
            "Count automations that are switched off but still take a slot.",
            "Keep scripts short, and test against your largest table."
          ]
        },
        {
          "heading": "Who is told when it fails",
          "body": [
            "Airtable sends the failure email to the person who last turned the automation on. If that person has left the workspace, workspace owners are notified. Extra subscribers can be added, but they need Creator permission or higher on the base. The article also says an automation can switch itself off after an authentication change, such as a password update on a connected third-party account, and has to be reconnected under Manage connected accounts. If the one person who gets the emails ignores them, nobody knows."
          ],
          "items": []
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide does not raise plan limits, reconnect accounts or restructure a base. The paid outcome repairs one automation of up to five actions on a duplicate base, proves three agreed cases and one deliberate failure with an alert to a named person, and reports how many of your monthly runs the tests used. A separate outcome splits a repetitive table into linked tables, and a standing service watches named automations and prepares corrected copies for you to apply."
          ],
          "items": []
        }
      ],
      "platforms": [
        "airtable"
      ],
      "incoming": [
        "collection-automation-failure-diagnosis-order",
        "guide-airtable-split-table-linked-records",
        "platform-airtable"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/airtable-automation-failed-runs-and-limits/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/airtable-automation-failed-runs-and-limits/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fairtable-automation-failed-runs-and-limits%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fairtable-automation-failed-runs-and-limits%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-airtable-split-table-linked-records",
      "type": "guide",
      "title": "Splitting a repetitive Airtable table into linked tables: how the matching works and where it goes wrong",
      "description": "Why exact text matching, commas and formula primary fields matter, and the order that keeps counts reconcilable when one table becomes two.",
      "url": "/guides/airtable-split-table-linked-records/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Airtable: converting existing fields to linked records",
          "url": "https://support.airtable.com/docs/converting-existing-fields-to-linked-records",
          "checked": "2026-10-11",
          "claims": [
            "Pasting or converting into a linked record field matches each value against the linked table's primary field, exactly including spacing, capitalisation and punctuation.",
            "Unmatched values create new records in the linked table, and commas separate multiple values unless a value is in double quotation marks.",
            "If the linked table's primary field is computed, paste cannot create new records and unmatched values are dropped."
          ]
        },
        {
          "label": "Airtable: linking records",
          "url": "https://support.airtable.com/docs/linking-records-in-airtable",
          "checked": "2026-10-11",
          "claims": [
            "Linked record fields create a counterpart field in the linked table, and lookup fields can display values stored in linked records.",
            "Deleting a linked record field does not delete records, but the opposite field becomes a text field, dependent lookups and automations stop working, and a field deleted by mistake can be restored from the base's trash."
          ]
        }
      ],
      "related": [
        "airtable-wide-table-to-linked-tables",
        "platform-airtable",
        "guide-airtable-automation-failed-runs-and-limits",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "Why one table becomes two",
          "body": [
            "A table that repeats a customer's name and contact details on every row has no single place to correct them. Names drift into several spellings, summaries count variants as different customers, and an edit has to be repeated on every row. Splitting the table gives each customer one record in a parent table, and each row in the original table, now the child, links to its parent. Fixes then happen once, and values can be shown in the child through lookup fields. The risk is in the conversion, because the way Airtable creates the links has precise rules."
          ],
          "items": []
        },
        {
          "heading": "How Airtable matches text to records",
          "body": [
            "Airtable's article says that when you paste into a linked record field, or convert an existing field to one, it matches each value against the linked table's primary field, and only that field. Matching is exact: spacing, capitalisation and punctuation all count. A near-match does not link. Depending on the primary field, it may create a blank or a new record. Values with no match become new records in the linked table, named after the unmatched text. So a column containing the three spellings 'Acme Ltd', 'ACME Ltd' and 'Acme Ltd.' produces three parents unless the variants are made identical first, or linked to an existing parent."
          ],
          "items": [
            "Decide the key list before converting anything.",
            "Normalise variants on a duplicate base, using rules you write down.",
            "Create the parent records from the approved list, then link to them."
          ]
        },
        {
          "heading": "Commas, formulas and synced tables",
          "body": [
            "Three traps are documented. Commas separate values: for a conversion, the article says a cell is split into several links at each comma unless the value is wrapped in double quotation marks, so a name such as Smith, Jones, and Co. becomes three links. If the linked table's primary field is a formula or other computed field, paste cannot create new records and unmatched values are silently dropped; the workaround Airtable describes is to change the primary field to an editable type, such as single line text, for the duration. And new linked records cannot be created in a read-only synced table. Check each of these before you start, not after the counts disagree."
          ],
          "items": []
        },
        {
          "heading": "Do it in this order",
          "body": [
            "Work on a duplicate of the base. Record the starting counts: records in the table and distinct values of the key. Build and approve the key list, with a decision for every ambiguous variant. Create the parent table with an editable primary field. Link the children to their parents using the approved list, then add lookup fields that show the old columns' values. Reconcile: the child count must equal the original count at the time you took the snapshot, the parent count must equal the key list, and no child may have an empty or double link apart from listed exceptions. Compare a sample of records you choose. Keep the old columns until everything that used them has been repaired."
          ],
          "items": [
            "Child records after equals records before.",
            "Parent records equals the approved key list.",
            "Children with no parent: none, or the signed-off exceptions.",
            "A sample of twenty records matches the old text, or differs only as the normalisation rule says."
          ]
        },
        {
          "heading": "Deleting fields is not undoing them",
          "body": [
            "It is tempting to delete the old text columns once the lookups look right. Airtable's page on linking says deleting a linked record field does not delete records, but the field on the other side becomes a text field holding the former record names, and lookups, rollups, filters, sorts, automations and interface elements that depended on the field stop working. A field deleted by mistake can be restored from the base's trash, which restores the links. Keep the old columns, and hide them if you must, until every dependency has been listed and repaired."
          ],
          "items": []
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide does not clean free-text data beyond the rules you decide, move data between platforms or rebuild interface pages. The paid outcome splits one table into one parent and one child on a duplicate base, with a key list, a reconciliation sheet and a list of dependent views and automations. A duplicate is a separate base, so it comes with a cut-over note: what changes when your team moves onto it and a check for records added or deleted in the original since the snapshot, which does not find edits to existing records. You decide when to move."
          ],
          "items": []
        }
      ],
      "platforms": [
        "airtable"
      ],
      "incoming": [
        "guide-airtable-automation-failed-runs-and-limits",
        "platform-airtable"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/airtable-split-table-linked-records/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/airtable-split-table-linked-records/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fairtable-split-table-linked-records%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fairtable-split-table-linked-records%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-api-polling-checkpoint-overlap-keys",
      "type": "guide",
      "title": "A scheduled job that asks for records since its last run: why it loses some and repeats others",
      "description": "Four small pieces make polling reliable: a checkpoint, an overlap, a key for every record and complete paging. See why each exists.",
      "url": "/guides/api-polling-checkpoint-overlap-keys/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Zapier: polling trigger de-duplication",
          "url": "https://docs.zapier.com/platform/build/deduplication",
          "checked": "2026-10-11",
          "claims": [
            "Polling triggers need a unique id for every item, should return results newest first, and Zapier stores the ids it has seen and clears the list when the Zap is turned off.",
            "Zapier does not automatically fetch additional pages, so effectively only the first page of results is checked."
          ]
        },
        {
          "label": "HubSpot: CRM search API",
          "url": "https://developers.hubspot.com/docs/api/crm/search",
          "checked": "2026-10-11",
          "claims": [
            "A search returns at most 200 objects per page, is limited to 10,000 total results per query, and new or changed records may take a few moments to appear."
          ]
        },
        {
          "label": "IETF RFC 9110, section 9.2.2",
          "url": "https://www.rfc-editor.org/rfc/rfc9110.html",
          "checked": "2026-10-11",
          "claims": [
            "A client should not automatically retry a request with a non-idempotent method unless it knows the request is effectively idempotent or can detect that the original was not applied."
          ]
        }
      ],
      "related": [
        "workflow-api-polling-no-gaps-no-repeats",
        "webhook-receiver-verified-signature-duplicate-safe",
        "example-polling-checkpoint-strategies",
        "guide-webhook-timeouts-retries-missed-events",
        "guide-make-error-handlers-and-incomplete-executions",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "Why asking since the last run goes wrong",
          "body": [
            "The obvious design is to remember when the job last ran and ask for everything changed after that moment. It fails in two ways. A record can become visible after it was created: HubSpot's search documentation notes that new or changed records may take a few moments to appear, so a record stamped before the last run can first show up after it, and the next run, asking only for later changes, never sees it. And if the job asks again from the same point after a crash, or from an overlap you added to catch late records, it sees records it has already handled and acts on them a second time. The example page runs five variants over the same synthetic records so the effect is visible."
          ],
          "items": []
        },
        {
          "heading": "Four pieces that fix it",
          "body": [
            "A checkpoint is the saved position, kept somewhere that survives a crash. An overlap means asking from a little before the checkpoint, so that late-appearing records are caught. A key is whatever uniquely identifies a record, used to recognise ones already handled so that the overlap does not cause repeats. Complete paging means reading every page of results until there are none, because stopping after the first page loses everything beyond it; Zapier's developer documentation says it does not automatically fetch additional pages, so only the first page of a polling trigger's results is effectively checked, and it expects the newest items there. Together these make the job tolerant of slow, repeated and interrupted data."
          ],
          "items": [
            "Choose an overlap longer than the longest delay you can observe.",
            "Choose a key the API guarantees is unique, such as its own id.",
            "Stop only when the API says there are no more pages, or the documented cap is reached and logged."
          ]
        },
        {
          "heading": "Order of operations decides what a crash does",
          "body": [
            "There are three steps for each record: act on it, record its key as handled, and, once the whole run is recorded, move the checkpoint. The checkpoint always goes last, so a crash leaves it behind and the next run reads the same records again. The question is the order of the first two, and each order has a failure.",
            "If you record the key first and then act, a crash between the two leaves a key marked handled whose action never ran. The next run skips it, and the record is lost without any error. If you act first and then record the key, a crash between the two means the next run acts again. For an action that adds something each time it runs, such as sending an email or appending a row, that is a duplicate. The table shows one record and a crash at the one risky point, with the next run starting from the unmoved checkpoint.",
            "Acting and recording touch two different systems, so no ordering makes them one step. What works is to act first, in a way that leaves one effect however often it runs for the same record key, then record the key. Create-or-update on the key does that, as does a destination that accepts an idempotency key. The repeated call after a crash then changes nothing. The other route is to write the result and the handled key to one store in one transaction. Where the action can be neither, the choice between a possible repeat and a possible loss remains, and it has to be made on purpose and written down."
          ],
          "items": [
            "Never advance the checkpoint before the work for that page is recorded.",
            "Act first in a way that is safe to repeat for the same key, then record the key.",
            "Keep handled keys for at least the overlap plus the longest gap between runs.",
            "Test by stopping the job before each step in turn, restarting it and counting the effects per key."
          ],
          "code": "One record K1. A crash at the risky point, then the next run starts from the unmoved checkpoint.\n\norder                  crash point                     action             effects for K1\nkey first, then act    after recording, before acting  any                0  (lost)\nact first, then key    after acting, before recording  adds each time    2  (duplicate)\nact first, then key    after acting, before recording  safe to repeat    1\n\nEffects are counted at the destination, not calls made."
        },
        {
          "heading": "Know the API's limits before you design around it",
          "body": [
            "Read the list endpoint's documentation first. HubSpot's search allows at most 200 objects per page, caps a query at 10,000 total results and limits the request rate, which matters for a long backlog. Zapier's polling guidance expects newest-first order and a unique id for each item. If the API cannot filter by modification time, or offers no stable order, a complete and repeat-free job may not be possible, and you should say so rather than build one that appears to work. Where the API offers webhooks, consider whether they fit better, remembering that they bring their own repeat and loss behaviour."
          ],
          "items": []
        },
        {
          "heading": "Retries and repeat safety",
          "body": [
            "RFC 9110 says a client should not automatically retry a request with a non-idempotent method, unless it knows that the request is effectively idempotent or can detect that the original was never applied. A job that retries a create call after a timeout may therefore create twice. The remedy is to make the action idempotent by key, for example create-or-update on the record's id, or to look the record up before retrying. This is why the action should be safe to repeat for the same key even when the API itself is well behaved, and why the handled-key record alone is not enough."
          ],
          "items": []
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide does not apply to the built-in polling triggers of Zapier or Make, which manage their own checkpoints. It does not promise that a live API behaves as its documentation says, or that a record appearing later than the overlap is caught. The paid outcome builds one scheduled job against one documented API, with a checkpoint, an overlap, handled keys and complete paging, and proves it against a synthetic stand-in for 120 records over three pages, a late arrival and a stop before each step in turn. It needs the action to be safe to repeat for the same record key. Your team deploys and schedules it."
          ],
          "items": []
        }
      ],
      "platforms": [
        "make"
      ],
      "incoming": [
        "collection-automation-failure-diagnosis-order",
        "example-polling-checkpoint-strategies",
        "guide-make-error-handlers-and-incomplete-executions",
        "platform-make"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/api-polling-checkpoint-overlap-keys/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/api-polling-checkpoint-overlap-keys/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fapi-polling-checkpoint-overlap-keys%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fapi-polling-checkpoint-overlap-keys%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-automation-failure-alerts-reach-someone",
      "type": "guide",
      "title": "Who is told when an automation fails? What each tool really does, and how to test it",
      "description": "Zapier, Make, n8n and Airtable each alert in different ways, with delays, gaps and side effects. Find out where yours goes, then test it by breaking a copy.",
      "url": "/guides/automation-failure-alerts-reach-someone/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Airtable: troubleshooting automations",
          "url": "https://support.airtable.com/articles/6756755850-troubleshooting-airtable-automations",
          "checked": "2026-10-11",
          "claims": [
            "Airtable sends the failure email to the last person who turned the automation on, or to workspace owners if that person has left, and extra subscribers need Creator permission or higher."
          ]
        },
        {
          "label": "Zapier: what is replay",
          "url": "https://help.zapier.com/hc/en-us/articles/19220226086797-What-is-replay",
          "checked": "2026-10-11",
          "claims": [
            "With Autoreplay on, error emails and Zapier Manager triggers wait until the final autoreplay attempt fails, about 10 hours 35 minutes after the first error."
          ]
        },
        {
          "label": "Zapier: set up custom error handling",
          "url": "https://help.zapier.com/hc/en-us/articles/22495436062605-Set-up-custom-error-handling",
          "checked": "2026-10-11",
          "claims": [
            "When a custom error handler runs, Zapier does not send its usual error notification emails, publishing a Zap with error handling turns that Zap's autoreplay off, and custom error handling is available on paid plans."
          ]
        },
        {
          "label": "Make: overview of error handling",
          "url": "https://help.make.com/Overview-of-error-handling",
          "checked": "2026-10-11",
          "claims": [
            "The page's example for being notified of an error is adding a Slack create-a-message module to the scenario's error route.",
            "An error route does not have to contain an error handler, and if no module outputs an error in the route, Make skips the error.",
            "The Skip handler prevents the scenario from stopping and marks the run as successful even if an error occurs."
          ]
        },
        {
          "label": "n8n: error handling",
          "url": "https://docs.n8n.io/flow-logic/error-handling/",
          "checked": "2026-10-11",
          "claims": [
            "An error workflow starting with the Error Trigger node runs when an execution fails, and it must be selected in the failing workflow's settings."
          ]
        }
      ],
      "related": [
        "auto-failure-alerts-reach-a-named-owner",
        "platform-zapier",
        "platform-make",
        "platform-airtable",
        "guide-zapier-errored-halted-and-replay",
        "guide-make-error-handlers-and-incomplete-executions",
        "guide-n8n-webhook-test-and-production-urls",
        "collection-automation-failure-diagnosis-order",
        "buyer-operations-manager-inherited-automations"
      ],
      "sections": [
        {
          "heading": "The question to ask about every automation",
          "body": [
            "For each automation that matters, ask three things. Who is told if it fails? How soon? And would they act? The answers are often found to be nobody, eventually and no. Alerts were set up once, often by default, by someone who has since left or whose mailbox is full. Nothing is wrong with the tool; the alert is simply not designed. The way to find out is to read the settings and then to test by causing a failure."
          ],
          "items": []
        },
        {
          "heading": "What each tool documents",
          "body": [
            "The four tools differ. In Airtable the failure email goes to the person who last turned the automation on, and to workspace owners if that person has left; extra subscribers can be added but need Creator permission or higher on the base. In Zapier, when Autoreplay is on, error emails and Zapier Manager triggers wait for the final autoreplay attempt, which is about ten and a half hours after the first error; a custom error handler stops the usual error emails, turns that Zap's autoreplay off, and must send its own alert. In n8n, the documented route to an alert is an error workflow, which starts with the Error Trigger node and is selected in the failing workflow's settings. In Make, the documentation's own example adds a message module, such as Slack, on the scenario's error route. Make also says that an error route does not need a handler and that if no module outputs an error in the route, it skips the error, so a route that only sends a message ends with the error skipped."
          ],
          "items": [
            "Airtable: last person to enable it, else workspace owners; extra subscribers must be Creator-or-higher collaborators.",
            "Zapier: delayed by Autoreplay when it is on; standard emails stopped, and autoreplay turned off, by a custom handler.",
            "n8n: an error workflow that you set up and select.",
            "Make: a message module on the error route, followed by a directive, or the error is skipped."
          ]
        },
        {
          "heading": "Delays and suppressions",
          "body": [
            "Three effects deserve attention because they hide failures or change them. A delay means someone is told hours after the failure, which may be too late for a time-sensitive process. A suppression means a feature you switched on for one reason silently turned off another: a Zapier custom handler stops the standard email and turns that Zap's autoreplay off, so a failure inside the handler is itself silent and failed steps are no longer retried automatically. And a change of behaviour means the alert you added altered what a failure does: in Make, by its own wording an alert-only route skips the error, so a scenario that used to stop and be deactivated now drops the bundle and carries on, and Make says a skip marks the run as successful. Write down, for each automation, what is delayed, what is suppressed and what the failure now does. If a particular failure must be seen within minutes, check that the tool can deliver that, rather than assuming."
          ],
          "items": []
        },
        {
          "heading": "A shared place and two named people",
          "body": [
            "An alert addressed to one person is a single point of failure. Send alerts to a shared mailbox or channel that at least two named people read, and agree who acts and who covers. Make the alert name the automation and say where to find the failed run, so that the reader does not have to hunt. Keep the shared place out of anyone's personal rules and filters, and review who reads it when the team changes. Check what each tool lets you address: Airtable only accepts collaborators with Creator permission or higher as subscribers, so a shared mailbox works there only if that address is itself such a collaborator; otherwise add the two named people directly."
          ],
          "items": [
            "One shared mailbox or channel for all automation alerts.",
            "A named owner and a named backup, both confirmed.",
            "The automation's name and a link to the run in the message."
          ]
        },
        {
          "heading": "Test by breaking a copy",
          "body": [
            "The only reliable test is a deliberate failure. Make a copy of the automation, point it at a disposable destination and a disposable source, and cause an error in a controlled way, for example by sending a record that lacks a required field. Check who receives what, and when, and whether the message is clear. In Make, also check the run status and whether the scenario still stops or is deactivated as it did before. Do the test on a copy so that no live record or customer is involved. Repeat after any change to the alert route, the plan or the team."
          ],
          "items": []
        },
        {
          "heading": "What alerts do not catch",
          "body": [
            "An error alert needs an error. An automation that never triggers, because its trigger was turned off or its connection lapsed quietly, may produce no failure at all. An automation that runs and succeeds with wrong data produces no alert either. Cover those with a different check, such as an expected-by time for a regular output. The paid outcome sets up and proves failure alerts for up to five named automations on one platform, and records what each failure does to the run before and after; it does not detect silent non-triggering, and it makes no response-time promise."
          ],
          "items": []
        }
      ],
      "platforms": [
        "airtable"
      ],
      "incoming": [
        "buyer-operations-manager-inherited-automations",
        "collection-automation-failure-diagnosis-order",
        "collection-web-lead-to-owner-checks",
        "guide-airtable-automation-failed-runs-and-limits",
        "guide-zapier-errored-halted-and-replay",
        "guide-make-error-handlers-and-incomplete-executions",
        "guide-scheduled-report-time-zone-and-period",
        "guide-n8n-webhook-test-and-production-urls",
        "platform-airtable"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/automation-failure-alerts-reach-someone/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/automation-failure-alerts-reach-someone/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fautomation-failure-alerts-reach-someone%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fautomation-failure-alerts-reach-someone%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-webhook-signature-raw-body",
      "type": "guide",
      "title": "Verify a webhook signature on the exact bytes you received, not the data you parsed",
      "description": "Why a correct secret can still fail, how to compare signatures safely, which identifier to trust against replays, and how to test a receiver with known inputs.",
      "url": "/guides/webhook-signature-raw-body/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "GitHub: validating webhook deliveries",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries",
          "checked": "2026-10-11",
          "claims": [
            "GitHub signs deliveries with an HMAC hex digest in the X-Hub-Signature-256 header, whose value starts with sha256=.",
            "The page says never to use a plain == comparison and to use a constant-time function, naming secure_compare and crypto.timingSafeEqual in its prose; its Python example uses hmac.compare_digest.",
            "It advises verifying the signature before any further processing, keeping the secret out of code and repositories, and treating the payload as UTF-8.",
            "The older X-Hub-Signature header uses SHA-1 and is included only for legacy purposes.",
            "Its published test uses the secret It's a Secret to Everybody and the payload Hello, World!, giving a header that begins sha256=757107ea and ends 3e17; the signature is derived from the secret and the payload alone."
          ]
        },
        {
          "label": "GitHub: best practices for using webhooks",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/best-practices-for-using-webhooks",
          "checked": "2026-10-11",
          "claims": [
            "The page defines a replay attack as a bad actor intercepting a webhook delivery and re-sending it, and says to use the X-GitHub-Delivery header to ensure each delivery is unique per event.",
            "If a redelivery is requested, the X-GitHub-Delivery header is the same as in the original delivery."
          ]
        },
        {
          "label": "Shopify: HTTPS webhook subscriptions",
          "url": "https://shopify.dev/docs/apps/build/webhooks/subscribe/https",
          "checked": "2026-10-11",
          "claims": [
            "Each delivery carries an X-Shopify-Hmac-SHA256 header that is a base64-encoded HMAC signature, made with the app's client secret and the raw request body; a delivery whose signature does not match should be rejected.",
            "Shopify advises idempotent processing, and, if processing is not idempotent, detecting and skipping duplicates with the X-Shopify-Webhook-Id header."
          ]
        },
        {
          "label": "Stripe: receive events in your webhook endpoint",
          "url": "https://docs.stripe.com/webhooks",
          "checked": "2026-10-11",
          "claims": [
            "The Stripe-Signature header carries a timestamp (t=) and a v1 signature; the signed payload is the timestamp, a full stop and the request body, and the HMAC is SHA-256.",
            "Because the timestamp is part of the signed payload, an attacker cannot change it without invalidating the signature; Stripe's libraries have a default tolerance of 5 minutes between the timestamp and the current time, and a tolerance of 0 disables the recency check.",
            "Stripe advises guarding against duplicated event receipts by logging the event IDs already processed."
          ]
        },
        {
          "label": "Slack: verifying requests from Slack",
          "url": "https://docs.slack.dev/authentication/verifying-requests-from-slack",
          "checked": "2026-10-11",
          "claims": [
            "Slack signs a base string made of v0, the X-Slack-Request-Timestamp value and the request body joined with colons, and sends the hex digest prefixed with v0= in the X-Slack-Signature header.",
            "The page's example rejects a request whose timestamp differs from local time by more than five minutes."
          ]
        },
        {
          "label": "Node.js: crypto module",
          "url": "https://nodejs.org/api/crypto.html",
          "checked": "2026-10-11",
          "claims": [
            "crypto.timingSafeEqual(a, b) takes Buffers, TypedArrays or DataViews of the same byte length and throws an error if their byte lengths differ."
          ]
        }
      ],
      "related": [
        "webhook-receiver-verified-signature-duplicate-safe",
        "example-webhook-signature-test-vectors",
        "guide-webhook-timeouts-retries-missed-events",
        "guide-n8n-webhook-test-and-production-urls",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "What a signature proves, and what it does not",
          "body": [
            "A webhook URL is public. Anyone who learns it can send a request to it, so a receiver needs a way to tell the sender's real deliveries from anything else. Many senders solve this with a shared secret and an HMAC signature: the sender computes a digest of the request body with the secret and sends it in a header, and the receiver recomputes the digest and compares. A match shows that whoever made the request held the secret and that the body has not changed since the digest was made.",
            "It does not show that the event is new, or that your system should act on it. A signed delivery can arrive twice, or be recorded by an attacker and sent again later with its signature still valid. So a valid signature is the first check. Then comes a check for repeats, and which identifier that check uses decides whether it stops a replay (see the section on replays), and then your own rules about what the event is allowed to cause."
          ],
          "items": []
        },
        {
          "heading": "Verify the bytes you received",
          "body": [
            "The digest is computed over specific bytes, so the receiver must hash exactly those bytes. Shopify's documentation says its signature is computed from the raw request body with the app's client secret. GitHub's examples hash the request body as read, and GitHub tells you to make sure nothing between GitHub and your server, such as a proxy or a load balancer, alters the payload or the headers before you verify it. If your framework parses the JSON first and you then serialise it again, the bytes change: keys can reorder, spaces appear or disappear, and a trailing newline may be added or lost. The digest will no longer match, although the data is the same.",
            "This is the most common reason a correct secret still fails. Capture the raw body before any parser touches it, hash that, and only then parse. GitHub also says to treat the payload as UTF-8, because payloads can contain non-English characters."
          ],
          "items": [
            "Read the raw body first; parse it only after the signature is accepted.",
            "Do not hash a re-serialised copy of the JSON.",
            "Check that a proxy or gateway in front of your server does not rewrite the body or the headers.",
            "Compare against the header's exact format, which differs by sender (see the next section)."
          ]
        },
        {
          "heading": "The header format depends on the sender",
          "body": [
            "Two receivers can both do the HMAC correctly and still fail because they compare different encodings. GitHub puts a hex digest after the prefix sha256=. Shopify documents its X-Shopify-Hmac-SHA256 value as base64-encoded, so you compare against the decoded header value or encode your own digest as base64. Stripe puts a timestamp and one or more signatures in one header (t=...,v1=...) and signs the timestamp, a full stop and the body, not the body alone. Slack signs v0, the timestamp and the body joined with colons, and prefixes the hex digest with v0=. Read your sender's page for the header name, the encoding and exactly which bytes are signed, and test against its published example before anything else."
          ],
          "items": [],
          "code": "GitHub  sha256=<hex of HMAC-SHA256(secret, body)>\nShopify <base64 of HMAC-SHA256(client secret, raw body)>\nStripe  t=<timestamp>,v1=<hex of HMAC-SHA256(secret, timestamp + \".\" + body)>\nSlack   v0=<hex of HMAC-SHA256(secret, \"v0:\" + timestamp + \":\" + body)>"
        },
        {
          "heading": "Compare safely",
          "body": [
            "Comparing two strings with a plain equality operator can leak, through timing, how many leading characters matched. GitHub's page says never to use a plain == and to use a constant-time function instead, naming secure_compare and Node's crypto.timingSafeEqual; its Python example uses hmac.compare_digest. Node's documentation says timingSafeEqual throws an error if its two inputs have different byte lengths, so check the lengths first and reject if they differ. Verify before doing any other work: GitHub notes that this saves server time on fake deliveries as well as guarding against tampering."
          ],
          "items": [
            "Reject with an error status and do no work when the signature is missing, malformed or wrong.",
            "Do not log the secret or the full computed digest.",
            "GitHub's older SHA-1 header is kept for legacy use; use the SHA-256 one where the sender offers both."
          ]
        },
        {
          "heading": "Replays: which identifier to trust",
          "body": [
            "GitHub describes a replay attack as a bad actor intercepting a webhook delivery and re-sending it. The copy carries a genuine signature, so verification alone cannot refuse it. The usual defence is to remember which deliveries you have handled and ignore a second one, but the identifier you remember matters.",
            "GitHub's identifier is the X-GitHub-Delivery header, and Shopify's is X-Shopify-Webhook-Id. Both are headers. GitHub's published test vector derives the signature from the secret and the payload alone, and Shopify says its signature is made from the raw request body, so neither signature covers those headers. Someone holding one captured request can send the same body with a new header value, and the signature still checks. Remembering the header identifier therefore handles the sender's own retries, which is useful, but it does not stop that kind of replay.",
            "Prefer an identifier that is inside the signed bytes, such as an event ID in the JSON body, as Stripe's event objects carry. If the only identifier is a header, key your memory on a hash of the raw body instead, after checking that two different genuine events cannot have byte-identical bodies. Then decide how long to keep what you remember. Where the sender signs a timestamp, as Stripe and Slack do, reject deliveries whose timestamp is outside a tolerance (Stripe's libraries default to 5 minutes; Slack's example uses five), and keep the remembered keys for at least that tolerance plus the sender's retry period. Where nothing signed carries a time, a captured request stays valid for as long as the secret does, so the retention period is a choice to write down: a request older than it can act again.",
            "Remembering is not enough on its own. If you record a delivery as handled and then act on it, and the process stops between the two, the retry is ignored and the event is lost. Store the delivery in one durable step, acknowledge, and run the action from that stored record, in a way that leaves one effect for one key."
          ],
          "items": [
            "Use an event ID inside the signed body if there is one; otherwise a hash of the raw body.",
            "An unsigned header identifier handles retries, not replays.",
            "Reject stale signed timestamps where the sender signs one.",
            "Write down how long handled keys are kept."
          ]
        },
        {
          "heading": "Look after the secret",
          "body": [
            "GitHub recommends a random string with high entropy, kept securely on the server, and warns never to hard-code a token into an application or push it to a repository. In practice that means the secret lives in your hosting provider's configuration or secrets store, a separate secret per environment, and a written way to rotate it. Never paste it into a chat, a ticket or an email. When you ask someone to build or review a receiver, give them a test secret, not the live one."
          ],
          "items": []
        },
        {
          "heading": "Test with known inputs first",
          "body": [
            "Test before any real delivery arrives. GitHub publishes a test vector: the secret It's a Secret to Everybody and the payload Hello, World! should produce a header that begins sha256=757107ea and ends 3e17. Run that through your code, then add cases of your own: an altered body, a different secret, a missing header and a re-serialised body. Every one except the exact original should be rejected without any work being done. The example page prints the secrets, the bodies and the digests for six synthetic cases, with a short script that reproduces them, so you can check a receiver you have written or been given."
          ],
          "items": []
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide is about authenticity and replays. It does not show how to answer fast or recover missed events; those are covered separately. It is not a security assessment. The paid outcome builds one receiver for one signed event type, with a repeat key taken from the signed bytes, a durable record before the action, and tests for valid, altered, wrong-secret, missing, repeated, simultaneous and header-changed deliveries and for a stop between storing and acting, in a repository you control. Send the sender's documentation link and the event and action, never the secret. If you already have a receiver that only rejects genuine deliveries, a smaller fixed-price repair of one endpoint fits better."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-webhook-signature-test-vectors",
        "example-webhook-delivery-ledger-repeat-safe",
        "guide-webhook-timeouts-retries-missed-events",
        "guide-n8n-webhook-test-and-production-urls",
        "guide-slack-events-signature-timestamp-and-three-second-ack"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/webhook-signature-raw-body/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/webhook-signature-raw-body/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fwebhook-signature-raw-body%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fwebhook-signature-raw-body%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-webhook-timeouts-retries-missed-events",
      "type": "guide",
      "title": "The sender says webhook delivery failed: timeouts, retries, deleted subscriptions and missed events",
      "description": "Answer quickly, expect repeats, and know how to find and replay events that arrived while your endpoint was down.",
      "url": "/guides/webhook-timeouts-retries-missed-events/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "GitHub: best practices for using webhooks",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/best-practices-for-using-webhooks",
          "checked": "2026-10-11",
          "claims": [
            "A server should respond with a 2XX within 10 seconds, or GitHub drops the connection and treats the delivery as failed.",
            "GitHub suggests queuing payloads so the server can acknowledge at once and work in the background, and says to redeliver missed webhooks once the server is back up.",
            "GitHub presents the X-GitHub-Delivery header as a way to ensure that each delivery is unique per event."
          ]
        },
        {
          "label": "GitHub: handling failed webhook deliveries",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/handling-failed-webhook-deliveries",
          "checked": "2026-10-11",
          "claims": [
            "GitHub does not automatically redeliver failed webhook deliveries.",
            "Deliveries can be listed and redelivered through the REST API, where a failed delivery is one whose status is not OK."
          ]
        },
        {
          "label": "Shopify: HTTPS webhook subscriptions",
          "url": "https://shopify.dev/docs/apps/build/webhooks/subscribe/https",
          "checked": "2026-10-11",
          "claims": [
            "Shopify documents a one-second connection timeout and a five-second timeout for the whole request, and treats any non-2xx response, including a redirect, as a failure.",
            "Shopify retries 8 times over the next 4 hours, and after 8 consecutive failed attempts deletes a subscription created through the Admin API.",
            "Shopify recommends idempotent processing and de-duplicating deliveries by the X-Shopify-Webhook-Id header."
          ]
        }
      ],
      "related": [
        "webhook-receiver-verified-signature-duplicate-safe",
        "workflow-api-polling-no-gaps-no-repeats",
        "example-webhook-delivery-ledger-repeat-safe",
        "guide-webhook-signature-raw-body",
        "guide-n8n-webhook-test-and-production-urls",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "Two different failures from the sender's side",
          "body": [
            "A sender judges your endpoint by what it receives back, and within a time limit. A delivery fails if you do not answer in time, or if you answer with something that is not a success. GitHub says a server should respond with a 2XX within 10 seconds, otherwise it drops the connection and treats the delivery as failed. Shopify documents a one-second connection timeout and a five-second timeout for the entire request, and counts any non-2xx response, including a redirect, as a failure. Your own logs may show the work completing fine after the sender has already given up."
          ],
          "items": [
            "Look at the sender's delivery log for the status and the time taken.",
            "Compare it with your own log: did the work finish after the limit?",
            "Check for a redirect, for example from http to https or from a bare domain to www; some senders count that as a failure."
          ]
        },
        {
          "heading": "Answer first, work second",
          "body": [
            "The fix for timeouts is to separate acknowledging from doing. GitHub suggests queuing the payload so the server can acknowledge straight away and process it in the background without blocking later deliveries. The receiver verifies the signature, writes down that the delivery arrived, answers with a success status, and hands the work to something that can take as long as it needs. The important detail is the order: record before you answer. If you answer first and then crash before recording, the sender believes it succeeded and the event is gone."
          ],
          "items": [
            "Verify, record durably, answer, then process.",
            "Keep the answer path short, with no calls to other systems.",
            "Make the background work safe to run twice, because a crash can repeat it."
          ]
        },
        {
          "heading": "Retries mean repeats",
          "body": [
            "Senders retry when they think a delivery failed, and sometimes when it did not. Shopify says it retries 8 times over the next 4 hours when there is no response or an error, and recommends that you process deliveries idempotently or, if you cannot, store each X-Shopify-Webhook-Id and skip ones you have seen. GitHub names its X-GitHub-Delivery header as a way to ensure each delivery is unique per event. Whichever sender you use, find the identifier it documents, keep a record of identifiers handled, check it before acting, and return success for a repeat without repeating the action. Remembering a header identifier handles the sender's own retries; it does not by itself stop someone replaying a captured request with the header changed, and the signature guide explains which identifier to trust for that."
          ],
          "items": [
            "Store the delivery identifier atomically, so two copies arriving together do not both pass.",
            "Treat the same identifier as the same event, and a different identifier with similar content as a new one.",
            "Return a success status for a repeat so the sender stops retrying."
          ]
        },
        {
          "heading": "When your endpoint was down",
          "body": [
            "Do not assume a missed event will come back. GitHub states that it does not automatically redeliver failed deliveries; it offers a way to list deliveries and redeliver them through its REST API, where a failed delivery is one whose status is not OK, and it recommends redelivering missed webhooks once your server is back up. Shopify retries for about four hours and then, after eight consecutive failures, deletes a subscription that was created through the Admin API, so a long outage can remove the subscription as well as lose events. Other senders differ, so read the sender's own pages.",
            "After an outage, reconcile instead of hoping. List what the sender says it delivered or what changed in the period, compare that with the identifiers you handled, and process only the difference, once each. If the sender's delivery log is short-lived, do this promptly."
          ],
          "items": [
            "Write down the start and end of the outage from your logs.",
            "Check whether your subscription still exists, as well as whether it delivered.",
            "Use the sender's delivery list or its normal listing endpoint to find what you missed."
          ]
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide does not promise that no event is ever lost; no receiver can promise that without the sender's cooperation. A reconciliation against the sender's delivery log is a separate piece of work. The paid receiver outcome covers fast acknowledgement and repeat-safe handling for one event type. For a system that has to find changes by asking, the polling outcome covers a checkpointed job that neither misses nor repeats records. Both are accepted by tests against synthetic data, not by a promise about live behaviour."
          ],
          "items": []
        }
      ],
      "incoming": [
        "collection-automation-failure-diagnosis-order",
        "example-webhook-signature-test-vectors",
        "example-webhook-delivery-ledger-repeat-safe",
        "example-polling-checkpoint-strategies",
        "guide-api-polling-checkpoint-overlap-keys",
        "guide-webhook-signature-raw-body",
        "guide-n8n-webhook-test-and-production-urls"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/webhook-timeouts-retries-missed-events/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/webhook-timeouts-retries-missed-events/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fwebhook-timeouts-retries-missed-events%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fwebhook-timeouts-retries-missed-events%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-hubspot-form-duplicates-contacts-companies",
      "type": "guide",
      "title": "Duplicate HubSpot contacts or companies after a web form: which route created them",
      "description": "HubSpot matches contacts by email and companies by domain, but the route a lead takes decides what happens. Learn the four routes and the risks of merging.",
      "url": "/guides/hubspot-form-duplicates-contacts-companies/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "HubSpot: deduplicate records",
          "url": "https://knowledge.hubspot.com/records/deduplication-of-records",
          "checked": "2026-10-11",
          "claims": [
            "HubSpot automatically de-duplicates contacts using email addresses and companies using domain names.",
            "A form submission with an existing email adds its data to that contact, and a submission with a secondary email overwrites the existing email address.",
            "Companies created through the API are not de-duplicated by the Company domain name property, and custom unique-value properties are not supported in forms."
          ]
        },
        {
          "label": "HubSpot: create and edit forms",
          "url": "https://knowledge.hubspot.com/forms/create-and-edit-forms",
          "checked": "2026-10-11",
          "claims": [
            "With Automatically create new contacts from unknown email addresses on, each unique email gets a contact; with it off, HubSpot falls back to browser cookies, which may overwrite contacts.",
            "By default the Email field is required for form submissions to create contacts."
          ]
        },
        {
          "label": "HubSpot: use non-HubSpot forms",
          "url": "https://knowledge.hubspot.com/forms/use-non-hubspot-forms",
          "checked": "2026-10-11",
          "claims": [
            "Submissions from collected external forms create or update a contact by email, and all submissions associated with the same cookie are added to the same contact record.",
            "Collection depends on conditions such as the tracking code loading, a static form and no iframe, and a developer can use the Forms API instead."
          ]
        },
        {
          "label": "HubSpot: how HubSpot tracks visitors",
          "url": "https://knowledge.hubspot.com/account/how-does-hubspot-track-visitors",
          "checked": "2026-10-11",
          "claims": [
            "A form with no email field identifies submissions by browser cookie and this cannot be changed.",
            "On a shared computer the cookie can update or overwrite the cookied contact's properties, including email, and HubSpot suggests a private window or different browser each time when testing."
          ]
        },
        {
          "label": "HubSpot: merge records",
          "url": "https://knowledge.hubspot.com/records/merge-records",
          "checked": "2026-10-11",
          "claims": [
            "Merged contacts cannot be unmerged.",
            "The primary record's property values are prioritised, timelines and property history are combined, and a merge fails if the records have a combined total of 250 or more merges."
          ]
        }
      ],
      "related": [
        "crm-duplicate-contacts-from-web-forms",
        "platform-hubspot",
        "crm-web-lead-pipeline-repair",
        "guide-hubspot-lead-owner-rotation-fallbacks",
        "guide-hubspot-salesforce-sync-rules-and-errors",
        "buyer-marketing-manager-web-lead-flow",
        "collection-web-lead-to-owner-checks"
      ],
      "sections": [
        {
          "heading": "What HubSpot matches on",
          "body": [
            "HubSpot's documentation says it automatically de-duplicates contacts using email addresses and companies using domain names. When a form is submitted with an email that already exists, the new information is added to that contact rather than creating another. If the submission uses a contact's secondary email address, the page says that address overwrites the contact's existing email address, which surprises people. Custom unique-value properties, which can be used to match on other fields in imports, are not supported in forms, so form matching stays with the default properties."
          ],
          "items": []
        },
        {
          "heading": "Four routes into HubSpot, four behaviours",
          "body": [
            "The route a lead takes decides which rules apply. A HubSpot form with an email field follows the matching above. A HubSpot form with no email field has no email to match on, so HubSpot uses the browser cookie to identify the visitor, and its page on visitor tracking says that cannot be changed. A form on your own website that HubSpot collects through its tracking code depends on that code loading and on the form being a plain static form, not in an iframe; HubSpot suggests using its Forms API when those conditions are not met. An integration or script that creates records through the API follows the API's rules, and the documentation says companies created through the API are not de-duplicated by the domain name property. That last route is a common source of many company records sharing one domain."
          ],
          "items": [
            "HubSpot form with an email field: matched by email.",
            "Form with no email field: matched by cookie.",
            "Collected external form: matched by email, with cookie effects, if the tracking code loads.",
            "API or integration: follows the API's rules; companies are not de-duplicated by domain."
          ]
        },
        {
          "heading": "Cookie matching can overwrite or combine people",
          "body": [
            "The create-and-edit forms page describes a setting named Automatically create new contacts from unknown email addresses. With it on, each unique email address gets its own contact, and an existing email updates that record. With it off, HubSpot tries the email first and, if there is no match, falls back to the browser's cookies to decide which contact to update, and the page warns this may result in contacts being overwritten when the same form is submitted repeatedly from one device. The page on non-HubSpot forms adds that all submissions associated with the same cookie are added to the same contact record, so different email addresses entered on one browser can end up on one contact.",
            "A shared office computer or a family laptop can therefore combine different people, or overwrite one with another; HubSpot's visitor-tracking page says the cookie can update or overwrite the cookied contact's properties, including email, on a shared computer, and suggests a private window or a different browser each time when testing. The remedy depends on the route, so confirm which route your duplicate or overwrite came through."
          ],
          "items": []
        },
        {
          "heading": "Do not merge as a test",
          "body": [
            "Merging contacts looks like the obvious clean-up, but the page on merging says merged contacts cannot be unmerged, and the workaround it gives is to remove the extra email from the merged contact and create a new contact. A merge also combines timelines and property history, keeps the primary record's values where both have them, and can fail when records have combined more than a documented number of merges. Decide which record wins and test on synthetic records before any real merge, and do not merge in order to find out how a form behaves."
          ],
          "items": [
            "Never merge real contacts to experiment.",
            "Choose the winning record rule before merging anything.",
            "Export or record what you are about to merge if you might need the values back."
          ]
        },
        {
          "heading": "A safe diagnosis",
          "body": [
            "Open three duplicate contacts and note, for each, the original source and the form or integration that created it. If they share a path, that path is the suspect. Then check the form: is there an email field, and what does the create-contacts setting say? Then, on a test route and with synthetic addresses, submit the same email twice, two different emails from one domain, and two different people from one browser, and count the contacts and companies after each. Make sure that no workflow will email or notify real people when a test contact appears."
          ],
          "items": []
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide does not clean up existing duplicates, give consent or data-protection advice, or cover duplicates that come from the Salesforce sync, which follow different rules. The paid outcome changes one form path so that it creates one contact and one company, proven with six synthetic submissions. It does not merge anything. If several paths and tools need attention at once, the project outcome tests each path from form to owner."
          ],
          "items": []
        }
      ],
      "platforms": [
        "hubspot"
      ],
      "incoming": [
        "buyer-marketing-manager-web-lead-flow",
        "collection-web-lead-to-owner-checks",
        "guide-hubspot-lead-owner-rotation-fallbacks",
        "guide-hubspot-salesforce-sync-rules-and-errors",
        "platform-hubspot"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/hubspot-form-duplicates-contacts-companies/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/hubspot-form-duplicates-contacts-companies/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fhubspot-form-duplicates-contacts-companies%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fhubspot-form-duplicates-contacts-companies%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-hubspot-lead-owner-rotation-fallbacks",
      "type": "guide",
      "title": "New HubSpot leads with no owner: how rotation, away users and fallbacks really behave",
      "description": "Write the routing rules first, then learn what the rotate action counts for contact records, how HubSpot's Leads tool differs, and where a lead ends up when nobody is available.",
      "url": "/guides/hubspot-lead-owner-rotation-fallbacks/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "HubSpot: assign and rotate record owners using workflows",
          "url": "https://knowledge.hubspot.com/workflows/assign-and-rotate-record-owners-using-workflows",
          "checked": "2026-10-11",
          "claims": [
            "The rotate-to-owner action needs a Sales Hub or Service Hub Professional or Enterprise subscription and paid seats for assigned users; deactivated users and users without a paid seat are not included.",
            "For most record types, records are assigned equally by default within a team or between specified users, and if owners are added or removed after the workflow is on, the assignment counts for that action reset; the page says these distribution rules do not apply to lead-based or ticket-based workflows.",
            "Lead assignment using workflows is marked BETA and offers load balanced, round robin or random distribution; users set to Away are not assigned records, and if all users in the pool are away the owner is left unassigned. The No one option leaves records unassigned.",
            "If the HubSpot owner property syncs to Salesforce, the action may not work as expected, and Salesforce may try to update HubSpot with the previous owner even after HubSpot clears ownership."
          ]
        },
        {
          "label": "HubSpot: create and edit forms",
          "url": "https://knowledge.hubspot.com/forms/create-and-edit-forms",
          "checked": "2026-10-11",
          "claims": [
            "Owner notifications go only to a contact's owner, and if a contact has no owner no notifications will be sent."
          ]
        }
      ],
      "related": [
        "lead-routing-new-leads-unassigned-or-wrong-owner",
        "platform-hubspot",
        "guide-hubspot-form-duplicates-contacts-companies",
        "buyer-marketing-manager-web-lead-flow",
        "collection-web-lead-to-owner-checks",
        "guide-hubspot-salesforce-sync-rules-and-errors"
      ],
      "sections": [
        {
          "heading": "Write the rules before anyone builds them",
          "body": [
            "Most routing problems start as undecided rules. Write the routing as an ordered list in which the first matching rule wins, and finish it with a named fallback owner for everything that matches nothing. Each rule should be checkable from fields that exist on the lead when it is created. Decide what happens to a lead that already has an owner, and say who is told when the rule list changes. The example page shows a small rule table with eight synthetic leads and the owner each should reach, and in a real test every rule, edge case and fallback gets a lead of its own."
          ],
          "items": [
            "One line per rule, in priority order.",
            "A named fallback owner who has agreed to take unmatched leads.",
            "A written decision about leads that already have an owner.",
            "A date and a person responsible for reviewing the rules."
          ]
        },
        {
          "heading": "Contact records: what the rotate action counts, and what resets it",
          "body": [
            "HubSpot's documentation describes the rotate-to-owner workflow action as assigning records to users with paid sales or service seats, and says it needs a Sales Hub or Service Hub Professional or Enterprise subscription. For most record types, including a workflow on contact records, records are assigned equally within a selected team or between specified users by default. The assignment counts are tracked for each action separately, not as a total of how many records each person owns, and they reset when owners are added or removed after the workflow is on. Deactivated users, and users who no longer have a paid seat, are not included in assignments.",
            "The practical lesson is that rotation is not a permanent ledger. A change to the team changes who gets the next records, and because deactivated users are not included, a pool that looks full on paper can be smaller than it seems. When you test a shared pool, check that every record went to a member of the pool and that nobody outside it received any; do not expect a fixed alternation."
          ],
          "items": []
        },
        {
          "heading": "The Leads tool and tickets work differently",
          "body": [
            "HubSpot's page says the distribution rules above apply to most record types and do not apply to lead-based or ticket-based workflows. Assigning leads with workflows is marked BETA on that page, and it offers load balanced, round robin or random distribution; the page describes round robin as assigning in a rotating, sequential order and random as assigning randomly to eligible users in the pool. Which of the two mechanisms your account uses depends on whether your team works new leads in the Leads tool or only as contact records, so find that out before you rely on either description. The rest of this guide marks which one it means."
          ],
          "items": [
            "Contact records: equal distribution by default, counts per action, reset when owners change.",
            "Leads tool and tickets: choose a distribution type, with its own Away setting; the reset rule above is not stated for these.",
            "Ask your HubSpot administrator which one your new leads use."
          ]
        },
        {
          "heading": "Away users, empty pools and the no-one option",
          "body": [
            "For leads and tickets, HubSpot's page says users set to Away are not assigned records, and that if every user in the assignment pool is away the owner becomes unassigned. The Away setting is described for those records, not for contact-based workflows. A separate option, No one, deliberately leaves incoming records unassigned on any of them. With the contact owner option, the page says a fallback option is used when the owner is missing or the assignment fails. Each of these is a path to a record with no owner, which is why a named fallback and an alert for unowned records belong in the design from the start."
          ],
          "items": []
        },
        {
          "heading": "If owner also syncs to Salesforce",
          "body": [
            "HubSpot warns that if the HubSpot owner property syncs to Salesforce, the assignment action may not work as expected, and that Salesforce may try to update HubSpot with the previous owner even after HubSpot clears ownership. A routing rule can therefore look correct in HubSpot and still be reversed a sync later. If your owner field syncs, have your Salesforce administrator say which side wins for owner and test a synthetic record for long enough to see a sync happen. The sync guide explains the rules and the first-sync baseline."
          ],
          "items": []
        },
        {
          "heading": "Why an unowned lead stays unnoticed",
          "body": [
            "The forms page says that notifications go to a contact's owner, and that if a contact has no owner no notifications will be sent. So a lead without an owner produces no owner notification, and a sales manager has nothing in their inbox to show that anything arrived. A saved view of new contacts with an empty owner, checked on a schedule by a named person, catches this. So does an alert on the fallback path. Neither depends on the person who has gone missing."
          ],
          "items": [
            "Create a view of new leads with no owner and look at it daily or weekly, as the business needs.",
            "Alert the fallback owner whenever a lead reaches the fallback.",
            "Include departed users in your review of who is in each pool."
          ]
        },
        {
          "heading": "Test with synthetic leads, not real ones",
          "body": [
            "Test the rule list with leads you invented: one for each rule, one for each edge such as a blank field or a boundary value, and one that matches nothing. Check the owner each receives against the table, then change the pool and test again. Make sure that test leads do not trigger real notifications, tasks or sequences, and delete them afterwards. Record the expected and actual owner side by side so that a reader can see the result at a glance."
          ],
          "items": []
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide does not design territories, quotas or commission, and it makes no promise about how fast a lead is contacted. It assumes you already have HubSpot with the features named above. The paid outcome rebuilds one assignment workflow on contact records from a rule table you approve, with a fallback, and proves it with the synthetic leads listed in that table (one for each rule, edge case and fallback, plus one that already has an owner) and three more after a pool change. It does not cover the Leads tool or tickets. A standing service checks weekly that new leads still have owners and that no new duplicates appear."
          ],
          "items": []
        }
      ],
      "platforms": [
        "hubspot"
      ],
      "incoming": [
        "buyer-marketing-manager-web-lead-flow",
        "collection-web-lead-to-owner-checks",
        "guide-hubspot-form-duplicates-contacts-companies",
        "platform-hubspot"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/hubspot-lead-owner-rotation-fallbacks/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/hubspot-lead-owner-rotation-fallbacks/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fhubspot-lead-owner-rotation-fallbacks%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fhubspot-lead-owner-rotation-fallbacks%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-hubspot-salesforce-sync-rules-and-errors",
      "type": "guide",
      "title": "A HubSpot field keeps changing back from Salesforce: sync rules, baselines and picklist errors",
      "description": "Read the sync rule on the field, understand the first-sync baseline, then check types, picklists and where HubSpot lists sync errors.",
      "url": "/guides/hubspot-salesforce-sync-rules-and-errors/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "HubSpot: map properties to Salesforce fields",
          "url": "https://knowledge.hubspot.com/salesforce/map-hubspot-properties-to-salesforce-fields",
          "checked": "2026-10-11",
          "claims": [
            "The sync rules are prefer Salesforce unless blank, always use Salesforce, two-way where the most recent value wins, and do not sync.",
            "Existing values do not sync retroactively when a mapping is created, and for a contact with no sync history the first sync uses the current Salesforce value as the baseline.",
            "A mapping needs compatible field types, and owner can only be mapped two-way."
          ]
        },
        {
          "label": "HubSpot: manage Salesforce integration sync errors",
          "url": "https://knowledge.hubspot.com/salesforce/resolve-salesforce-integration-sync-errors",
          "checked": "2026-10-11",
          "claims": [
            "Sync errors appear under Settings, Integrations, Connected Apps, Salesforce, Data sync, Sync Health.",
            "Documented error types include mismatched options, restricted picklist value, inactive Salesforce owner, field permission, type mismatch and custom code, with a fix for each.",
            "After fixing a cause, errors are resynced manually, with a cap of 100 errors at a time."
          ]
        }
      ],
      "related": [
        "hubspot-salesforce-synced-field-reverts",
        "platform-hubspot",
        "guide-hubspot-form-duplicates-contacts-companies",
        "crm-web-lead-pipeline-repair",
        "collection-web-lead-to-owner-checks"
      ],
      "sections": [
        {
          "heading": "Start with the rule on the field",
          "body": [
            "HubSpot's integration gives each mapped field one of four sync rules, and the rule decides which system wins. Prefer Salesforce unless blank lets HubSpot pass a value to Salesforce only when Salesforce has no value; otherwise the Salesforce value overwrites HubSpot. Always use Salesforce is one-directional: HubSpot never passes data, and Salesforce values always overwrite HubSpot. Two-way lets the most recent value overwrite the other. Do not sync means data never passes. Under the first two rules, a person who corrects the value in HubSpot will see it return, which looks like a fault but is the rule working as set.",
            "Owner is the documented exception: it can be mapped only two-way, and the values must match exactly."
          ],
          "items": [
            "Open the field mapping in the integration settings and read the rule.",
            "Ask which system people actually edit, and which should win.",
            "Check whether two mappings touch the same field."
          ]
        },
        {
          "heading": "The first sync and old values",
          "body": [
            "Two documented details explain many surprises. Existing values do not sync retroactively when a new mapping is created. And for a contact with no sync history for that field, the first sync uses the current Salesforce value as the baseline, which can overwrite a newer HubSpot value. A mapping created after people have been editing in HubSpot can therefore replace their work the first time the record syncs. Test a new or changed mapping on a synthetic record before anyone relies on it."
          ],
          "items": []
        },
        {
          "heading": "Types, picklists and the errors they cause",
          "body": [
            "A mapping can be saved only when the field types are compatible; for example a HubSpot dropdown can map to a Salesforce picklist and a single-line text property to a string. Even then, values have to line up. HubSpot's error article lists mismatched options, where the Salesforce option API names must match HubSpot's internal values; restricted picklist values, where it recommends turning off the restriction setting; inactive Salesforce owners; and state and country values that must be valid together. Each has its own documented fix. A value present in one system and absent from the other's list is rejected, so the record may stop syncing while others carry on."
          ],
          "items": [
            "Compare the option lists on both sides by internal value, not by label.",
            "Check whether a restricted picklist is blocking the value.",
            "Check that an owner is active in Salesforce."
          ]
        },
        {
          "heading": "Where to see the errors, and who fixes what",
          "body": [
            "HubSpot lists sync errors under Settings, Integrations, Connected Apps, Salesforce, Data sync, in the Sync Health tab, with a card for each error type and the option to export them. Some causes sit in Salesforce, such as custom code like flows and validation rules, duplicate rules and the integration user's field permissions; the article says these need your Salesforce administrator. After a cause is fixed, errors are resynced by hand, in batches of at most 100, so a large backlog needs another route. Do not resync a large batch before you know what the fix will do to live records."
          ],
          "items": []
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide does not connect the integration, add new objects or clean historical errors. It is not a substitute for your Salesforce administrator on flows and permissions. The paid outcome covers one mapped field pair: it reads the cause, corrects the mapping on the HubSpot side, names any Salesforce change for your administrator, and tests in each direction on a synthetic record. It does not bulk re-sync or edit live records."
          ],
          "items": []
        }
      ],
      "platforms": [
        "hubspot"
      ],
      "incoming": [
        "collection-web-lead-to-owner-checks",
        "guide-hubspot-form-duplicates-contacts-companies",
        "guide-hubspot-lead-owner-rotation-fallbacks",
        "platform-hubspot"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/hubspot-salesforce-sync-rules-and-errors/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/hubspot-salesforce-sync-rules-and-errors/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fhubspot-salesforce-sync-rules-and-errors%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fhubspot-salesforce-sync-rules-and-errors%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-zapier-duplicate-records-five-causes",
      "type": "guide",
      "title": "A Zap created the record twice: find which of five causes it was",
      "description": "Count the runs, then check for a second Zap on the same trigger, a self-trigger, a replay and a plain create step before you change anything.",
      "url": "/guides/zapier-duplicate-records-five-causes/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Zapier: how Zapier handles duplicate data",
          "url": "https://help.zapier.com/hc/en-us/articles/8496260269965-How-Zapier-handles-duplicate-data-in-Zap-workflows",
          "checked": "2026-10-11",
          "claims": [
            "For polling triggers Zapier compares each item's unique ID with IDs the Zap has already seen and runs only for a new ID.",
            "De-duplication checks only within the same Zap, so two Zaps built on one trigger both fire.",
            "How an action step handles duplicate data depends on the destination app, and an app that allows duplicates gets a duplicate record."
          ]
        },
        {
          "label": "Zapier: how Zap triggers work",
          "url": "https://help.zapier.com/hc/en-us/articles/8496244568589-How-Zap-triggers-work",
          "checked": "2026-10-11",
          "claims": [
            "A Zap that writes back to the app and resource it monitors can retrigger itself in a loop."
          ]
        },
        {
          "label": "Zapier: Zap is stuck in a loop",
          "url": "https://help.zapier.com/hc/en-us/articles/8496232045453-Zap-is-stuck-in-a-loop",
          "checked": "2026-10-11",
          "claims": [
            "Recommended fixes include a marker plus a filter, a dedicated processed field and replacing create steps with Find or Create actions."
          ]
        },
        {
          "label": "Zapier: what is replay",
          "url": "https://help.zapier.com/hc/en-us/articles/19220226086797-What-is-replay",
          "checked": "2026-10-11",
          "claims": [
            "Replay from Zap history retries only steps that errored; replay from the editor re-runs every step of the Zap starting with the trigger."
          ]
        },
        {
          "label": "Zapier: search steps",
          "url": "https://help.zapier.com/hc/en-us/articles/8496241402253",
          "checked": "2026-10-11",
          "claims": [
            "Some search actions offer a create-if-it-does-not-exist option; when a search finds nothing the step is halted unless set to succeed."
          ]
        }
      ],
      "related": [
        "platform-zapier",
        "guide-zapier-errored-halted-and-replay",
        "guide-zapier-field-mapping",
        "example-webhook-delivery-ledger-repeat-safe",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "Count the runs before you change anything",
          "body": [
            "Open Zap history and find the time the duplicate was created. The number of runs around that moment tells you which half of the Zap to look at. Two runs for one source event points at the trigger side. One run that wrote two records points at the action side. A run followed some time later by a replay points at replay. Write down which pattern you see before you edit anything, because the fixes differ and a wrong fix can hide the real cause."
          ],
          "items": [
            "Two runs with the same source record ID: a second Zap, or a trigger that fired twice.",
            "Two runs with different source IDs: two real events, so the Zap is doing what it was built to do.",
            "One run and two records: two create steps, or a repeated branch inside the Zap.",
            "A run and a replay: a replay ran a step again that had already succeeded."
          ]
        },
        {
          "heading": "Trigger-side causes",
          "body": [
            "Zapier says that for a polling trigger it compares each item's unique ID with the IDs the Zap has already seen, and runs only when it sees a new one. It also says this check works only inside one Zap, so two Zaps built on the same form or app event both fire. Instant triggers do not use the check, because the app is expected to send each event once. If the sending app delivers an event twice, the Zap runs twice.",
            "A Zap can also trigger itself. Zapier's trigger documentation warns that a Zap that writes back to the app and resource it watches can loop, and its loop article lists a trigger for new or updated records that fires again when the Zap's own action edits the record. Look for a Zap whose action changes the same kind of record its trigger watches."
          ],
          "items": [
            "Search your Zap list for every Zap that uses the same trigger app and event, including old ones that were switched off and on again.",
            "Check whether the action step edits the record type the trigger watches.",
            "Ask the source app whether it can send one event twice, for example after a retry."
          ]
        },
        {
          "heading": "Action-side causes and replay",
          "body": [
            "How an action behaves when the data already exists depends on the destination app. Zapier's article says that if an app allows duplicate data, Zapier creates a duplicate record; if the app does not allow it, Zapier returns an error; and on rare occasions the app ignores the duplicate. A plain create step is therefore safe only when the destination refuses repeats.",
            "Replay is a second route to duplicates. Zapier documents that replaying from Zap history retries only the steps that errored, while replaying a whole run from the editor re-runs every step, starting with the trigger, whatever happened before. A create step that had succeeded can run again. As a caution rather than a documented rule: a step that reported an error after its request reached the destination may already have created a record, so look at the destination before you replay."
          ],
          "items": [
            "Treat the editor's full-run replay as a deliberate second run of everything.",
            "Check the destination for the record before replaying an errored create step.",
            "Do not replay a run on live data just to test a fix."
          ]
        },
        {
          "heading": "What usually fixes it",
          "body": [
            "The durable fix is to make creation find-first. Zapier's loop article recommends swapping plain create steps for find-or-create actions so that an existing record is not duplicated. Its search-step article explains that only some searches offer a create-if-it-does-not-exist option, and that when a search finds nothing the step stops as halted unless it is set to count as successful. Where the app has no such option, a search step followed by a filter or a path can do the same job.",
            "Choose the field that identifies one real thing, such as an order number or an email address, and use that field for the search. If the Zap edits its own source, add a processed marker field and a filter that stops records already marked."
          ],
          "items": [
            "Write down one unique key for each record type.",
            "Decide whether a match should be left alone or updated, and which fields may be overwritten.",
            "Test with disposable records: the same event twice, a full-run replay, and one different event."
          ]
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide does not clean up duplicates that already exist. Merging or deleting records can lose information and needs its own decision about which record wins. It also does not cover a Zap that never starts, or a mapping that drops fields; the existing field-mapping guide covers the second.",
            "The paid outcome for this problem is one Zap and one create step. It is accepted when one synthetic event creates one record, the same event again and a full-run replay still leave one record, and a different event creates its own record. Send the Zap's trigger and destination, with no customer data, and fit is confirmed before any access."
          ],
          "items": []
        }
      ],
      "platforms": [
        "zapier"
      ],
      "incoming": [
        "buyer-operations-manager-inherited-automations",
        "collection-automation-failure-diagnosis-order",
        "guide-zapier-errored-halted-and-replay",
        "platform-zapier"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/zapier-duplicate-records-five-causes/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/zapier-duplicate-records-five-causes/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fzapier-duplicate-records-five-causes%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fzapier-duplicate-records-five-causes%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-zapier-errored-halted-and-replay",
      "type": "guide",
      "title": "A Zap stopped or shows Errored or Halted: what each status means and what to replay",
      "description": "Read the run status first, understand why a Zap turns itself off, and know what a replay repeats and why error emails can arrive hours late.",
      "url": "/guides/zapier-errored-halted-and-replay/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Zapier: troubleshoot errors in Zap workflows",
          "url": "https://help.zapier.com/hc/en-us/articles/8496037690637-How-to-troubleshoot-errors-in-Zap-workflows",
          "checked": "2026-10-11",
          "claims": [
            "A Zap automatically turns off if 95% of its runs result in errors in the last 7 days.",
            "Errored runs did not finish; safely halted runs stopped on purpose, usually when a search step found nothing, and do not turn the Zap off.",
            "Other statuses include On hold, Handled error and Scheduled."
          ]
        },
        {
          "label": "Zapier: what is replay",
          "url": "https://help.zapier.com/hc/en-us/articles/19220226086797-What-is-replay",
          "checked": "2026-10-11",
          "claims": [
            "Autoreplay tries a failed step up to 5 times, at 5 minutes, 30 minutes, 1 hour, 3 hours and 6 hours after the previous attempt.",
            "Replay must happen within 60 days of the initial trigger event.",
            "Replay from history breaks if steps are added, deleted or moved, full-run replay is billed again, and autoreplay and full-run replay need a paid plan.",
            "Error emails and Zapier Manager triggers wait until the final autoreplay attempt fails."
          ]
        },
        {
          "label": "Zapier: set up custom error handling",
          "url": "https://help.zapier.com/hc/en-us/articles/22495436062605-Set-up-custom-error-handling",
          "checked": "2026-10-11",
          "claims": [
            "Publishing a Zap with error handling turns its autoreplay off, and no error notification email is sent when a handler runs.",
            "Custom error handling is available on Professional, Team and Enterprise plans."
          ]
        },
        {
          "label": "Zapier: how Zap triggers work",
          "url": "https://help.zapier.com/hc/en-us/articles/8496244568589-How-Zap-triggers-work",
          "checked": "2026-10-11",
          "claims": [
            "A Zap does not fire for data created in the app before the Zap was turned on.",
            "If a Zap stops triggering with no errors, turning it off, waiting about a minute and turning it on resets the trigger connection."
          ]
        }
      ],
      "related": [
        "auto-failure-alerts-reach-a-named-owner",
        "platform-zapier",
        "guide-zapier-duplicate-records-five-causes",
        "guide-automation-failure-alerts-reach-someone",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "Read the status before you touch the Zap",
          "body": [
            "Zapier's troubleshooting article separates runs that errored from runs that were safely halted. An errored run hit a problem and did not finish. A safely halted run stopped on purpose, usually because a search step found nothing, and halted runs do not cause the Zap to switch off. The article also names other statuses you will meet in Zap history."
          ],
          "items": [
            "Errored: the run did not finish because of a problem, such as a rejected value or an expired connection.",
            "Safely halted: the run stopped deliberately, often because a search found no match.",
            "On hold: the run is paused, for example because an app is disconnected or a task limit was reached.",
            "Handled error: a custom error handler took over the run.",
            "Scheduled: the run errored and Autoreplay will try it again."
          ]
        },
        {
          "heading": "Why a Zap can switch itself off",
          "body": [
            "Zapier states that a Zap will turn off automatically if 95% of its runs result in errors in the last 7 days. The article describes warning emails and a grace period for Team and Enterprise accounts. The practical effect is that a Zap with one broken step can go quiet without anyone choosing to stop it, and then nothing runs until someone notices.",
            "A Zap that is off is not collecting anything. Zapier's trigger documentation says a Zap does not fire for data created in the app before it was turned on, which means records created while a Zap was off may need separate handling when you turn it back on. Decide how you will catch up before you switch it back on, rather than after."
          ],
          "items": [
            "Check whether the Zap was switched off by Zapier, by a colleague or by an error rate.",
            "List what happened in the source app while the Zap was off.",
            "Fix the cause of the errors before switching on; otherwise the error rate can climb again."
          ]
        },
        {
          "heading": "Replay and Autoreplay: what each repeats",
          "body": [
            "Zapier's replay article says replay from Zap history retries only steps that errored, and leaves successful steps alone. Replaying a whole run from the editor re-runs every step, starting with the trigger, whatever each step's earlier status, and bills the tasks again. A replay must happen within 60 days of the initial trigger event, and it fails if you have added, removed or moved steps since. Zapier also says it cannot guarantee a replay will succeed.",
            "Autoreplay retries an errored step up to five times. The article gives the gaps as 5 minutes, 30 minutes, 1 hour, 3 hours and 6 hours, so the last attempt lands about ten and a half hours after the first error. It is meant for temporary problems such as a short outage. For an error you have already fixed, a manual replay recovers the data."
          ],
          "items": [
            "Use history replay for a fixed errored step, after checking the destination for a half-finished record.",
            "Use full-run replay only when every step should run again.",
            "Plan features differ: autoreplay and full-run replay need a paid plan."
          ]
        },
        {
          "heading": "Why the alert can be late or missing",
          "body": [
            "With Autoreplay on, Zapier holds back error emails and Zapier Manager triggers until the final attempt fails. A person waiting for an email about a broken Zap may therefore hear about it hours after it first failed. A custom error handler changes this again: publishing a Zap with error handling turns that Zap's autoreplay off, and no error notification email is sent when a handler runs. The handler must send its own alert, and a failure inside the handler needs its own check.",
            "None of this is a fault. It means that who is told, and when, depends on settings that someone chose once and may have forgotten. A named alert route with a test is the only way to know it works."
          ],
          "items": [
            "Find out whether each important Zap uses Autoreplay, a custom handler or neither.",
            "Send a test failure on a copy and see who receives what, and when.",
            "Use a shared mailbox, so the alert does not depend on one person."
          ]
        },
        {
          "heading": "A Zap that stops without any error",
          "body": [
            "If a Zap simply stops triggering and shows no errors, Zapier's trigger documentation suggests turning it off, waiting about a minute and turning it on again, which resets the trigger connection. It also warns that sample records shown while building can differ from live data, and that large bulk results, for example from a data migration, may be held for review rather than run at once. These explain some quiet Zaps without any fault in the Zap's steps.",
            "If you have to guess whether a Zap is healthy, compare the number of source records with the number of runs for a day you can check by hand. A gap that grows is a trigger problem, not an action problem."
          ],
          "items": []
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide helps you read what happened. It does not fix a Zap, recover records from a live replay or promise that an alert will arrive. The paid outcome for alerts hooks up a shared alert for up to five named automations and proves it with a deliberate failure on a copy. A standing service watches named automations and prepares corrected copies for you to switch on. Neither includes round-the-clock cover or a response-time guarantee."
          ],
          "items": []
        }
      ],
      "platforms": [
        "zapier"
      ],
      "incoming": [
        "collection-automation-failure-diagnosis-order",
        "guide-automation-failure-alerts-reach-someone",
        "guide-zapier-duplicate-records-five-causes",
        "platform-zapier"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/zapier-errored-halted-and-replay/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/zapier-errored-halted-and-replay/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fzapier-errored-halted-and-replay%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fzapier-errored-halted-and-replay%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-make-error-handlers-and-incomplete-executions",
      "type": "guide",
      "title": "A Make scenario stopped on one record: error handlers and incomplete executions explained",
      "description": "What Make does by default when a module fails, what the five error handlers do, why a route that only sends a message skips the error, and why a stored incomplete execution can hold back the next run.",
      "url": "/guides/make-error-handlers-and-incomplete-executions/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Make: overview of error handling",
          "url": "https://help.make.com/Overview-of-error-handling",
          "checked": "2026-10-11",
          "claims": [
            "With incomplete executions disabled, rollback is the default error handling and the scenario is deactivated after repeated failing runs.",
            "The default number of consecutive errors before deactivation is 3, and a webhook-triggered scenario is disabled immediately on an error.",
            "With incomplete executions enabled, the scenario stops and the run is stored in the Incomplete executions tab, and errors become warnings.",
            "For an account validation error, an operations-limit error or a data-size error, Make disables the scenario's scheduling immediately.",
            "An error handling route does not have to contain an error handler, and if no module outputs an error in the route, Make skips the error.",
            "The Skip handler prevents the scenario from stopping and marks the run as successful even if an error occurs.",
            "When an error creates an incomplete execution, Make postpones the next scenario run until the execution is resolved or the Retry handler resolves it automatically."
          ]
        },
        {
          "label": "Make: error handlers",
          "url": "https://help.make.com/error-handlers",
          "checked": "2026-10-11",
          "claims": [
            "The handlers are Skip, Retry, Resume, Commit and Rollback, each with a one-line purpose."
          ]
        },
        {
          "label": "Make: Retry error handler",
          "url": "https://help.make.com/retry-error-handler",
          "checked": "2026-10-11",
          "claims": [
            "The Retry handler pulls the failing bundle out of the flow, Make processes the rest of the bundles, and the error message, mappings and remaining flow are stored as an incomplete execution.",
            "Using the Retry handler requires incomplete executions to be enabled in the scenario settings."
          ]
        },
        {
          "label": "Make: incomplete executions",
          "url": "https://help.make.com/incomplete-executions",
          "checked": "2026-10-11",
          "claims": [
            "Incomplete executions are disabled by default and are enabled with Store incomplete executions in the scenario settings.",
            "Stored runs can be fixed by hand or deleted, and the number stored is capped by the plan."
          ]
        },
        {
          "label": "Make: scenario settings",
          "url": "https://help.make.com/scenario-settings",
          "checked": "2026-10-11",
          "claims": [
            "When processing in order is on, unresolved incomplete executions hold back new runs.",
            "With Keep data confidential on, Make does not keep the processed data and there are very limited options to solve errors."
          ]
        }
      ],
      "related": [
        "make-scenario-stops-on-one-bad-record",
        "platform-make",
        "auto-failure-alerts-reach-a-named-owner",
        "guide-automation-failure-alerts-reach-someone",
        "guide-api-polling-checkpoint-overlap-keys",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "What happens when a module fails and nothing is set up",
          "body": [
            "Make's overview says that with incomplete executions disabled, rollback is the default error handling if you set none. Rollback stops the run, reverts changes in modules that support transactions, and ends with an error status. The scenario is then deactivated after repeated failing runs; the page gives the default as three consecutive errors, and says a scenario triggered by a webhook is disabled immediately on an error. So one malformed record can stop everything behind it and then switch the scenario off.",
            "The same page says some errors skip the consecutive-errors count: for an account validation error, an operations-limit error or a data-size error, Make disables the scenario's scheduling immediately. A handler on one module cannot repair a lost connection or an exhausted plan, and those need the account holder."
          ],
          "items": [
            "Look at the scenario history for the failed run and read the error on the module that failed.",
            "Check how many failed runs came before it was deactivated.",
            "Check whether the scenario starts from a schedule or from a webhook, because the rules differ."
          ]
        },
        {
          "heading": "The five error handlers in plain words",
          "body": [
            "Make's help centre lists five handlers that you attach to a module through an error route. Skip disregards the error and lets the scenario process later bundles. Retry stores an incomplete execution and enables automatic or manual retries. Resume sets a substitute value for the failed module and carries on. Commit stops the run and saves the changes already processed. Rollback stops the run and reverts them. Older material may use different names for some of these, so match the behaviour, not only the label.",
            "An error route does not have to contain a handler, and Make says that if no module in the route outputs an error, it skips the error. That matters for alerts: a route that only holds a message module, such as a Slack message, tells you about the error but then ends without a directive, so by Make's own wording the error is skipped. Make says the Skip handler marks the run as successful, so run history may not show the error as a failure. Confirm what your route does with a deliberate failure on a copy, and end every error route with the directive you want."
          ],
          "items": [
            "Skip: the bad record is dropped and the rest continue; the run counts as successful.",
            "Retry: the bad record is stored and can be retried or fixed; the rest of the bundles are processed.",
            "Resume: a stand-in value is used and the run carries on, so choose a value that cannot do harm.",
            "Commit and Rollback: stop the run, keeping or undoing the work done so far in modules that support it.",
            "A route with only a message module ends the error as a skip by Make's wording, so add the directive you mean and confirm it on a copy."
          ]
        },
        {
          "heading": "Incomplete executions: the part people miss",
          "body": [
            "Incomplete executions are switched off by default. You turn them on with Store incomplete executions in the scenario settings. With them on, a failed run is stored in the Incomplete executions tab so a person can fix the data and resume it, and Make describes errors as turning into warnings. Stored runs can be fixed by hand or deleted, and the number you can hold is capped by your plan.",
            "There is a trade-off, and it is larger than it first looks. Make's overview page says that when an error creates an incomplete execution, Make postpones the next scenario run until you resolve the stored execution, or until the Retry handler resolves it automatically. The scenario settings page describes the same hold under Process data in order. So a stored bad record can make the next scheduled run wait, not only the records behind it, and the wait ends when someone resolves the record or automatic retries succeed. That is the right behaviour when order matters, and a silent stall when nobody is watching. Pair a Retry-style route with an alert, name who resolves held records, and test a second scheduled run on a copy before you rely on it, because the effect on your own scenario is what counts."
          ],
          "items": [
            "Decide whether order matters for this scenario before you choose the setting.",
            "Add an alert module on the error route, followed by the directive, so a stored record is noticed.",
            "On a copy fed by a disposable source, hold one record and then run again to see whether the next run waits.",
            "Remember that storing data counts against your plan's storage."
          ]
        },
        {
          "heading": "Choosing a handler for a record you cannot lose",
          "body": [
            "If a record represents money, a customer or a legal notice, losing it quietly is worse than a pause. Holding it with an alert is the safer choice when a pause in later runs is acceptable, because a person can fix the record and resume. Skipping is reasonable only where dropping the record is acceptable and still reported. Resume with a substitute value is reasonable only where the substitute cannot create a wrong record downstream. Commit and Rollback matter when a module writes to an app that supports transactions; for apps without them, a partial write cannot be undone.",
            "One setting rules out most of this. If Keep data confidential is on, Make does not keep the processed data, and its page warns that there are very limited options to solve errors. You cannot inspect or resume a record that was never kept."
          ],
          "items": []
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide does not repair a connection, restore an expired authorisation or buy more operations. Those belong to the account holder. It also does not move a scenario between platforms. The paid outcome for this problem is one scenario with one failing module: an error route with the directive you choose, an alert to a named person, and tests on a copy fed by a disposable source. It is accepted when the good records in a batch are written once, the bad one is held or skipped as agreed with its reason visible, the scenario stays scheduled, and a second run is tested and described, including whether a held record made it wait."
          ],
          "items": []
        }
      ],
      "platforms": [
        "make"
      ],
      "incoming": [
        "collection-automation-failure-diagnosis-order",
        "guide-api-polling-checkpoint-overlap-keys",
        "guide-automation-failure-alerts-reach-someone",
        "platform-make"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/make-error-handlers-and-incomplete-executions/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/make-error-handlers-and-incomplete-executions/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fmake-error-handlers-and-incomplete-executions%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fmake-error-handlers-and-incomplete-executions%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-scheduled-report-time-zone-and-period",
      "type": "guide",
      "title": "A scheduled report arrived at the wrong time or covered the wrong days",
      "description": "Find out which clock your automation tool uses, fix the reporting period in one place, and test across a clock change instead of assuming.",
      "url": "/guides/scheduled-report-time-zone-and-period/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Zapier: schedule Zap workflows",
          "url": "https://help.zapier.com/hc/en-us/articles/8496288648461-Schedule-Zap-workflows-to-run-at-specific-intervals",
          "checked": "2026-10-11",
          "claims": [
            "Schedule triggers use the time zone set in the Zapier account, not the time zone set in the Zap.",
            "Run times in Zap history display in UTC and Zapier does not guarantee the exact minute.",
            "After changing the account time zone the Zap must be turned off and on for the change to take effect."
          ]
        },
        {
          "label": "Make: manage time zones",
          "url": "https://help.make.com/manage-time-zones",
          "checked": "2026-10-11",
          "claims": [
            "The organization time zone defines the time used when executing scenarios; the user time zone only changes how time is displayed.",
            "Only organization owners and admins can edit the organization's time zone, and date functions default to it."
          ]
        },
        {
          "label": "GOV.UK: when do the clocks change",
          "url": "https://www.gov.uk/when-do-the-clocks-change",
          "checked": "2026-10-11",
          "claims": [
            "In 2026 UK clocks go forward on 29 March and back on 25 October, at 1am and 2am respectively on those Sundays."
          ]
        }
      ],
      "related": [
        "auto-failure-alerts-reach-a-named-owner",
        "platform-zapier",
        "platform-make",
        "guide-automation-failure-alerts-reach-someone",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "Whose clock runs the schedule",
          "body": [
            "Two settings are easy to confuse: the clock that decides when a scheduled automation runs, and the clock that decides how times are shown to you. Zapier's schedule article says a schedule trigger uses the time zone set in your Zapier account, not the one set in the Zap, and that run times in Zap history display in UTC. After you change the account time zone you must turn the Zap off and on for it to take effect. Zapier also says it does not guarantee the precise minute; a run should land within a few minutes of the chosen time.",
            "Make works differently. Its help centre says the organisation's time zone defines the time used when scenarios execute, while a user's time zone only changes how times appear in logs. A schedule of 4pm means 4pm in the organisation's zone, whichever country the person who built it sits in, and only organisation owners and admins can change it. Date functions that omit a time zone use the organisation's zone too."
          ],
          "items": [
            "Find the account or organisation time zone and write it next to the schedule.",
            "Do not read the history's timestamps as local time until you know which zone they use.",
            "Ask who is allowed to change the setting and who would notice."
          ]
        },
        {
          "heading": "Clock changes: test, do not assume",
          "body": [
            "On GOV.UK, UK clocks go forward at 1am on 29 March 2026 and back at 2am on 25 October 2026. On the first date one local hour does not exist, and on the second one occurs twice, so by simple arithmetic those days are 23 and 25 hours long. The two tool pages checked for this guide do not describe how a daily or weekly schedule behaves across a clock change, so this guide does not claim to know.",
            "The safe approach is to test: put a copy of the schedule on a test destination and look at what runs on and around the change, or avoid the affected hours altogether."
          ],
          "items": [
            "Avoid scheduling anything between about 01:00 and 03:00 local time on a change date.",
            "Write down, for each report, whether it should follow local clock time or a fixed offset.",
            "Check the first report after each change by hand."
          ]
        },
        {
          "heading": "Define the reporting period once",
          "body": [
            "A report that covers the wrong days usually has a loose definition of its period. 'The last seven days' counted from the moment the job runs shifts if the job is late, and a period ending at 23:59 leaves out anything stamped in the last minute. The clean rule is a half-open window: the period starts at one named instant and ends at another, the start is included and the end is not, and both are computed once, in one named time zone, then reused by every step. Records stored in UTC must be converted before they are assigned to a local day, otherwise an evening sale lands in the wrong day."
          ],
          "items": [
            "Name the report week, for example Monday 00:00 to the next Monday 00:00 in Europe/London.",
            "Compute the two instants in one step and pass them to every later step.",
            "Include a count of records and the window itself in the report's footer so a reader can see what it covered."
          ],
          "code": "Report week     : Monday 2026-10-05 00:00 to Monday 2026-10-12 00:00, Europe/London\nRule            : start included, end excluded\nRun             : Monday 2026-10-12 08:00 local, 8 hours after the window closed\nSale at 23:59   : counted in the week that ends\nSale at 00:00   : counted in the next week\nFooter shows    : window start, window end, 214 records (synthetic count)"
        },
        {
          "heading": "Make a missing report visible",
          "body": [
            "A report that does not arrive produces no message, so nobody knows. Add a check that does not depend on the report job itself: a second step or tool that expects the report by a set time and tells a named person if it has not arrived. Keep it simple, and keep it separate from the job so that one failure cannot silence both. The alert needs a shared recipient, not one person's inbox, and it should name the report and the time it was due."
          ],
          "items": []
        },
        {
          "heading": "What this guide does not cover",
          "body": [
            "This guide does not check that the numbers in a report are right, or give accounting or tax advice. The paid outcome for alerts gives up to five named automations a shared failure alert with two named people, and is accepted when a deliberate failure on a copy of each reaches the shared place and both people confirm receipt. A standing service watches named automations each week and prepares corrected copies for you to switch on. Neither guarantees delivery time or round-the-clock cover."
          ],
          "items": []
        }
      ],
      "platforms": [
        "zapier",
        "make"
      ],
      "incoming": [
        "platform-zapier",
        "platform-make"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/scheduled-report-time-zone-and-period/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/scheduled-report-time-zone-and-period/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fscheduled-report-time-zone-and-period%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fscheduled-report-time-zone-and-period%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-n8n-webhook-test-and-production-urls",
      "type": "guide",
      "title": "An n8n webhook works while you watch but not afterwards: test and production URLs",
      "description": "The Webhook node has two addresses with different rules. See which one your sender uses, how to read production runs, and how to be told when one fails.",
      "url": "/guides/n8n-webhook-test-and-production-urls/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "n8n: Webhook node",
          "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/",
          "checked": "2026-10-11",
          "claims": [
            "The node has a Test URL and a Production URL; the test URL is registered while listening for a test event and the production URL when the workflow is published.",
            "Incoming data on the production URL is not shown in the editor; it appears under the workflow's Executions tab.",
            "Respond options are immediately, when the last node finishes, or with a Respond to Webhook node.",
            "Authentication options listed are Basic, Header and JWT, plus an IP allowlist; the maximum payload is 16MB."
          ]
        },
        {
          "label": "GitHub: best practices for using webhooks",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/best-practices-for-using-webhooks",
          "checked": "2026-10-11",
          "claims": [
            "GitHub expects a 2XX response within 10 seconds of receiving a webhook delivery."
          ]
        },
        {
          "label": "Shopify: HTTPS webhook subscriptions",
          "url": "https://shopify.dev/docs/apps/build/webhooks/subscribe/https",
          "checked": "2026-10-11",
          "claims": [
            "Shopify documents a one-second connection timeout and a five-second timeout for the entire request."
          ]
        },
        {
          "label": "n8n: error handling",
          "url": "https://docs.n8n.io/flow-logic/error-handling/",
          "checked": "2026-10-11",
          "claims": [
            "An error workflow starts with the Error Trigger node and runs when an execution fails, and it can send an email or Slack alert.",
            "The Error Trigger receives execution and workflow details; execution.id and execution.url need the execution to be saved."
          ]
        }
      ],
      "related": [
        "webhook-receiver-verified-signature-duplicate-safe",
        "auto-failure-alerts-reach-a-named-owner",
        "guide-webhook-signature-raw-body",
        "guide-webhook-timeouts-retries-missed-events",
        "guide-automation-failure-alerts-reach-someone",
        "collection-automation-failure-diagnosis-order"
      ],
      "sections": [
        {
          "heading": "Two addresses, two states",
          "body": [
            "The n8n Webhook node gives you two URLs, and they are registered at different times. The documentation says the Test URL is registered when you click Listen for Test Event, or run the workflow while it is not active, and that data sent to it appears in the editor. The Production URL is registered when the workflow is published. The page checked uses the word published, so check what your n8n version calls it. The editor does not show incoming data for the production URL. To see it you open the workflow's Executions tab and pick a run.",
            "The most common mistake follows directly. A sender that was set up with the test URL works only while the editor is waiting, and silently has nowhere to deliver after you close it. A sender set up with the production URL before the workflow is published has nothing registered to receive it."
          ],
          "items": [
            "Copy the URL from the production tab for any sender that must keep working.",
            "Publish the workflow before pointing the sender at the production address.",
            "Look for runs in the Executions tab, not in the editor canvas."
          ]
        },
        {
          "heading": "A short checklist for a webhook that does nothing",
          "body": [
            "Work from the sender towards n8n. First, which URL did the sender store? A test URL stored in a live tool is the usual cause. Second, is the workflow published, and does the Executions tab show a run for the time you sent the event? Third, what does the sender's own delivery log say about the response it received? If your sender keeps a delivery log, its status code and timing are better evidence than any guess about n8n.",
            "If there is no execution and the sender reports success, the request may have gone to a different workflow or a different n8n instance. If there is an execution that failed, read its error before changing anything."
          ],
          "items": [
            "Check the HTTP method set on the node matches what the sender sends.",
            "Check the path on the node matches the path the sender stored.",
            "Check there is only one n8n instance and one copy of the workflow receiving the traffic."
          ]
        },
        {
          "heading": "What the sender sees: respond options",
          "body": [
            "The node's response option decides what the sender is told and when. Responding immediately sends a response code and a short message right away. Responding when the last node finishes returns the output of the final node, so the sender waits for the whole workflow. A Respond to Webhook node lets you shape the reply. A sender with a short time limit will count a slow workflow as a failure and may retry, which then runs the workflow twice. GitHub documents a 10-second limit and Shopify a 5-second request timeout. Answer quickly and do the long work afterwards."
          ],
          "items": [
            "Choose immediate response for senders with short deadlines.",
            "Expect retries if the sender saw a slow answer, and make the workflow safe to repeat.",
            "Read the sender's own documentation for its time limit."
          ]
        },
        {
          "heading": "Authentication, signatures and size",
          "body": [
            "The authentication choices the node page lists are Basic, Header and JWT, with an IP allowlist that returns an error to other callers; the maximum payload is 16MB, adjustable on a self-hosted install by an environment variable. An HMAC signature computed over the request body, the scheme many senders use, is not among the options listed on the page checked. Verifying it needs an extra step that works on the exact bytes received, and that step is easy to get wrong. The signature guide explains why."
          ],
          "items": []
        },
        {
          "heading": "Being told when it fails",
          "body": [
            "n8n does not tell anyone about a failed execution unless you set up an error workflow. The documentation says an error workflow must start with the Error Trigger node, you choose it in the failing workflow's settings, and it can send an email or a Slack message. The trigger receives the execution and workflow details; the execution ID and URL are available only if the execution is saved. Build it once, share it between workflows, and test it with a deliberate failure.",
            "This guide does not build a signature-checking receiver or tell you how to host n8n. The paid outcomes cover a verified, repeat-safe receiver built as code you control, accepted by tests with altered, unsigned and repeated deliveries, and failure alerts for up to five named automations on one platform, accepted when a deliberate failure on a copy reaches a shared place and two named people."
          ],
          "items": []
        }
      ],
      "incoming": [
        "collection-automation-failure-diagnosis-order",
        "guide-automation-failure-alerts-reach-someone",
        "guide-webhook-signature-raw-body",
        "guide-webhook-timeouts-retries-missed-events"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/n8n-webhook-test-and-production-urls/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/n8n-webhook-test-and-production-urls/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fn8n-webhook-test-and-production-urls%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fn8n-webhook-test-and-production-urls%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "platform-zapier",
      "type": "platform",
      "title": "Zapier automations: what repeats, what stops and who is told",
      "description": "The documented behaviours behind duplicate records, switched-off Zaps, late alerts and scheduled runs, and which outcome fits each problem.",
      "url": "/platforms/zapier/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Zapier: how Zapier handles duplicate data",
          "url": "https://help.zapier.com/hc/en-us/articles/8496260269965-How-Zapier-handles-duplicate-data-in-Zap-workflows",
          "checked": "2026-10-11",
          "claims": [
            "De-duplication of polling triggers checks only within one Zap, and an action on an app that allows duplicates creates a duplicate record."
          ]
        },
        {
          "label": "Zapier: troubleshoot errors in Zap workflows",
          "url": "https://help.zapier.com/hc/en-us/articles/8496037690637-How-to-troubleshoot-errors-in-Zap-workflows",
          "checked": "2026-10-11",
          "claims": [
            "A Zap turns off automatically if 95% of its runs result in errors in the last 7 days; halted runs do not count."
          ]
        },
        {
          "label": "Zapier: what is replay",
          "url": "https://help.zapier.com/hc/en-us/articles/19220226086797-What-is-replay",
          "checked": "2026-10-11",
          "claims": [
            "Replay from history retries only errored steps, replay from the editor re-runs every step, and error emails wait for the final autoreplay attempt."
          ]
        },
        {
          "label": "Zapier: schedule Zap workflows",
          "url": "https://help.zapier.com/hc/en-us/articles/8496288648461-Schedule-Zap-workflows-to-run-at-specific-intervals",
          "checked": "2026-10-11",
          "claims": [
            "Schedule triggers use the Zapier account's time zone, not the Zap's."
          ]
        }
      ],
      "related": [
        "guide-zapier-duplicate-records-five-causes",
        "guide-zapier-errored-halted-and-replay",
        "guide-scheduled-report-time-zone-and-period",
        "guide-zapier-field-mapping",
        "zapier-facebook-leads-to-salesforce",
        "zapier-sheets-to-xero-missing-columns",
        "auto-failure-alerts-reach-a-named-owner"
      ],
      "sections": [
        {
          "heading": "Four things Zapier does that surprise people",
          "body": [
            "A Zap looks simple, but several documented behaviours decide whether it can be trusted. De-duplication for polling triggers compares item IDs, but only inside one Zap, so two Zaps on one trigger both fire, and it does not stop an action creating a second record. A Zap that errors on 95% of its runs over seven days is switched off by Zapier. Replay comes in two kinds: from history it repeats only errored steps, from the editor it repeats every step. And scheduled Zaps follow the account's time zone, not the Zap's."
          ],
          "items": []
        },
        {
          "heading": "Failure is quiet unless someone arranges it",
          "body": [
            "With Autoreplay on, error emails and Zapier Manager triggers wait until the final attempt fails, so the first person to hear may hear about ten and a half hours late. A custom error handler turns the standard emails off. Halted runs, which are deliberate stops often caused by a search finding nothing, are described separately from errors and do not switch the Zap off, so they are easy to overlook. If an important Zap matters daily, someone has to decide who is told, test it with a deliberate failure on a copy, and look at run history from time to time."
          ],
          "items": []
        },
        {
          "heading": "Choose the outcome that matches the symptom",
          "body": [
            "Records appear twice: the duplicate-records outcome examines one Zap and one create step. A Facebook Lead Ads Zap shows a sample lead but no live ones: the existing Facebook-to-Salesforce outcome. Fields go missing on the way to Xero: the existing Sheets-to-Xero mapping outcome. Nobody is told when Zaps fail: the failure-alerts outcome covers up to five named automations. Zaps fail now and then and nobody has time to watch: the standing monitor-and-repair service. Each is separate, with its own test and its own boundaries."
          ],
          "items": [
            "Duplicate records: one Zap, one create step, three synthetic sends and a replay.",
            "Alerts: a deliberate failure on a copy reaches a shared place and two named people.",
            "Standing monitoring: weekly review and up to two repairs a month, no response-time promise."
          ]
        },
        {
          "heading": "Safe first contact",
          "body": [
            "Describe the Zap's trigger and destination, what you see and when it started. Do not send passwords, API keys, customer lists, invoices or an account invitation. Fit, scope and price are confirmed in writing before any access, and we work on a copy with synthetic data. Every price on the linked outcome pages is an untested proposal, and payment for fixed jobs follows the agreed checks and your sign-off."
          ],
          "items": []
        }
      ],
      "incoming": [
        "guide-automation-failure-alerts-reach-someone",
        "guide-zapier-duplicate-records-five-causes",
        "guide-zapier-errored-halted-and-replay",
        "guide-scheduled-report-time-zone-and-period"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/platforms/zapier/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/platforms/zapier/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fplatforms%2Fzapier%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fplatforms%2Fzapier%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "platform-make",
      "type": "platform",
      "title": "Make scenarios: default failure behaviour, error routes and who is told",
      "description": "What Make does when a module fails, how error handlers and incomplete executions change it, and which outcomes cover one bad record or missing alerts.",
      "url": "/platforms/make/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Make: overview of error handling",
          "url": "https://help.make.com/Overview-of-error-handling",
          "checked": "2026-10-11",
          "claims": [
            "Rollback is the default when no handler is set and incomplete executions are off; a scenario is deactivated after the configured consecutive errors, three by default, and a webhook-triggered scenario is disabled immediately.",
            "An error route does not have to contain a handler, and if no module outputs an error in it Make skips the error; the Skip handler marks the run as successful.",
            "When an error creates an incomplete execution, Make postpones the next scenario run until it is resolved or the Retry handler resolves it automatically."
          ]
        },
        {
          "label": "Make: incomplete executions",
          "url": "https://help.make.com/incomplete-executions",
          "checked": "2026-10-11",
          "claims": [
            "Incomplete executions are off by default and enabled with Store incomplete executions."
          ]
        },
        {
          "label": "Make: manage time zones",
          "url": "https://help.make.com/manage-time-zones",
          "checked": "2026-10-11",
          "claims": [
            "Scheduled scenarios run in the organization's time zone, and the user time zone only changes display."
          ]
        }
      ],
      "related": [
        "guide-make-error-handlers-and-incomplete-executions",
        "guide-scheduled-report-time-zone-and-period",
        "guide-api-polling-checkpoint-overlap-keys",
        "make-scenario-stops-on-one-bad-record",
        "auto-failure-alerts-reach-a-named-owner"
      ],
      "sections": [
        {
          "heading": "A failing module can stop everything behind it",
          "body": [
            "Make's default, with no error handling and incomplete executions off, is to roll back: stop the run, revert changes where modules support it and end with an error status. After repeated failing runs, three in a row by default, the scenario is deactivated, and one triggered by a webhook is disabled at once. A single malformed record can therefore halt a whole schedule and then switch it off. Nothing in that behaviour tells a person unless an alert has been added."
          ],
          "items": []
        },
        {
          "heading": "Error routes, handlers and stored records",
          "body": [
            "Make lets you attach an error route to a module with one of five handlers: skip, retry, resume, commit or rollback. Turning on Store incomplete executions keeps a failed run so it can be fixed and resumed, and Make says errors then become warnings. The trade-off is that, as Make documents, when an error creates a stored incomplete execution the next scenario run is postponed until that record is resolved or the Retry handler resolves it. A held record therefore needs an alert and an owner. A route that only sends a message and has no handler ends with Make skipping the error, so add the directive you mean. Choose the handler by what losing a record, or pausing later runs, would cost."
          ],
          "items": []
        },
        {
          "heading": "Whose clock, and what Make cannot fix",
          "body": [
            "Scheduled scenarios run in the organisation's time zone, set by an owner or admin; a user's time zone only changes how times are displayed. An error route does not repair a lost connection or an exhausted plan, which need the account holder. If a scenario depends on an API that must not miss or repeat records, the job is also about checkpoints and keys, which the polling guide explains."
          ],
          "items": []
        },
        {
          "heading": "Choose the outcome that matches the symptom",
          "body": [
            "A scenario halts on one record and later records wait: the outcome for one scenario and one failing module adds an error route with a directive you choose, an alert, and tests on a copy with a deliberately bad record and a second run. Nobody is told when scenarios fail: the failure-alerts outcome covers up to five named automations. Scenarios fail now and then and nobody watches: the standing monitor-and-repair service. We work on copies fed by disposable data, you switch changes on, and no outcome promises round-the-clock cover or a response time."
          ],
          "items": []
        }
      ],
      "incoming": [
        "guide-automation-failure-alerts-reach-someone",
        "guide-make-error-handlers-and-incomplete-executions",
        "guide-scheduled-report-time-zone-and-period"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/platforms/make/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/platforms/make/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fplatforms%2Fmake%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fplatforms%2Fmake%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "platform-airtable",
      "type": "platform",
      "title": "Airtable bases: automations that fail or skip records, and tables that repeat themselves",
      "description": "Documented causes of failed and skipped automation runs, limits that count failed runs, and how linked records match, with the outcomes that fit each.",
      "url": "/platforms/airtable/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Airtable: troubleshooting automations",
          "url": "https://support.airtable.com/articles/6756755850-troubleshooting-airtable-automations",
          "checked": "2026-10-11",
          "claims": [
            "Automation history shows run statuses, a rerun uses the original configuration, records already matching when an automation is switched on do not trigger it, and failure emails go to the last user who turned it on."
          ]
        },
        {
          "label": "Airtable: getting started with automations",
          "url": "https://support.airtable.com/docs/getting-started-with-airtable-automations",
          "checked": "2026-10-11",
          "claims": [
            "Failed and successful runs both count against a workspace's monthly run allowance, and turning an automation off does not free its slot."
          ]
        },
        {
          "label": "Airtable: converting existing fields to linked records",
          "url": "https://support.airtable.com/docs/converting-existing-fields-to-linked-records",
          "checked": "2026-10-11",
          "claims": [
            "Linked record matching uses the linked table's primary field, exactly, and unmatched values create new records."
          ]
        }
      ],
      "related": [
        "guide-airtable-automation-failed-runs-and-limits",
        "guide-airtable-split-table-linked-records",
        "guide-automation-failure-alerts-reach-someone",
        "airtable-automation-fails-or-skips-records",
        "airtable-wide-table-to-linked-tables",
        "auto-failure-alerts-reach-a-named-owner"
      ],
      "sections": [
        {
          "heading": "Two kinds of trouble in an Airtable base",
          "body": [
            "Airtable problems tend to fall into two groups. The first is an automation that fails, or does not run for some records. The second is a table whose structure has outgrown its purpose, typically one that repeats a customer or supplier on every row. They need different jobs, although the second often exposes the first, because automations and lookups that depend on the old columns break when the structure changes."
          ],
          "items": []
        },
        {
          "heading": "Why automations fail or skip records",
          "body": [
            "Airtable's troubleshooting article shows that triggers fire on a change, not on a state: records already matching when an automation is switched on do not trigger it. Action inputs that resolve to nothing, for example an empty linked record in a recipient field, fail the run. Scripts have a 30-second network timeout. Runs are metered per workspace each month and failed runs count, and a switched-off automation still takes a slot against the base's cap. The person who last turned an automation on receives the failure email, or the workspace owners if they have left."
          ],
          "items": []
        },
        {
          "heading": "Why restructuring needs care",
          "body": [
            "Airtable matches text to linked records by the linked table's primary field, exactly, so spelling variants create extra records. Commas split values into several links, a formula primary field blocks record creation, and deleting a linked field leaves the other side as text and breaks lookups and automations that used it. The safe route is a duplicate base, an approved key list, reconciled counts, and the old columns kept until every dependency is repaired."
          ],
          "items": []
        },
        {
          "heading": "Choose the outcome that matches the symptom",
          "body": [
            "One automation fails or skips records: the outcome for one automation repairs it on a duplicate base and proves three cases and a failing one with an alert. One repetitive table needs splitting: the outcome for one table into a parent and a child, with a reconciliation sheet. Nobody is told when automations fail: the failure-alerts outcome. Work happens on a duplicate base, you decide when your team moves onto the duplicate, with a cut-over note and a check for records added or deleted since the snapshot, and we ask for no record exports, passwords or invitations in a first enquiry."
          ],
          "items": []
        }
      ],
      "incoming": [
        "guide-airtable-automation-failed-runs-and-limits",
        "guide-airtable-split-table-linked-records",
        "guide-automation-failure-alerts-reach-someone"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/platforms/airtable/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/platforms/airtable/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fplatforms%2Fairtable%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fplatforms%2Fairtable%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "platform-hubspot",
      "type": "platform",
      "title": "HubSpot lead flow: duplicates, owners and the Salesforce sync",
      "description": "How HubSpot matches contacts and companies, assigns owners, and syncs mapped fields with Salesforce, and which outcomes fix duplicates, routing and field reverts.",
      "url": "/platforms/hubspot/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "HubSpot: deduplicate records",
          "url": "https://knowledge.hubspot.com/records/deduplication-of-records",
          "checked": "2026-10-11",
          "claims": [
            "Contacts are de-duplicated by email and companies by domain, and companies created through the API are not de-duplicated by domain."
          ]
        },
        {
          "label": "HubSpot: use non-HubSpot forms",
          "url": "https://knowledge.hubspot.com/forms/use-non-hubspot-forms",
          "checked": "2026-10-11",
          "claims": [
            "For non-HubSpot (collected external) forms, all submissions associated with the same cookie are added to the same contact record, even with different email addresses, and the HubSpot tracking code must load for a submission to be collected."
          ]
        },
        {
          "label": "HubSpot: how HubSpot tracks visitors",
          "url": "https://knowledge.hubspot.com/account/how-does-hubspot-track-visitors",
          "checked": "2026-10-11",
          "claims": [
            "A form with no email field identifies submissions by browser cookie, and this cannot be changed."
          ]
        },
        {
          "label": "HubSpot: assign and rotate record owners using workflows",
          "url": "https://knowledge.hubspot.com/workflows/assign-and-rotate-record-owners-using-workflows",
          "checked": "2026-10-11",
          "claims": [
            "The rotate-to-owner action needs a Sales Hub or Service Hub Professional or Enterprise subscription; for most record types its assignment counts reset when owners are added or removed, but the page says this does not apply to lead-based or ticket-based workflows; and records can be left unassigned."
          ]
        },
        {
          "label": "HubSpot: map properties to Salesforce fields",
          "url": "https://knowledge.hubspot.com/salesforce/map-hubspot-properties-to-salesforce-fields",
          "checked": "2026-10-11",
          "claims": [
            "Mapped fields use one of four sync rules, and the first sync for a contact with no history uses the current Salesforce value as the baseline."
          ]
        }
      ],
      "related": [
        "guide-hubspot-form-duplicates-contacts-companies",
        "guide-hubspot-lead-owner-rotation-fallbacks",
        "guide-hubspot-salesforce-sync-rules-and-errors",
        "crm-duplicate-contacts-from-web-forms",
        "lead-routing-new-leads-unassigned-or-wrong-owner",
        "hubspot-salesforce-synced-field-reverts",
        "crm-web-lead-pipeline-repair"
      ],
      "sections": [
        {
          "heading": "A web lead passes four stages",
          "body": [
            "A lead reaches a salesperson by way of four stages: capture, when the form or integration creates or updates a contact and company; matching, when HubSpot decides whether this is someone it already knows; ownership, when a workflow assigns an owner; and sync, when mapped fields cross to Salesforce. Each stage can be correct alone and still lose or duplicate the lead in combination, which is why problems tend to appear in pairs."
          ],
          "items": []
        },
        {
          "heading": "How HubSpot decides who is who",
          "body": [
            "HubSpot matches contacts by email and companies by domain. Which route a lead takes matters. A form with no email field is matched by browser cookie. A form on your own site that HubSpot collects through its tracking code depends on that code loading, and for such a collected external form HubSpot adds all submissions from one browser to one contact, even with different email addresses. Companies created through the API are not de-duplicated by domain. Merged contacts cannot be unmerged, so merging is not a safe way to find out how a form behaves."
          ],
          "items": []
        },
        {
          "heading": "Owners and sync have their own rules",
          "body": [
            "Rotate-to-owner needs a Professional or Enterprise subscription. For contact records and most other types it spreads records by per-action counts that reset when owners change, skips deactivated users and can leave records unassigned. For leads in HubSpot's separate Leads tool (marked BETA) and for tickets, HubSpot describes other distribution options and an Away setting instead, and says the reset rules do not apply. If the owner also syncs to Salesforce, HubSpot warns that Salesforce may put the previous owner back. Mapped Salesforce fields each have a sync rule that decides which system wins, so a value that keeps reverting is often the rule doing its job. First syncs, picklist mismatches and field permissions explain much of the rest."
          ],
          "items": []
        },
        {
          "heading": "Choose the outcome that matches the symptom",
          "body": [
            "Duplicate contacts or companies from a web form: the outcome that changes one HubSpot form or API integration path. Leads with no owner or the wrong owner: the outcome that rebuilds one contact-based assignment workflow from your rule table. One field changes back or never arrives: the outcome for one mapped field pair. Several of these at once: the project that tests up to three form paths end to end. Keeping it true afterwards: the standing weekly check. We use synthetic leads and ask for no contact lists, exports or passwords in a first enquiry."
          ],
          "items": []
        }
      ],
      "incoming": [
        "guide-hubspot-form-duplicates-contacts-companies",
        "guide-hubspot-lead-owner-rotation-fallbacks",
        "guide-hubspot-salesforce-sync-rules-and-errors"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/platforms/hubspot/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/platforms/hubspot/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fplatforms%2Fhubspot%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fplatforms%2Fhubspot%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-etl-erp-export-numbers-and-dates-to-import-format",
      "type": "guide",
      "title": "Converting an ERP export for a store import: decimal commas, day-first dates, codes and rejects",
      "description": "Why hand-editing an ERP export produces different files on different days, and how explicit number, date and code rules, exact decimal arithmetic and a reject file make the conversion repeatable.",
      "url": "/guides/etl-erp-export-numbers-and-dates-to-import-format/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "csv-files"
      ],
      "buyer_role": "Operations manager or owner who reformats an ERP stock or price export by hand before each webshop import",
      "purchase_trigger": "Prices, dates or codes are wrong in the store only on days when a column held an unusual value",
      "problem": "An ERP export uses its own conventions, and a manual or macro conversion applies them inconsistently.",
      "related": [
        "spreadsheet-normalise-mixed-dates-and-numbers"
      ],
      "provenance": [
        {
          "label": "Python decimal documentation",
          "url": "https://docs.python.org/3/library/decimal.html",
          "checked": "2026-10-11",
          "claims": [
            "Decimal numbers can be represented exactly; Decimal(3.14) built from a float carries the float's long binary expansion, while Decimal('3.14') built from a string is 3.14; quantize rounds to a fixed exponent; the default context rounding is ROUND_HALF_EVEN, and a malformed string raises InvalidOperation under the default traps.",
            "The documented grammar uses a dot as the decimal point only."
          ]
        },
        {
          "label": "Python locale documentation",
          "url": "https://docs.python.org/3/library/locale.html",
          "checked": "2026-10-11",
          "claims": [
            "setlocale is process-wide and not thread-safe on most systems; locale names available depend on the platform; calling it from library code is discouraged."
          ]
        },
        {
          "label": "Python datetime documentation",
          "url": "https://docs.python.org/3/library/datetime.html",
          "checked": "2026-10-11",
          "claims": [
            "strptime parses a date with an explicit format and raises ValueError on a mismatch; fromisoformat reads ISO 8601 dates."
          ]
        },
        {
          "label": "W3C Metadata Vocabulary for Tabular Data",
          "url": "https://www.w3.org/TR/tabular-metadata/",
          "checked": "2026-10-11",
          "claims": [
            "A column can declare a date format such as dd/MM/yyyy and a null marker, so the rule is stated per column rather than assumed."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Why the same export gives different files",
          "body": [
            "ERP systems export in their own conventions. A price may be written 1.234,56 with a decimal comma and a thousands dot, a negative stock figure as 15- with the sign at the end, a date as 03/04/2026 meaning 3 April, a product code as 00123 with leading zeros, a status as a single letter. A person converting by hand corrects these by feel and differently on different days. A one-off spreadsheet macro corrects the cases its author saw. The result is an import file that changes with who prepared it and with the particular values that day."
          ],
          "items": []
        },
        {
          "heading": "Write down a rule for every column",
          "body": [
            "Treat the conversion as a table with one row per column: its type, its parse rule, what counts as invalid and what the output looks like. Text codes stay text, so 00123 is never turned into a number. Numbers are parsed with an explicit rule for the decimal and thousands marks. Dates are parsed with an explicit format, never guessed, because 03/04/2026 is valid in both orders. Status codes go through a mapping table, and an unknown code is a reject, not a default. The W3C tabular vocabulary takes the same approach, letting a column declare its date format and its null marker instead of leaving them to the reader."
          ],
          "items": [
            "Include dates where day and month are both 12 or lower in the test data; they expose the wrong order.",
            "Decide in writing whether blank means unchanged, zero or invalid."
          ]
        },
        {
          "heading": "Exact decimal arithmetic, and explicit separators",
          "body": [
            "Money should not pass through binary floating point. Python's decimal documentation says decimal numbers can be represented exactly, and shows that Decimal(3.14) built from a float carries the float's binary expansion, 3.140000000000000124 and more digits, while Decimal('3.14') built from a string is exactly 3.14. That is why we build them from strings and not floats. The documentation also describes quantize for rounding to a fixed number of places and gives half-even, which sends ties to the nearest even digit, as the default rounding. That may not be the rule your accountant expects, so state the rounding mode explicitly and test a tie such as 2.665: half-even gives 2.66 and half-up gives 2.67, as computed with Python's decimal module.",
            "Decimal accepts only a dot as the decimal point in its documented grammar, so a decimal-comma price needs an explicit conversion step before it is parsed. Do not rely on the server's locale for that. Python's locale documentation says the setting is process-wide, not thread-safe on most systems, and available locale names vary by platform, so a conversion that works on one machine can fail or change behaviour on another."
          ],
          "items": []
        },
        {
          "heading": "Rejects and reconciliation",
          "body": [
            "A row that fails a rule must go to a reject file with its record number, original text and the reason, not be fixed by guesswork or dropped. Then the counts must reconcile: rows read equal rows converted plus rows rejected. The conversion must be deterministic, so the same export always gives byte-identical output, which makes it testable against an approved expected file. Keep personal data out of the export entirely; this is a product, stock and price conversion."
          ],
          "items": []
        },
        {
          "heading": "A safe first investigation",
          "body": [
            "In a copy of one export, sort each numeric column and look at the smallest, largest and any blanks. Look at each date column for day-first versus month-first. List every change a person currently makes by hand; each becomes a rule. Do not send real price lists to start. The two header rows, three invented rows and the list of hand edits are enough to scope the work."
          ],
          "items": []
        },
        {
          "heading": "What fits and what does not",
          "body": [
            "Fits: one export layout converted to one import layout by a tested script that you can run. Not a fit: running it on a schedule on your server, changing the ERP's export, accounting or tax calculations, category mapping or exports with personal data. A schedule is a separate job, and the existing guides on pack and unit prices cover units rather than formats. If you only need the dates and decimal separators of one file fixed once, the fixed job for mixed dates and numbers in one spreadsheet or CSV is the closer and cheaper fit; this job is for an export you convert again and again, with status codes, text codes and a reject file."
          ],
          "items": []
        },
        {
          "heading": "How the paid job is accepted",
          "body": [
            "The job etl-erp-export-to-store-import-transform starts from £595 for one ERP layout and one target layout up to 40 columns, of which up to ten need a conversion rule (one rule is the conversion for one column), quoted after we see the two header rows and the hand edits. The agreed synthetic export must convert to the approved expected file byte for byte; failing rows must appear in the reject file with reasons and the counts reconcile; decimal-comma, thousands and trailing-minus values must convert to the agreed values with the agreed rounding; and two runs must give identical files. Prices are untested proposals, and payment follows the agreed checks and your sign-off. Nothing is booked or charged by an enquiry."
          ],
          "items": []
        }
      ],
      "incoming": [],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/etl-erp-export-numbers-and-dates-to-import-format/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/etl-erp-export-numbers-and-dates-to-import-format/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fetl-erp-export-numbers-and-dates-to-import-format%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fetl-erp-export-numbers-and-dates-to-import-format%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-pdf-price-list-reconciliation",
      "type": "example",
      "title": "Synthetic price list: a page-by-page row count that finds two mistakes a matching total hides",
      "description": "An invented three-page price list shows why rows are counted per page. A first extraction matches on the total, 14 of 14, while page 1 is a row short and page 2 has an extra row.",
      "url": "/examples/pdf-price-list-reconciliation/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "pdf-documents",
        "microsoft-excel"
      ],
      "buyer_role": "Office or purchasing manager who has to trust data taken from a PDF",
      "purchase_trigger": "A spreadsheet built from a supplier list has the right number of rows but nobody trusts it",
      "problem": "A total that matches can hide two errors that cancel each other out.",
      "related": [
        "guide-extract-reconciliation-method",
        "guide-pdf-text-layer-or-scan-check",
        "extract-pdf-price-lists-to-spreadsheet",
        "platform-pdf-documents"
      ],
      "provenance": [
        {
          "label": "Power Query PDF connector (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/power-query/connectors/pdf",
          "checked": "2026-10-11",
          "claims": [
            "Pdf.Tables returns tables found in a PDF; where multi-line rows are not identified properly the data may need cleaning, and similar tables on consecutive pages are combined by default."
          ]
        },
        {
          "label": "pdfplumber README",
          "url": "https://github.com/jsvine/pdfplumber",
          "checked": "2026-10-11",
          "claims": [
            "Table extraction works best on machine-generated PDFs and the project offers no text recognition."
          ]
        }
      ],
      "sections": [
        {
          "heading": "The invented source",
          "body": [
            "This is a made-up three-page price list from an imaginary supplier, with invented codes and prices. It was not taken from any real document or customer. Page 2 has one description that wraps onto a second line and one entry with no numeric price. Page 3 begins with a repeated heading line, which is layout and not a record. The correct count is 14 data rows: 6, 5 and 3."
          ],
          "items": [],
          "code": "page | code | description                           | unit  | price\n1    | A100 | Pine batten 25x50 mm, 2.4 m           | each  | 3.20\n1    | A101 | Pine batten 25x50 mm, 3.0 m           | each  | 4.05\n1    | A102 | Pine batten 38x63 mm, 2.4 m           | each  | 5.10\n1    | A110 | Plywood sheet 9 mm, 2440x1220         | sheet | 28.50\n1    | A111 | Plywood sheet 12 mm, 2440x1220        | sheet | 36.90\n1    | A120 | Wood screw 4x40 mm, box of 200        | box   | 7.45\n2    | A130 | Hinge, zinc, 75 mm                    | pair  | 2.60\n2    | A131 | Gate hinge, galvanised, heavy duty,   | pair  | 9.95\n2    |      |   with fixing bolts   (wrapped line)  |       |\n2    | A132 | Door latch, black                     | each  | 4.30\n2    | A140 | Paint, white satin, 2.5 l             | tin   | 18.40\n2    | A141 | Paint, colour match to sample         | each  | on request\n3    | (repeated heading line, not a record)\n3    | A150 | Dust sheet, 3.6x2.7 m                 | each  | 3.75\n3    | A151 | Masking tape, 50 m                    | roll  | 1.95\n3    | A160 | Cable clips, box of 100               | box   | 2.15"
        },
        {
          "heading": "First pass: the total matches, two pages do not",
          "body": [
            "Imagine a first extraction. Two tightly spaced lines on page 1 were read as a single row, and the wrapped second line of A131 on page 2 was read as a row of its own with an empty code. The repeated heading line on page 3 was correctly left out. The total is 14 against 14, so a check of the total alone would pass. Counting per page shows what happened."
          ],
          "items": [
            "A total of 14 extracted rows against 14 expected passes a total-only check.",
            "Page 1 is one row short and page 2 has one row too many, so two errors cancel each other."
          ],
          "code": "page | rows counted | first pass | difference | note\n1    | 6            | 5          | -1         | A101 and A102 read as one row\n2    | 5            | 6          | +1         | wrapped line of A131 became a row with no code\n3    | 3            | 3          | 0          | repeated heading line left out\nall  | 14           | 14         | 0          | total matches; two pages do not"
        },
        {
          "heading": "After correction: 13 clean rows and one exception",
          "body": [
            "The corrections join the wrapped description to A131 and split A101 from A102. Entry A141 has no numeric price. The agreed rule for this example is that a row without a numeric price goes to the exceptions sheet with the price left blank, so the data sheet holds 13 rows and the exceptions sheet holds one. The rule is invented for this example; a real job agrees it with the owner first."
          ],
          "items": [
            "Rows counted 14 = data rows 13 + exceptions 1.",
            "Every extracted row keeps its source page, so the counts can be repeated by filtering."
          ],
          "code": "page | rows counted | data sheet | exceptions | difference\n1    | 6            | 6          | 0          | 0\n2    | 5            | 4          | 1 (A141)   | 0\n3    | 3            | 3          | 0          | 0\nall  | 14           | 13         | 1          | 0"
        },
        {
          "heading": "The sample check, chosen before looking",
          "body": [
            "A sample of three rows is fixed in advance: one that was wrongly joined, one with a wrapped description and the last row of the last page. Each field is compared with the source."
          ],
          "items": [
            "A real sample would be larger, with a size and method agreed in writing; three rows keep the example short."
          ],
          "code": "row  | field       | source                              | extracted                           | result\nA101 | description | Pine batten 25x50 mm, 3.0 m         | Pine batten 25x50 mm, 3.0 m         | match\nA101 | price       | 4.05                                | 4.05                                | match\nA131 | description | Gate hinge, galvanised, heavy duty, with fixing bolts | Gate hinge, galvanised, heavy duty, with fixing bolts | match\nA160 | price       | 2.15                                | 2.15                                | match"
        },
        {
          "heading": "What this does not prove",
          "body": [
            "Matching counts and a matching sample do not show that every unsampled row is right, and nothing here checks that the invented prices are sensible. The result of a real job is reported as the measured counts and the sample outcomes, never as a percentage accuracy."
          ],
          "items": [
            "No real list was extracted: this is an authored illustration of the method.",
            "No extraction tool, supplier or customer is implied."
          ]
        },
        {
          "heading": "Use it to specify an enquiry",
          "body": [
            "If your own list is a text-based PDF in one layout, the one-off extraction job starts from £295 for up to 60 pages, after we have seen a redacted sample page and agreed the columns. The price is an untested proposal and payment follows the agreed checks and your sign-off. Send the page count, whether the text can be selected and the column headings you want, never the document."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-pdf-documents",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "guide-extract-reconciliation-method"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/pdf-price-list-reconciliation/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/pdf-price-list-reconciliation/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Fpdf-price-list-reconciliation%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Fpdf-price-list-reconciliation%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-merged-report-flattened",
      "type": "example",
      "title": "Synthetic merged-cell report flattened: nine rows in, five records out, every total reconciled",
      "description": "An invented printed-style sales report with merged region labels, subtotal rows and a spacer becomes a flat five-record table, with a reconciliation showing 9 original rows = 5 records + 4 removed.",
      "url": "/examples/merged-report-flattened/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel"
      ],
      "buyer_role": "Administrator who needs to sort or summarise a report built for printing",
      "purchase_trigger": "A report has merged group labels and subtotal lines, and Excel will not sort it",
      "problem": "Flattening by hand risks dropping rows, double-counting subtotals or putting a label on the wrong rows.",
      "related": [
        "guide-excel-merged-cells-and-tidy-tables",
        "spreadsheet-flatten-merged-cell-report",
        "platform-microsoft-excel",
        "guide-spreadsheet-import-readiness"
      ],
      "provenance": [
        {
          "label": "Merge and unmerge cells (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/merge-and-unmerge-cells-5cbd15d5-9375-4540-907f-c673a93fcedf",
          "checked": "2026-10-11",
          "claims": [
            "Merging keeps only the upper-left cell's contents; unmerging moves the data to the left cell."
          ]
        },
        {
          "label": "Find merged cells (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/find-merged-cells-d02b2a5a-a08d-4641-8d4d-b3f233daca2c",
          "checked": "2026-10-11",
          "claims": [
            "Excel does not sort data in a column that contains merged cells."
          ]
        },
        {
          "label": "Create a PivotTable to analyze worksheet data (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576",
          "checked": "2026-10-11",
          "claims": [
            "Source data should have one header row, no merged cells, no blank rows or columns and one type of data per column."
          ]
        }
      ],
      "sections": [
        {
          "heading": "The invented report",
          "body": [
            "This is a made-up report with invented products and figures. In the original, each region name is one merged cell spanning the rows of that region, so the name is stored once. Subtotal lines, a blank spacer row and a grand total sit inside the data area. Below the header there are nine rows."
          ],
          "items": [],
          "code": "row | Region (merged)       | Product | Units | Value\n1   | North (3 rows merged) | Anvil   | 4     | 120.00\n2   |                       | Hinge   | 10    | 25.00\n3   |                       | Latch   | 6     | 33.00\n4   |                       | Subtotal North | 20 | 178.00\n5   | (blank spacer row)\n6   | South (2 rows merged) | Anvil   | 2     | 60.00\n7   |                       | Latch   | 9     | 49.50\n8   |                       | Subtotal South | 11 | 109.50\n9   | Grand total           |         | 31    | 287.50"
        },
        {
          "heading": "The hazard in a quick unmerge",
          "body": [
            "Unmerging alone puts North only in the first row of its block and leaves empty cells below, because Microsoft says the data in a merged cell moves to the left cell when it is split. Sorting that table would separate Hinge and Latch from their region. The label must be repeated on every row it covered, and the subtotal and spacer rows must leave the data."
          ],
          "items": []
        },
        {
          "heading": "The flat table",
          "body": [
            "One header row, no merged cells and one record per row, with the region repeated."
          ],
          "items": [
            "Five records, one per product line, each carrying its own region."
          ],
          "code": "Region | Product | Units | Value\nNorth  | Anvil   | 4     | 120.00\nNorth  | Hinge   | 10    | 25.00\nNorth  | Latch   | 6     | 33.00\nSouth  | Anvil   | 2     | 60.00\nSouth  | Latch   | 9     | 49.50"
        },
        {
          "heading": "The reconciliation",
          "body": [
            "Original rows must equal records plus removed rows, and totals must match. The removed rows are listed by type so nothing disappears unexplained."
          ],
          "items": [
            "Original rows 9 = records 5 + removed 4 (one spacer, two subtotals, one grand total).",
            "Units: records sum to 4 + 10 + 6 + 2 + 9 = 31, equal to the grand total 31.",
            "Value: records sum to 287.50, equal to the grand total 287.50; North 178.00 and South 109.50 equal the original subtotals."
          ],
          "code": "check                         | original | flat sheet | result\nrecord rows                   | 5        | 5          | match\nremoved: spacer               | 1        | 0          | listed\nremoved: subtotal lines       | 2        | 0          | listed, recomputed: 20/178.00 and 11/109.50 match\nremoved: grand total          | 1        | 0          | listed, recomputed: 31/287.50 matches\nall rows                      | 9        | 5 + 4      | match"
        },
        {
          "heading": "The label sample",
          "body": [
            "Totals cannot reveal a label put on the wrong row, so a sample compares labels record by record. In a real job at least twenty records are compared; with five records here, all five are."
          ],
          "items": [
            "Anvil/North, Hinge/North, Latch/North, Anvil/South and Latch/South each carry the label that covered them in the original."
          ]
        },
        {
          "heading": "Use it to specify an enquiry",
          "body": [
            "If your report has one repeating layout and up to 5,000 data rows, the flattening job is from £145 (an untested proposal, with the final price confirmed after we see the layout description), accepted by checks like these, with payment after your sign-off. It does not correct figures, build a pivot or deal with labels whose rows nobody can identify. Send a description of the layout and the row count, never the report."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-microsoft-excel",
        "guide-excel-merged-cells-and-tidy-tables"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/merged-report-flattened/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/merged-report-flattened/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Fmerged-report-flattened%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Fmerged-report-flattened%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-quote-line-rounding",
      "type": "example",
      "title": "Synthetic quotation: why the same three lines total 4.38 or 4.39 depending on the rounding rule",
      "description": "Three invented lines of 1.30 plus a 12.5 per cent add-on give 4.38 if the add-on is rounded per line and 4.39 if it is rounded on the total. The rule must be written down.",
      "url": "/examples/quote-line-rounding/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel",
        "microsoft-word"
      ],
      "buyer_role": "Owner or office manager who produces quotations from a price list",
      "purchase_trigger": "Two quotes with the same lines differ by a penny, or a customer queries a total",
      "problem": "A rounding rule chosen by habit gives different totals for the same lines, and a merge template can hide which rule was used.",
      "related": [
        "guide-mailmerge-fields-and-number-formats",
        "guide-excel-stale-or-text-numbers",
        "quote-pack-from-customer-brief",
        "platform-microsoft-excel"
      ],
      "provenance": [
        {
          "label": "Floating-point arithmetic may give inaccurate result in Excel (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/troubleshoot/microsoft-365-apps/excel/floating-point-arithmetic-inaccurate-result",
          "checked": "2026-10-11",
          "claims": [
            "Excel stores 15 digits of precision and offers the ROUND function to compensate for rounding errors."
          ]
        },
        {
          "label": "ROUND function (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/functions/round-function",
          "checked": "2026-10-11",
          "claims": [
            "=ROUND(2.15, 1) returns 2.2 and =ROUND(-1.475, 2) returns -1.48."
          ]
        },
        {
          "label": "Prepare your Excel data source for a Word mail merge (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/word/prepare-your-excel-data-source-for-a-word-mail-merge",
          "checked": "2026-10-11",
          "claims": [
            "Merged numbers come through without currency or percent symbols."
          ]
        }
      ],
      "sections": [
        {
          "heading": "The invented inputs",
          "body": [
            "All figures here are invented for the example. The 12.5 per cent add-on is a made-up rate chosen only to make the rounding visible. It is not a real or recommended tax rate and nothing here is tax or accounting advice; a real rate and its rule come from you."
          ],
          "items": [],
          "code": "price list (invented)\ncode | description       | unit price\nS01  | Spacer, plastic   | 0.26\nW10  | Widget, standard  | 0.65\nW20  | Widget, long      | 1.30\n\nbrief 1: S01 x 5, W10 x 2, W20 x 1\nadd-on rate for this example: 12.5%"
        },
        {
          "heading": "The lines",
          "body": [
            "Each line is quantity times unit price. All three come to the same figure, which is what makes the difference between the two rules show."
          ],
          "items": [],
          "code": "line | code | qty | unit | net\n1    | S01  | 5   | 0.26 | 1.30\n2    | W10  | 2   | 0.65 | 1.30\n3    | W20  | 1   | 1.30 | 1.30\nnet total               | 3.90"
        },
        {
          "heading": "Two rules, two totals",
          "body": [
            "Rule A rounds the add-on on each line to two decimal places and then adds the lines. Rule B adds the net lines and rounds the add-on once on the total. Both are consistent, but they are not the same, and a quote should say which one it uses."
          ],
          "items": [
            "Rule A: 12.5% of 1.30 is 0.1625, which rounds to 0.16 on each line; three lines give 0.48; the total is 3.90 + 0.48 = 4.38.",
            "Rule B: 12.5% of 3.90 is 0.4875, which rounds to 0.49; the total is 3.90 + 0.49 = 4.39.",
            "Neither is wrong. The owner of the quote chooses and writes the rule down."
          ],
          "code": "rule | add-on          | add-on rounded | total\nA    | 3 x 0.1625      | 3 x 0.16 = 0.48 | 4.38\nB    | 0.4875 on 3.90  | 0.49            | 4.39"
        },
        {
          "heading": "Where the workbook and the template come in",
          "body": [
            "Excel stores 15 digits of precision and cannot hold some decimals exactly, so rounding should be applied with the ROUND function at the step the rule names, not left to cell formatting. Microsoft's ROUND page shows, for example, =ROUND(2.15, 1) giving 2.2. The merge template should then show the finished figures. Microsoft notes merged numbers arrive without currency symbols, so the symbol is added in the template next to the field."
          ],
          "items": []
        },
        {
          "heading": "Edge cases in a proof set",
          "body": [
            "A real proof set includes cases like these, each calculated by hand beforehand."
          ],
          "items": [
            "Brief 2 includes code S09, which is not on the price list: the proof sheet lists it as an exception and the quote has no priced line for it, never a blank or guessed price.",
            "Brief 3 includes a line with quantity 0: the written rule says whether it is omitted or shown, and the proof sheet records which.",
            "Brief 4 has a quantity at a price break: the break rule is applied and tested on both sides of the boundary."
          ]
        },
        {
          "heading": "Use it to specify an enquiry",
          "body": [
            "A batch of up to 40 quotations from one template and one price list, with your written rules, is a one-off job from £495 (an untested proposal), accepted by hand-calculated test briefs like these, with payment after your sign-off. We never send quotations. Send your rules in words and the counts, not the price list or customers."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-microsoft-excel",
        "guide-excel-stale-or-text-numbers",
        "guide-mailmerge-fields-and-number-formats"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/quote-line-rounding/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/quote-line-rounding/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Fquote-line-rounding%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Fquote-line-rounding%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-archive-rename-manifest",
      "type": "example",
      "title": "Synthetic rename manifest: three clean renames with matching checksums and four held names",
      "description": "An invented seven-file folder shows a dry run and a manifest with a row for every file, with three renamed files whose checksums match, and four held, a pair of names that differ only in letter case, a reserved device name and a name ending in a period, all listed, not guessed.",
      "url": "/examples/archive-rename-manifest/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "file-archives"
      ],
      "buyer_role": "Administrator tidying a shared drive before a move",
      "purchase_trigger": "A bulk rename is planned and nobody knows how to show that nothing was lost",
      "problem": "Renames fail quietly on collisions and rejected names, and without a manifest nobody can reverse or verify them.",
      "related": [
        "guide-archive-rename-manifest-path-limits",
        "archive-rename-organise-with-manifest",
        "archive-digitise-and-normalise-one-archive",
        "guide-convert-legacy-office-fidelity"
      ],
      "provenance": [
        {
          "label": "Naming Files, Paths, and Namespaces (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file",
          "checked": "2026-10-11",
          "claims": [
            "Windows file names cannot be reserved names such as CON, should not end with a space or period, and case sensitivity should not be assumed."
          ]
        },
        {
          "label": "RFC 8493: The BagIt File Packaging Format",
          "url": "https://www.rfc-editor.org/rfc/rfc8493",
          "checked": "2026-10-11",
          "claims": [
            "A payload manifest pairs a checksum with a relative path for each file so that completeness and fixity can be checked."
          ]
        }
      ],
      "sections": [
        {
          "heading": "The invented folder and rule",
          "body": [
            "These seven files and their contents are invented, and no name here holds a person's name, which a real job would also require. The first three each hold one line of text, so their checksums are real SHA-256 values that anyone could recompute. The naming rule is also invented: year-month, a subject, then a three-digit number, with no spaces. Its inputs are invented too: the year-month is each file's modified date (March 2024 for the two scans, November 2023 for the Word file) and the subject comes from a mapping sheet the buyer supplies (supplier letters for the two scans, annual report for the Word file)."
          ],
          "items": [],
          "code": "old path                       | content (one line)\nScans/scan0001.pdf             | synthetic file one\nScans/scan0002.pdf             | synthetic file two\nOld/Report FINAL.doc           | synthetic file three\nLetters/Summary.pdf            | (content not needed for this example)\nLetters/summary.pdf            | (content not needed for this example)\nMisc/CON.txt                   | (content not needed for this example)\nMisc/notes.                    | (content not needed for this example)"
        },
        {
          "heading": "The dry run, approved before anything changes",
          "body": [
            "The dry run proposes names and flags problems; it changes nothing. Microsoft says Windows should not be assumed to be case-sensitive, so Summary.pdf and summary.pdf would be the same name there and cannot both keep it. CON is a reserved device name, and Microsoft says a name should not end in a period. These four are marked held and go to the exceptions list for a person to decide; they are not copied under a new name until then."
          ],
          "items": [
            "Three files can be renamed by the rule.",
            "Four files are held: two that collide when case is ignored, one reserved name and one trailing period."
          ],
          "code": "old path                 | proposed new path                 | status\nScans/scan0001.pdf       | 2024-03_supplier-letters_001.pdf  | renamed\nScans/scan0002.pdf       | 2024-03_supplier-letters_002.pdf  | renamed\nOld/Report FINAL.doc     | 2023-11_annual-report_001.doc     | renamed\nLetters/Summary.pdf      | (held)                            | exception: differs only in case from summary.pdf\nLetters/summary.pdf      | (held)                            | exception: differs only in case from Summary.pdf\nMisc/CON.txt             | (held)                            | exception: reserved device name\nMisc/notes.              | (held)                            | exception: name ends in a period"
        },
        {
          "heading": "The manifest of all seven files",
          "body": [
            "The manifest has one row for every source file and pairs a checksum with a path, the same idea as a BagIt payload manifest. For each renamed file the source checksum and the checksum at the new path must be identical. These are SHA-256 values of the invented contents above, each including its trailing line break. The four held files have rows too, with status held, no new path and no copy until a person decides."
          ],
          "items": [
            "A mismatch on any renamed row would mean the file changed or was damaged.",
            "The reversal list is the same table read backwards for the renamed rows: the new path returns to the old path."
          ],
          "code": "status  | checksum (SHA-256)                                                | old path             | new path\nrenamed | e46e10b85f229616d8a988252da2cb5bbe562a89403d0feb9eb05cf40f68fd62  | Scans/scan0001.pdf   | 2024-03_supplier-letters_001.pdf\nrenamed | 442d39082e03d9002d710f5e09d78412d58755aff911d064ac17031be36bbd82  | Scans/scan0002.pdf   | 2024-03_supplier-letters_002.pdf\nrenamed | 3a4b2c6ac81fe538ca96417064742b5ecf8b010ed0d27ae926da9f79ecd3856e  | Old/Report FINAL.doc | 2023-11_annual-report_001.doc\nheld    | (content not given in this example)                               | Letters/Summary.pdf  | (not copied)\nheld    | (content not given in this example)                               | Letters/summary.pdf  | (not copied)\nheld    | (content not given in this example)                               | Misc/CON.txt         | (not copied)\nheld    | (content not given in this example)                               | Misc/notes.          | (not copied)"
        },
        {
          "heading": "The counts",
          "body": [
            "Counts reconcile in the same way whatever the folder size."
          ],
          "items": [
            "Source files 7 = renamed 3 + held 4.",
            "Manifest rows 7 = source files 7, with 3 rows marked renamed and 4 marked held.",
            "Files at new paths 3 = rows marked renamed 3.",
            "Originals untouched: the three source checksums are the same before and after the copy."
          ]
        },
        {
          "heading": "Use it to specify an enquiry",
          "body": [
            "For one folder tree of up to 5,000 files, the rename-and-organise job starts from £245 (an untested proposal), accepted by checks like these, with payment after your sign-off. The files are handed over as a copy through a secure route agreed in writing first; no upload portal exists yet, and folders whose names or contents hold personal data are not covered. Originals are never changed or deleted. Send counts and a few example names, not files."
          ],
          "items": []
        }
      ],
      "incoming": [
        "guide-archive-rename-manifest-path-limits"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/archive-rename-manifest/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/archive-rename-manifest/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Farchive-rename-manifest%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Farchive-rename-manifest%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-date-ambiguity-matrix",
      "type": "example",
      "title": "Synthetic date column: which rows a rule can settle, and which must stay on the ambiguity list",
      "description": "Nine invented entries show day-first and month-first typing, a two-digit year, a row where only one reading is possible, an impossible date and a four-year shift, with the decision and reason for each.",
      "url": "/examples/date-ambiguity-matrix/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel",
        "csv"
      ],
      "buyer_role": "Administrator who combines lists typed by different offices",
      "purchase_trigger": "A date column from several sources sorts wrongly and nobody can say which entries are right",
      "problem": "The same typed text can mean two dates, and the honest result for some rows is a list, not an answer.",
      "related": [
        "guide-excel-date-serials-and-regional-order",
        "spreadsheet-normalise-mixed-dates-and-numbers",
        "platform-microsoft-excel",
        "guide-spreadsheet-import-readiness"
      ],
      "provenance": [
        {
          "label": "Date systems in Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/date-systems-in-excel",
          "checked": "2026-10-11",
          "claims": [
            "The 1900 and 1904 systems differ by 1,462 days; July 5, 2011 is 40729 and 39267."
          ]
        },
        {
          "label": "Change the date system, format, or two-digit year interpretation (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/change-the-date-system-format-or-two-digit-year-interpretation-aaa2159b-4ae8-4651-8bce-d4707bc9fb9f",
          "checked": "2026-10-11",
          "claims": [
            "Two-digit years 00 to 29 are read as 2000 to 2029 and 30 to 99 as 1930 to 1999."
          ]
        },
        {
          "label": "W3C Note: Date and Time Formats",
          "url": "https://www.w3.org/TR/NOTE-datetime",
          "checked": "2026-10-11",
          "claims": [
            "YYYY-MM-DD is an unambiguous date representation."
          ]
        }
      ],
      "sections": [
        {
          "heading": "The invented rules",
          "body": [
            "All entries, offices and rules here are invented. The Leeds office types day first. The Austin office types month first. The system export writes year-month-day. Two-digit years follow Microsoft's documented reading: 00 to 29 mean 2000 to 2029 and 30 to 99 mean 1930 to 1999. Output is year-month-day."
          ],
          "items": []
        },
        {
          "heading": "Nine entries and what is done with each",
          "body": [
            "Each row shows the source, the typed text, the decision and the reason. Only rows with a stated source or a logical impossibility are converted."
          ],
          "items": [],
          "code": "row | source  | typed text   | decision                | result      | reason\n1   | Leeds   | 03/04/2026   | converted by rule       | 2026-04-03  | Leeds is day-first\n2   | Austin  | 03/04/2026   | converted by rule       | 2026-03-04  | Austin is month-first\n3   | system  | 2026-04-03   | converted               | 2026-04-03  | already year-month-day\n4   | unknown | 05/06/2026   | ambiguity list          | (blank)     | 2026-06-05 or 2026-05-06; no source\n5   | Leeds   | 27/04/2026   | converted by rule       | 2026-04-27  | day-first, valid\n6   | unknown | 27/04/2026   | converted, logged       | 2026-04-27  | 27 cannot be a month, so only day-first is possible\n7   | Austin  | 31/12/2025   | exception               | (blank)     | month-first rule makes month 31, which is impossible; typo or wrong source\n8   | Leeds   | 5 Mar 26     | converted, logged       | 2026-03-05  | two-digit year 26 is read as 2026 by the agreed cutoff\n9   | system  | 2030-04-04   | exception               | (blank)     | paper record says 2026-04-03; difference is exactly 1,462 days"
        },
        {
          "heading": "What the last row shows",
          "body": [
            "Microsoft documents two date systems, 1900 and 1904, that differ by 1,462 days, which is four years and one day; its example is July 5, 2011, which is 40729 in one and 39267 in the other. A date that is exactly 1,462 days away from a known source date suggests a workbook that used the other system. Row 9 is an authored illustration of that pattern."
          ],
          "items": [
            "A whole column offset by 1,462 days suggests a date-system mismatch, which can be corrected for the whole column once confirmed.",
            "A single row offset by that amount is listed for the owner to check against the source."
          ]
        },
        {
          "heading": "The result, as counts",
          "body": [
            "The conversion is reported as counts, with every uncertain row visible."
          ],
          "items": [
            "Converted by a stated source: rows 1, 2, 3 and 5 (four).",
            "Converted by logic or a cutoff and logged: rows 6 and 8 (two).",
            "On the ambiguity list: row 4 (one).",
            "Exceptions for the owner: rows 7 and 9 (two).",
            "Four + two + one + two = nine entries, with none left unaccounted for."
          ],
          "code": "category                          | rows           | count\nconverted by stated source        | 1, 2, 3, 5     | 4\nconverted by logic or cutoff      | 6, 8           | 2\nambiguity list                    | 4              | 1\nexceptions for the owner          | 7, 9           | 2\nall                               |                | 9"
        },
        {
          "heading": "Use it to specify an enquiry",
          "body": [
            "For one file of up to 20,000 rows with up to three date columns, the clean-up job is from £125 (an untested proposal, with the final price confirmed after we see the column list), accepted by checks like these and a reference set of at least thirty dates you confirm, with payment after your sign-off. It never guesses a date. Send the column list and what you know about each source, not the file."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-microsoft-excel"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/date-ambiguity-matrix/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/date-ambiguity-matrix/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Fdate-ambiguity-matrix%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Fdate-ambiguity-matrix%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "platform-pdf-documents",
      "type": "platform",
      "title": "PDF documents: getting data, forms and searchable text out of office files",
      "description": "For office managers and administrators with PDF price lists, filled forms and scans: how to tell which kind of PDF you have, how to check what is extracted and where a paid, checked job fits.",
      "url": "/platforms/pdf-documents/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "pdf-documents"
      ],
      "buyer_role": "Office manager, administrator or small-business owner who works from PDF documents",
      "purchase_trigger": "Someone is retyping or searching PDFs by hand, or has been told a PDF can be converted to a spreadsheet",
      "problem": "Three different kinds of PDF need three different approaches, and from the outside they look the same.",
      "related": [
        "guide-pdf-text-layer-or-scan-check",
        "guide-extract-reconciliation-method",
        "guide-pdf-form-field-data-export",
        "guide-pdf-scan-ocr-search-check",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "example-pdf-price-list-reconciliation",
        "buyer-doc-office-manager-paper-and-pdf-backlog",
        "extract-pdf-price-lists-to-spreadsheet",
        "extract-pdf-form-fields-to-spreadsheet",
        "pdf-scanned-batch-to-searchable-text",
        "extract-pdf-batches-every-month"
      ],
      "provenance": [
        {
          "label": "Power Query PDF connector (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/power-query/connectors/pdf",
          "checked": "2026-10-11",
          "claims": [
            "Pdf.Tables returns tables found in a PDF; where multi-line rows are not identified properly the data may need cleaning, and similar tables on consecutive pages are combined by default."
          ]
        },
        {
          "label": "pdfplumber README",
          "url": "https://github.com/jsvine/pdfplumber",
          "checked": "2026-10-11",
          "claims": [
            "Table extraction works best on machine-generated PDFs and the project offers no text recognition."
          ]
        },
        {
          "label": "pypdf documentation: interactions with PDF forms",
          "url": "https://pypdf.readthedocs.io/en/stable/user/forms.html",
          "checked": "2026-10-11",
          "claims": [
            "Form field values are read by field name; flattening turns field contents into regular page content; an XFA entry can override the page content."
          ]
        },
        {
          "label": "OCRmyPDF introduction",
          "url": "https://ocrmypdf.readthedocs.io/en/latest/introduction.html",
          "checked": "2026-10-11",
          "claims": [
            "It adds text layers to scanned image PDFs, depends on input quality and cannot recognise handwriting."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Start with what kind of PDF you have",
          "body": [
            "A PDF is a container for pages. Some hold text positioned on the page, some hold fields that people filled in and some hold only pictures of paper. The right method, the right checks and the right promises differ for each, so the first job is to find out which you have. A two-minute test with a text search and a text selection is described in the first guide below."
          ],
          "items": [
            "Text-based table or list: the rows can be read from the positions of the text.",
            "Fillable form returned with answers: the answers are stored as named fields.",
            "Scanned pages: only pictures, so nothing can be searched until text recognition adds a hidden layer.",
            "A mix of the above in one file."
          ]
        },
        {
          "heading": "The questions to ask in order",
          "body": [
            "The collection linked here puts the questions in the order that saves most effort: does the file have text, is it a table or a form, does it have one layout, what should unusual entries become, how will you know it is complete, and who decides the exceptions. The worked example shows a row count that exposes mistakes a matching total hides."
          ],
          "items": [
            "Guide: can this PDF be read as data? Check for a text layer first.",
            "Guide: how to check an extraction is right.",
            "Guide: where filled-in form answers live and when they are gone.",
            "Guide: what text recognition can fix on scans and how to test it."
          ]
        },
        {
          "heading": "What documentation says, and what we do with it",
          "body": [
            "Microsoft documents a PDF connector for Excel that returns tables found in a PDF, and notes that rows spanning several lines may need cleaning. The pdfplumber project says its table extraction works best on machine-generated PDFs and offers no text recognition. The pypdf documentation describes reading form values by field name, and notes that flattening removes the field structure. The OCRmyPDF documentation says text recognition output depends on input quality and cannot read handwriting. We cite these only because they explain how PDFs behave. What a buyer receives is a checked spreadsheet or a searchable copy, not a tool."
          ],
          "items": []
        },
        {
          "heading": "The paid routes, and what none of them promise",
          "body": [
            "A text-based list of up to 60 pages in one layout becomes a checked spreadsheet from £295. Up to 200 returned fillable forms from one template become one sheet from £245. Up to 500 already-scanned pages can be made searchable from £195. Each month's batch of same-layout documents can be extracted as a standing service from £395 a month. All prices are untested proposals, and payment follows agreed checks and your sign-off.",
            "None of these promise accuracy. Results are reported as counts and sampled comparisons, with every row that could not be read with confidence listed for you to decide. None gives legal, medical, financial or tax advice. The first enquiry never includes confidential or personal documents: send counts, descriptions and an invented or redacted sample, and secure handling is agreed after scoping."
          ],
          "items": [
            "This page describes new offers; no delivery history is claimed.",
            "Documents with personal, payment or health information are not suitable for these fixed jobs."
          ]
        }
      ],
      "incoming": [
        "example-pdf-price-list-reconciliation",
        "buyer-doc-office-manager-paper-and-pdf-backlog",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "guide-pdf-text-layer-or-scan-check",
        "guide-extract-reconciliation-method",
        "guide-pdf-form-field-data-export",
        "guide-pdf-scan-ocr-search-check"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/platforms/pdf-documents/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/platforms/pdf-documents/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fplatforms%2Fpdf-documents%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fplatforms%2Fpdf-documents%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "platform-microsoft-excel",
      "type": "platform",
      "title": "Microsoft Excel workbooks: repair, tidy and make them reliable",
      "description": "For administrators and small businesses who rely on Excel: where workbooks go wrong, the order to diagnose them in, and the checked, fixed-scope jobs that repair formulas, flatten layouts and clean dates and duplicates.",
      "url": "/platforms/microsoft-excel/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel"
      ],
      "buyer_role": "Office manager, finance administrator or small-business owner who runs on spreadsheets",
      "purchase_trigger": "A workbook shows errors, sorts wrongly, will not import or produces totals nobody trusts",
      "problem": "Different kinds of damage in the same workbook look alike from the outside, and fixing them in the wrong order makes the next fix harder.",
      "related": [
        "guide-excel-error-values-trace",
        "guide-excel-stale-or-text-numbers",
        "guide-excel-merged-cells-and-tidy-tables",
        "guide-excel-date-serials-and-regional-order",
        "guide-spreadsheet-contact-duplicate-match-rules",
        "guide-spreadsheet-import-readiness",
        "guide-mailmerge-fields-and-number-formats",
        "guide-report-pack-repeatable-export",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "spreadsheet-repair-broken-formulas",
        "spreadsheet-flatten-merged-cell-report",
        "spreadsheet-normalise-mixed-dates-and-numbers",
        "spreadsheet-remove-duplicate-contacts",
        "quote-pack-from-customer-brief",
        "report-pack-every-period",
        "spreadsheet-make-workbook-set-import-ready",
        "example-merged-report-flattened",
        "example-date-ambiguity-matrix",
        "example-quote-line-rounding",
        "buyer-report-operations-lead-weekly-pack"
      ],
      "provenance": [
        {
          "label": "Create a PivotTable to analyze worksheet data (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576",
          "checked": "2026-10-11",
          "claims": [
            "Source data should have one header row, no merged cells, no blank rows or columns and one type of data per column."
          ]
        },
        {
          "label": "Find merged cells (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/find-merged-cells-d02b2a5a-a08d-4641-8d4d-b3f233daca2c",
          "checked": "2026-10-11",
          "claims": [
            "Excel does not sort data in a column that contains merged cells."
          ]
        },
        {
          "label": "Date systems in Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/date-systems-in-excel",
          "checked": "2026-10-11",
          "claims": [
            "The 1900 and 1904 systems differ by 1,462 days; July 5, 2011 is 40729 and 39267."
          ]
        },
        {
          "label": "Floating-point arithmetic may give inaccurate result in Excel (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/troubleshoot/microsoft-365-apps/excel/floating-point-arithmetic-inaccurate-result",
          "checked": "2026-10-11",
          "claims": [
            "Excel stores 15 digits of precision and offers the ROUND function to compensate for rounding errors."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Why the order of repair matters",
          "body": [
            "Most troubled workbooks have more than one problem, and fixing them in the wrong order makes the next fix harder. The collection linked here sets out the order and the reason for it: stale calculation and formula errors first, then layout, then numbers and dates, then duplicates, then import readiness."
          ],
          "items": []
        },
        {
          "heading": "What Excel's own documentation settles",
          "body": [
            "Microsoft documents that Excel does not sort a column that contains merged cells, that a PivotTable source needs one header row and one kind of data per column, that dates are stored as serial numbers in one of two systems 1,462 days apart and that Excel stores 15 digits of precision, which is why the ROUND function exists. These are reasons to check, not claims about any particular workbook, and the guides cite each page."
          ],
          "items": []
        },
        {
          "heading": "The fixed-scope jobs",
          "body": [
            "One workbook's formulas can be repaired against check cases you calculate yourself, as a fixed £195 job. One merged-cell report can be flattened with a reconciliation, from £145. Mixed dates and numbers in one file can be normalised with every ambiguous row listed, from £125. One product, parts, asset or company list with no personal data can be de-duplicated with a merge log and review file, from £195; lists of people are not covered. Quotation batches from your price list start from £495, and a standing report pack from your export starts from £295 a month, by proposal. A set of up to eight workbooks can be made import-ready as a project from £1,500. All prices are untested proposals, one-off payment follows agreed checks and your sign-off, and the standing service is billed monthly under terms you agree first."
          ],
          "items": [
            "Not covered: macros, add-ins, live data connections, tax or accounting advice, deciding which figures are right, and workbooks that hold personal data.",
            "The first enquiry never includes workbooks; send a description, counts and invented examples. No upload portal exists yet, and a secure way to hand the files over is agreed in writing before any file moves."
          ]
        }
      ],
      "incoming": [
        "example-merged-report-flattened",
        "example-quote-line-rounding",
        "example-date-ambiguity-matrix",
        "buyer-report-operations-lead-weekly-pack",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-excel-error-values-trace",
        "guide-excel-stale-or-text-numbers",
        "guide-excel-merged-cells-and-tidy-tables",
        "guide-excel-date-serials-and-regional-order",
        "guide-spreadsheet-contact-duplicate-match-rules",
        "guide-convert-legacy-office-fidelity",
        "guide-mailmerge-fields-and-number-formats",
        "guide-report-pack-repeatable-export",
        "guide-spreadsheet-import-readiness"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/platforms/microsoft-excel/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/platforms/microsoft-excel/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fplatforms%2Fmicrosoft-excel%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fplatforms%2Fmicrosoft-excel%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "buyer-doc-office-manager-paper-and-pdf-backlog",
      "type": "buyer",
      "title": "For an office manager: sort your PDF, scan and old-file jobs into do it yourself, buy, or leave",
      "description": "List the jobs by what the file actually is, find the ones your own software already handles, and ask for outside help only where volume, repetition or the need for a checked record justifies it.",
      "url": "/buyers/doc-office-manager-paper-and-pdf-backlog/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "pdf-documents",
        "file-archives"
      ],
      "buyer_role": "Office manager, administrator or secretary responsible for documents",
      "purchase_trigger": "A pile of PDFs, scans, old Office files and badly named folders is growing and nobody owns it",
      "problem": "Several different jobs are described as one big tidy-up, so nobody can tell what is quick, what is risky and what is worth paying for.",
      "related": [
        "platform-pdf-documents",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "guide-pdf-text-layer-or-scan-check",
        "guide-convert-legacy-office-fidelity",
        "convert-legacy-office-files-batch",
        "archive-rename-organise-with-manifest",
        "archive-digitise-and-normalise-one-archive"
      ],
      "provenance": [
        {
          "label": "Power Query PDF connector (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/power-query/connectors/pdf",
          "checked": "2026-10-11",
          "claims": [
            "Pdf.Tables returns tables found in a PDF; where multi-line rows are not identified properly the data may need cleaning, and similar tables on consecutive pages are combined by default."
          ]
        },
        {
          "label": "Use Excel with earlier versions of Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/use-excel-with-earlier-versions-of-excel-2fd9ffcb-6fce-485b-85af-fecfd651a5ac",
          "checked": "2026-10-11",
          "claims": [
            "Compatibility Mode opens old workbooks; File, Info, Convert replaces the original and Save As keeps it."
          ]
        },
        {
          "label": "Converting documents to a newer format (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/converting-documents-to-a-newer-format-34ec742e-f0f9-4d95-bbe3-3ee8e30a86fa",
          "checked": "2026-10-11",
          "claims": [
            "File, Info, Convert converts a document without saving a copy; Save As makes a separate copy."
          ]
        }
      ],
      "sections": [
        {
          "heading": "List the jobs by what the file is",
          "body": [
            "Do not start with \"the archive\". Start with what each pile is. A folder of PDFs exported from software is a different problem from a box of scans, which is different from a drive of old Word and Excel files, which is different from a folder whose names nobody can decode. Write a line for each: what it is, roughly how many, who needs what from it and what happens if nothing is done."
          ],
          "items": [
            "Text-based PDFs you need data from: extraction.",
            "Filled-in fillable forms: reading the form fields.",
            "Scans nobody can search: adding searchable text.",
            "Old .doc and .xls files: conversion.",
            "Confusing names and folders: renaming with a record."
          ]
        },
        {
          "heading": "Do it yourself when the software already does it",
          "body": [
            "For a handful of files, the built-in routes are often enough. Excel's Get Data from PDF can import tables from a text-based PDF, as Microsoft documents. Word and Excel can convert an old file you open: File, Info, Convert changes it in place without a copy, while Save As keeps the original, so use Save As and keep a backup. Check the result against the original for layout changes, which Microsoft tells you to do.",
            "The cost of doing it yourself is time and no record. Nothing shows afterwards which files were done or whether anything changed."
          ],
          "items": []
        },
        {
          "heading": "Consider buying when volume, repetition or evidence matters",
          "body": [
            "Outside help starts to make sense when the number is large enough that the time is a problem, when the same job recurs every month, or when you need to show that everything was handled. The fixed-scope jobs each end in a reconciliation: counts that match, originals unchanged, an exceptions list. Prices start at £195 for making up to 500 scanned pages searchable, £245 for renaming and organising up to 5,000 files with a manifest and £295 for converting up to 300 old files. They are untested proposals, and payment follows agreed checks and your sign-off. A whole archive with scans, old files and names to fix is a project from £2,450."
          ],
          "items": []
        },
        {
          "heading": "Leave it alone when",
          "body": [
            "Some piles should not be touched yet. If nobody uses them, tidying them is effort without a result. If you are unsure whether you may copy or share them, find out first. If a question is whether documents must be kept or may be destroyed, that is a records and legal question for someone qualified, and we do not advise on it. Documents holding personal, payment or health information are not suitable for these fixed jobs."
          ],
          "items": []
        },
        {
          "heading": "Start with a low-trust enquiry",
          "body": [
            "Send counts, types, a rough size and what you want to be able to do afterwards, plus an invented or redacted sample if you have one. Never send the documents in the first message. If we agree a scope, we agree a secure route, who may see the files, how long we keep them and how we delete them in writing before any file moves. We work on copies and never on your only original, and we do not contact anyone on your behalf."
          ],
          "items": [
            "This page is an offer of new services, not evidence of past delivery or of demand.",
            "No accuracy is promised for scanned text."
          ]
        }
      ],
      "incoming": [
        "platform-pdf-documents"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/buyers/doc-office-manager-paper-and-pdf-backlog/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/buyers/doc-office-manager-paper-and-pdf-backlog/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fbuyers%2Fdoc-office-manager-paper-and-pdf-backlog%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fbuyers%2Fdoc-office-manager-paper-and-pdf-backlog%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "buyer-report-operations-lead-weekly-pack",
      "type": "buyer",
      "title": "For an operations lead: stop rebuilding the weekly report by hand, or decide to hand it over",
      "description": "Work out what the report actually costs you in steps, put the method in writing, decide whether to template it yourself or hand it over, and choose checks that show each pack matches its export.",
      "url": "/buyers/report-operations-lead-weekly-pack/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel"
      ],
      "buyer_role": "Operations lead, office manager or finance administrator who produces a recurring report",
      "purchase_trigger": "The weekly or monthly pack takes hours, depends on one person and has gone out with a wrong figure",
      "problem": "The report's method is unwritten, so no one can tell whether it is worth automating, who could do it instead, or whether this period is right.",
      "related": [
        "report-pack-every-period",
        "guide-report-pack-repeatable-export",
        "platform-microsoft-excel",
        "quote-pack-from-customer-brief",
        "spreadsheet-make-workbook-set-import-ready"
      ],
      "provenance": [
        {
          "label": "Create a PivotTable to analyze worksheet data (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576",
          "checked": "2026-10-11",
          "claims": [
            "Source data should have one header row, no merged cells, no blank rows or columns and one type of data per column."
          ]
        },
        {
          "label": "Combine files overview (Power Query, Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/power-query/combine-files-overview",
          "checked": "2026-10-11",
          "claims": [
            "Files with the same schema, file type and columns can be combined into one table."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Write down what you do",
          "body": [
            "Do the report once more and write every step as you go: where the export comes from, which filters you apply, what you copy and where, how you check it and who receives it. Time each step. This is the report's method, and today it exists only in your head. It is also the thing any person or service would need.",
            "Note every figure that has ever been wrong, and what caused it. Those are the checks worth building first."
          ],
          "items": []
        },
        {
          "heading": "Make the export a contract",
          "body": [
            "Decide what the export must look like every period: columns, order, types, date format, period boundaries, expected row count. Anything that is a business definition, such as what counts as a completed order, is written down with its owner. Without this, any automation is built on guesses."
          ],
          "items": []
        },
        {
          "heading": "Three ways forward",
          "body": [
            "You can template it yourself. Microsoft documents that an Excel table as a PivotTable source includes added rows on refresh, and that Power Query can combine files from a folder if they share the same file type and columns. That works if someone on your team will own it and the export stays stable.",
            "You can keep doing it by hand with the written steps, which at least lets someone else cover. Or you can hand it over: the standing report-pack service builds the pack from your export each period by your written rules, checks every control total against the export and notes anything unusual, from £295 a month for one monthly pack (an untested proposal, sold by proposal, set up in the first month)."
          ],
          "items": [
            "Own it: best when the export is stable and someone has the skills and time.",
            "Keep it manual but written: lowest cost, still error-prone.",
            "Hand it over: best when the report must happen every period whoever is away."
          ]
        },
        {
          "heading": "What the service does not do",
          "body": [
            "It does not connect to your live systems, send the pack to anyone, change a definition without your approval or promise a delivery time. It does not suit exports containing personal data about individuals. Each pack is checked by a reviewer separate from the work that produced it, and at launch much of the work is automated. You can end it at the end of any month."
          ],
          "items": []
        },
        {
          "heading": "What to put in the first enquiry",
          "body": [
            "Describe the report: who reads it, how often, its tables and charts and how long it takes. List the export's column headings with three invented rows. Say which totals you check by eye today. Do not send an export or a finished pack. If we agree to proceed, we agree a secure route in writing before any data moves."
          ],
          "items": [
            "This is a new service and nothing here claims past delivery or savings.",
            "Any time you save is yours to measure; we do not promise a figure."
          ]
        }
      ],
      "incoming": [
        "platform-microsoft-excel",
        "guide-report-pack-repeatable-export"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/buyers/report-operations-lead-weekly-pack/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/buyers/report-operations-lead-weekly-pack/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fbuyers%2Freport-operations-lead-weekly-pack%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fbuyers%2Freport-operations-lead-weekly-pack%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "collection-spreadsheet-messy-workbook-diagnosis-order",
      "type": "category",
      "title": "A messy workbook: the order to diagnose it in, and where each fix leads",
      "description": "Seven checks in a fixed order, from stale calculation to import readiness, each pointing to the guide that explains it and the checked job that covers it if you would rather not do it yourself.",
      "url": "/collections/spreadsheet-messy-workbook-diagnosis-order/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel"
      ],
      "buyer_role": "Administrator or owner trying to rescue an inherited spreadsheet",
      "purchase_trigger": "A workbook has several visible problems and you do not know where to start",
      "problem": "Problems in a workbook interact, so fixing them in a random order wastes effort and can hide the real cause.",
      "related": [
        "guide-excel-stale-or-text-numbers",
        "guide-excel-error-values-trace",
        "guide-excel-date-serials-and-regional-order",
        "guide-excel-merged-cells-and-tidy-tables",
        "guide-spreadsheet-contact-duplicate-match-rules",
        "guide-spreadsheet-import-readiness",
        "platform-microsoft-excel",
        "spreadsheet-repair-broken-formulas",
        "spreadsheet-flatten-merged-cell-report",
        "spreadsheet-normalise-mixed-dates-and-numbers",
        "spreadsheet-remove-duplicate-contacts",
        "spreadsheet-make-workbook-set-import-ready"
      ],
      "provenance": [
        {
          "label": "Create a PivotTable to analyze worksheet data (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576",
          "checked": "2026-10-11",
          "claims": [
            "Source data should have one header row, no merged cells, no blank rows or columns and one type of data per column."
          ]
        },
        {
          "label": "Find merged cells (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/find-merged-cells-d02b2a5a-a08d-4641-8d4d-b3f233daca2c",
          "checked": "2026-10-11",
          "claims": [
            "Excel does not sort data in a column that contains merged cells."
          ]
        },
        {
          "label": "Date systems in Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/date-systems-in-excel",
          "checked": "2026-10-11",
          "claims": [
            "The 1900 and 1904 systems differ by 1,462 days; July 5, 2011 is 40729 and 39267."
          ]
        },
        {
          "label": "Floating-point arithmetic may give inaccurate result in Excel (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/troubleshoot/microsoft-365-apps/excel/floating-point-arithmetic-inaccurate-result",
          "checked": "2026-10-11",
          "claims": [
            "Excel stores 15 digits of precision and offers the ROUND function to compensate for rounding errors."
          ]
        }
      ],
      "sections": [
        {
          "heading": "1. Is it recalculating?",
          "body": [
            "Check the calculation mode before anything else. A workbook set to recalculate only on request shows old results, and every other check is meaningless on stale numbers. Force a full recalculation and see whether any figure moves. The guide on wrong totals without error messages explains manual calculation, numbers stored as text and displayed versus stored values."
          ],
          "items": [
            "Next: if figures move, you have found one cause; recheck everything after recalculating."
          ]
        },
        {
          "heading": "2. Which errors show, and which are hidden?",
          "body": [
            "List the error values by type: #REF!, #NAME?, #VALUE!. Each points to a different break. Then look for silent damage: values typed over formulas and ranges that stop short. The error-values guide shows how to trace a formula to its inputs and why hiding an error is not repairing it. If the workbook's outputs cannot be trusted, the fixed formula repair, from £195 for one workbook, is accepted by check cases you work out independently."
          ],
          "items": []
        },
        {
          "heading": "3. Is the layout a table?",
          "body": [
            "Merged cells, spacer rows, repeated headings and subtotal rows must be flattened before sorting, filtering or a PivotTable. Do this once the workbook is recalculating and its formulas are sound, but before you test numbers and dates: a print layout puts subtotal and heading text inside the number and date columns, so those columns cannot be typed correctly until the data is a flat table. The merged-cells guide and the worked example show the reconciliation. The flattening job is from £145 for one sheet."
          ],
          "items": []
        },
        {
          "heading": "4. Are the numbers real numbers?",
          "body": [
            "Numbers stored as text align left and are skipped or mis-sorted. Codes that need leading zeros must be text from the start. This is checked once the layout is flat, so that heading and subtotal text is not mistaken for bad numbers, and before dates and duplicates, because both depend on values being the type you think they are."
          ],
          "items": []
        },
        {
          "heading": "5. Are the dates real, and unambiguous?",
          "body": [
            "Dates can be text, shifted by 1,462 days between Excel's two date systems, or typed day-first and month-first in the same column. The dates guide and the worked ambiguity example show which rows a written rule can settle and which must stay on a list. The clean-up job, from £125 for one file, handles steps 4 and 5 together and never guesses."
          ],
          "items": []
        },
        {
          "heading": "6. Are there duplicates, and by what rule?",
          "body": [
            "Deduplication comes after dates and layout, because duplicates cannot be reliably matched while the fields are still inconsistent. Write the match rules first, keep a merge log and treat uncertain pairs as review items. The duplicates guide explains why Excel's Remove Duplicates compares displayed values and deletes permanently. A list of products, parts, assets or companies with no personal data can be de-duplicated as a job from £195; lists of people are not covered."
          ],
          "items": []
        },
        {
          "heading": "7. Is it ready for the next system?",
          "body": [
            "Last, check readiness: one header row, one type per column, blanks decided, keys unique and a column list that matches the target. The readiness guide sets out the sheet. If you have several workbooks with several kinds of damage, a project from £1,500 runs these steps in order across the set. Prices are untested proposals; payment follows agreed checks and your sign-off. Files are handed over only by a secure route agreed in writing first, and no upload portal exists yet. Nothing here loads data into a live system, and nothing is accounting advice."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-microsoft-excel",
        "guide-excel-error-values-trace",
        "guide-excel-stale-or-text-numbers",
        "guide-excel-merged-cells-and-tidy-tables",
        "guide-excel-date-serials-and-regional-order",
        "guide-spreadsheet-contact-duplicate-match-rules",
        "guide-mailmerge-fields-and-number-formats",
        "guide-spreadsheet-import-readiness"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/collections/spreadsheet-messy-workbook-diagnosis-order/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/collections/spreadsheet-messy-workbook-diagnosis-order/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fcollections%2Fspreadsheet-messy-workbook-diagnosis-order%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fcollections%2Fspreadsheet-messy-workbook-diagnosis-order%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "collection-pdf-to-spreadsheet-diagnosis-order",
      "type": "category",
      "title": "From a PDF to a checked spreadsheet: the order of questions that saves the most effort",
      "description": "Seven steps from \"does it have text?\" to \"who signs off the exceptions?\", linked to the guides, the worked example and the checked jobs, including what to do when the same document arrives every month.",
      "url": "/collections/pdf-to-spreadsheet-diagnosis-order/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "pdf-documents",
        "microsoft-excel"
      ],
      "buyer_role": "Administrator who needs data from PDFs and wants to know what is realistic",
      "purchase_trigger": "Someone has asked for a PDF to be turned into a spreadsheet and you do not know where to begin",
      "problem": "Starting extraction before knowing whether the file has text, a form or only pictures wastes effort and produces results nobody can trust.",
      "related": [
        "guide-pdf-text-layer-or-scan-check",
        "guide-extract-reconciliation-method",
        "guide-pdf-form-field-data-export",
        "guide-pdf-scan-ocr-search-check",
        "example-pdf-price-list-reconciliation",
        "platform-pdf-documents",
        "extract-pdf-price-lists-to-spreadsheet",
        "extract-pdf-form-fields-to-spreadsheet",
        "pdf-scanned-batch-to-searchable-text",
        "extract-pdf-batches-every-month"
      ],
      "provenance": [
        {
          "label": "Power Query PDF connector (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/power-query/connectors/pdf",
          "checked": "2026-10-11",
          "claims": [
            "Pdf.Tables returns tables found in a PDF; where multi-line rows are not identified properly the data may need cleaning, and similar tables on consecutive pages are combined by default."
          ]
        },
        {
          "label": "pdfplumber README",
          "url": "https://github.com/jsvine/pdfplumber",
          "checked": "2026-10-11",
          "claims": [
            "Table extraction works best on machine-generated PDFs and the project offers no text recognition."
          ]
        },
        {
          "label": "pypdf documentation: interactions with PDF forms",
          "url": "https://pypdf.readthedocs.io/en/stable/user/forms.html",
          "checked": "2026-10-11",
          "claims": [
            "Form field values are read by field name; flattening turns field contents into regular page content; an XFA entry can override the page content."
          ]
        },
        {
          "label": "OCRmyPDF introduction",
          "url": "https://ocrmypdf.readthedocs.io/en/latest/introduction.html",
          "checked": "2026-10-11",
          "claims": [
            "It adds text layers to scanned image PDFs, depends on input quality and cannot recognise handwriting."
          ]
        }
      ],
      "sections": [
        {
          "heading": "1. Does the PDF have text?",
          "body": [
            "Search for a word you can see and try to select a line. If neither works, the pages are pictures, and everything below waits for a text layer. The text-layer guide gives the test. A scanned batch can be made searchable by a separate job, from £195 for up to 500 pages, with no accuracy promised."
          ],
          "items": [
            "Next if there is text: go to step 2. If not: the scanned-PDF guide, then return here."
          ]
        },
        {
          "heading": "2. Is it a table or a filled form?",
          "body": [
            "A table or list is read from positions of text or ruled lines. A returned fillable form is read from its form fields. The two have different failure modes and different checks. Microsoft's PDF connector and the pdfplumber project describe the first, and the pypdf documentation describes the second."
          ],
          "items": []
        },
        {
          "heading": "3. One layout or several?",
          "body": [
            "One set of rules covers one layout. A list whose sections differ, or forms from several template versions, needs rules for each, and the price changes. Note the page ranges or versions before asking anyone for a quote."
          ],
          "items": []
        },
        {
          "heading": "4. Decide the columns and the rules for unusual entries",
          "body": [
            "Write the output columns, then say what happens to \"price on request\", wrapped lines, checkboxes, blanks and repeated headings. These decisions are the owner's. An extraction without them will make them silently."
          ],
          "items": []
        },
        {
          "heading": "5. Reconcile, then sample",
          "body": [
            "Count rows per page or files against rows plus exceptions, then compare a sample you chose first, field by field. The reconciliation guide explains the method and the worked example shows a total that matched while two pages did not. Report counts, never a percentage accuracy."
          ],
          "items": []
        },
        {
          "heading": "6. List what could not be read, and decide it",
          "body": [
            "Rows or files that could not be read with confidence go on an exceptions list with their page and reason, not into the data. Agree beforehand how many exceptions would make you stop and re-scope. A named person decides each one."
          ],
          "items": []
        },
        {
          "heading": "7. If it arrives every month",
          "body": [
            "Write the rules down once and reuse them. The one-off jobs are from £295 for one list of up to 60 pages and from £245 for up to 200 forms. A standing monthly service from £395 a month extracts each batch by the same rules, reconciles it and reports any layout change or image-only page instead of guessing. Prices are untested proposals and payment follows agreed checks and your sign-off. No job gives legal, medical, financial or tax advice, and the first enquiry never includes confidential or personal documents."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-pdf-documents",
        "buyer-doc-office-manager-paper-and-pdf-backlog",
        "guide-convert-legacy-office-fidelity",
        "guide-archive-rename-manifest-path-limits",
        "guide-pdf-scan-ocr-search-check"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/collections/pdf-to-spreadsheet-diagnosis-order/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/collections/pdf-to-spreadsheet-diagnosis-order/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fcollections%2Fpdf-to-spreadsheet-diagnosis-order%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fcollections%2Fpdf-to-spreadsheet-diagnosis-order%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-excel-error-values-trace",
      "type": "guide",
      "title": "Excel shows #REF! or #NAME?: work out which kind of broken formula you have",
      "description": "Each Excel error value points to a different kind of break. Learn what they mean, how to trace a formula back to its inputs, and why hiding the error is not a repair.",
      "url": "/guides/excel-error-values-trace/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel"
      ],
      "buyer_role": "Office manager or finance administrator who depends on one Excel workbook",
      "purchase_trigger": "Cells show #REF!, #NAME? or #VALUE!, or a total no longer matches a hand calculation",
      "problem": "A workbook edited by several people accumulates different kinds of formula damage, and an error message alone does not say which.",
      "related": [
        "platform-microsoft-excel",
        "spreadsheet-repair-broken-formulas",
        "guide-excel-stale-or-text-numbers",
        "spreadsheet-make-workbook-set-import-ready",
        "collection-spreadsheet-messy-workbook-diagnosis-order"
      ],
      "provenance": [
        {
          "label": "How to correct a #REF! error (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/how-to-correct-a-ref-error",
          "checked": "2026-10-11",
          "claims": [
            "A formula that lists cells one by one, such as =SUM(B2,C2,D2), returns #REF! when one of those columns is deleted; a range such as =SUM(B2:D2) adjusts automatically.",
            "A VLOOKUP asking for a column beyond its range, an INDEX outside its range and an INDIRECT pointing at a closed workbook also return #REF!."
          ]
        },
        {
          "label": "How to correct a #NAME? error (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/how-to-correct-a-name-error-b6d54e31-a743-4d7d-9b61-40002a7b4286",
          "checked": "2026-10-11",
          "claims": [
            "The most common cause is a misspelt function name; others are an undefined or misspelt defined name, text without quotation marks, a missing colon in a range and an add-in that is not enabled.",
            "It says not to use error-handling functions such as IFERROR to mask the error."
          ]
        },
        {
          "label": "How to correct a #VALUE! error (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/how-to-correct-a-value-error",
          "checked": "2026-10-11",
          "claims": [
            "Causes include dates stored as text, hidden spaces, text or special characters in numeric cells and a list separator set in the Windows region settings.",
            "Evaluate Formula steps through a formula; IFERROR hides all errors, not just #VALUE!, and hides the problem rather than fixing it."
          ]
        },
        {
          "label": "Remove or allow a circular reference in Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/remove-or-allow-a-circular-reference-in-excel",
          "checked": "2026-10-11",
          "claims": [
            "A circular reference happens when a formula refers to itself, directly or indirectly; Formulas, Error Checking, Circular References lists the cells and Trace Precedents or Dependents follows the links.",
            "After the warning, the cell might show 0 or the last calculated value; iterative calculation suits some deliberate models and the page gives defaults of 100 iterations and 0.001."
          ]
        },
        {
          "label": "Detect formula errors in Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/detect-formula-errors-in-excel",
          "checked": "2026-10-11",
          "claims": [
            "Excel's error checking rules include formulas inconsistent with other formulas in the region, numbers stored as text and formulas that omit cells in a region.",
            "The page says the rules do not guarantee that a worksheet is error free."
          ]
        },
        {
          "label": "How to avoid broken formulas in Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/how-to-avoid-broken-formulas-in-excel",
          "checked": "2026-10-11",
          "claims": [
            "It advises against putting constants inside formulas, because they are hard to find when updating and more prone to typing errors.",
            "A deleted worksheet cannot be recovered, so formulas that referred to it cannot be fixed."
          ]
        }
      ],
      "sections": [
        {
          "heading": "An error value is a symptom, not a diagnosis",
          "body": [
            "Excel uses a small set of error values, and each one points at a different kind of break. #REF! means a formula refers to a cell that is no longer valid. Microsoft's page gives the classic case: a formula that lists cells one by one, such as adding B2, C2 and D2, breaks when one of those columns is deleted, whereas a range such as B2 to D2 adjusts itself. The same page lists a VLOOKUP that asks for a column beyond its range, an INDEX outside its range and a reference to a closed workbook as other causes.",
            "#NAME? is usually a typing problem: a misspelt function, a name that was never defined or was misspelt, text without quotation marks, a missing colon in a range, or an add-in that is not enabled. #VALUE! is the general complaint that a formula is being given the wrong kind of thing, such as text where a number is needed."
          ],
          "items": [
            "#REF!: deleted or overwritten cells, or a lookup or index outside its range.",
            "#NAME?: a name Excel does not recognise.",
            "#VALUE!: wrong kind of value, often text that looks like a number or a date, hidden spaces or a regional separator setting."
          ]
        },
        {
          "heading": "Trace before you change anything",
          "body": [
            "Make a copy of the workbook first. Then use Excel's own tools. Evaluate Formula steps through a formula one part at a time, which in Microsoft's example exposed a hidden space where a number was expected. Trace Precedents and Trace Dependents follow the links to the cells a formula uses and the cells that use it. For a suspected loop, the Error Checking menu lists circular references.",
            "Do not delete rows, retype values or paste over formulas while investigating. Each of those can destroy the evidence, and a deleted worksheet cannot be recovered, so formulas that pointed at it cannot be repaired from the file alone."
          ],
          "items": []
        },
        {
          "heading": "Errors that show no error",
          "body": [
            "The worst damage is silent. Excel can mark a formula whose pattern differs from its neighbours, numbers stored as text, and formulas that leave out cells next to their range. Microsoft says plainly that these rules do not guarantee a worksheet is error free. A number typed over a formula looks like any other number. A circular reference, after its first warning, may display 0 or the last calculated value without any visible error.",
            "Two habits help. Click into a few cells in each total column and read the formula bar. Then compare a total with one you work out another way. Neither takes long, and either can show a problem that error markers never will."
          ],
          "items": [
            "A cell in a formula column whose formula bar shows a bare number was overwritten.",
            "A total that ignores new rows may sum a range that stops short.",
            "A figure that does not move when its inputs change may be a pasted value or a stale calculation."
          ]
        },
        {
          "heading": "Why hiding the error is not fixing it",
          "body": [
            "Wrapping a formula in IFERROR makes the message disappear and leaves the fault in place. Microsoft's page on #NAME? says not to use error-handling functions such as IFERROR to mask the error, and its #VALUE! page says IFERROR hides every error, not just that one. Constants typed inside formulas are another trap: they are hard to find when something changes and easier to mistype.",
            "A real repair changes the formula or the input that caused the error, then shows that known cases return the right answers."
          ],
          "items": []
        },
        {
          "heading": "How a repair is checked, and where the paid job fits",
          "body": [
            "The check is a set of cases whose answers you worked out independently, by hand or from a printed source. Enter their inputs, compare outputs, then force a full recalculation and confirm nothing moves. Compare the original with the repaired copy so only the logged cells differ.",
            "One workbook of up to five sheets and eight named outputs can be repaired as a fixed £195 job (an untested proposal), accepted by those checks and a change log, with payment after your sign-off. It does not cover macros, links to files we cannot see, redesigning a model, or any accounting or tax advice. If the problem is a whole set of workbooks with several kinds of damage, a project scope fits better. The first enquiry needs the error types and your check cases, never the workbook."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-microsoft-excel",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-excel-stale-or-text-numbers"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/excel-error-values-trace/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/excel-error-values-trace/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fexcel-error-values-trace%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fexcel-error-values-trace%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-excel-stale-or-text-numbers",
      "type": "guide",
      "title": "Totals look wrong but Excel shows no error: stale calculation, text numbers and displayed versus stored values",
      "description": "When no error appears, three quiet causes explain most wrong totals: manual calculation, numbers stored as text and rounding that is displayed but not stored. Here is how to tell which.",
      "url": "/guides/excel-stale-or-text-numbers/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel"
      ],
      "buyer_role": "Administrator or small-business owner whose spreadsheet totals disagree with a calculator",
      "purchase_trigger": "A sum is a few pounds out, ignores some rows or changes when the file is opened on another computer",
      "problem": "Numbers can look right on screen while being stored as text, left uncalculated or held at a different precision from the one displayed.",
      "related": [
        "platform-microsoft-excel",
        "spreadsheet-repair-broken-formulas",
        "guide-excel-error-values-trace",
        "example-quote-line-rounding",
        "collection-spreadsheet-messy-workbook-diagnosis-order"
      ],
      "provenance": [
        {
          "label": "Change formula recalculation, iteration, or precision in Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/change-formula-recalculation-iteration-or-precision-in-excel-73fc7dac-91cf-4d36-86e8-67124f6bcce4",
          "checked": "2026-10-11",
          "claims": [
            "Automatic recalculation is the default; in Manual mode formulas update only when you recalculate, for example with F9, and Ctrl+Alt+F9 recalculates every formula in all open workbooks whether or not it changed.",
            "Excel stores and calculates with 15 significant digits, and Set precision as displayed permanently trims stored values so that originals cannot be restored."
          ]
        },
        {
          "label": "Fix text-formatted numbers by applying a number format (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/fix-text-formatted-numbers-by-applying-a-number-format",
          "checked": "2026-10-11",
          "claims": [
            "Numbers stored as text align left and often carry a green triangle; they can cause problems with calculations or confusing sort orders and often arrive in imported or copied data.",
            "Fixes include Convert to Number from the error button and Paste Special with Multiply by 1; Ignore Error only removes the marker; TRIM and CLEAN remove spaces and nonprintable characters."
          ]
        },
        {
          "label": "Keeping leading zeros and large numbers (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/keeping-leading-zeros-and-large-numbers-1bf7b935-36e1-4985-842f-5dfa51f85fe7",
          "checked": "2026-10-11",
          "claims": [
            "Excel automatically removes leading zeros and converts large numbers to scientific notation, and has a maximum precision of 15 significant digits.",
            "Setting the column to Text format, or the data type to Text when importing with Power Query, keeps leading zeros."
          ]
        },
        {
          "label": "Floating-point arithmetic may give inaccurate result in Excel (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/troubleshoot/microsoft-365-apps/excel/floating-point-arithmetic-inaccurate-result",
          "checked": "2026-10-11",
          "claims": [
            "Excel follows the IEEE 754 specification, stores numbers with 15 digits of precision and cannot represent 0.1 exactly in binary.",
            "The ROUND function and Set precision as displayed are the two ways to compensate; the second cannot be undone.",
            "The formula =(43.1-43.2)+1 displays 0.899999999999999 when shown with 15 decimal places."
          ]
        },
        {
          "label": "Set rounding precision (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/set-rounding-precision-e5d707e3-07a8-4df2-810c-218c531eb06a",
          "checked": "2026-10-11",
          "claims": [
            "Set precision as displayed forces stored values to the displayed precision and can have cumulative effects that make data increasingly inaccurate; the ROUND function is offered as an alternative."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Three quiet causes of a wrong total",
          "body": [
            "A total can be wrong without any message because the workbook is not recalculating, because some entries are text that looks like numbers, or because what is displayed is not what is stored. Each has a quick test, and doing them in this order costs a few minutes."
          ],
          "items": [
            "Calculation set to manual, so formulas show old results.",
            "Numbers stored as text, so sums skip them and sorting is odd.",
            "Display formatting that hides more decimals than the sum uses."
          ]
        },
        {
          "heading": "Check the calculation mode first",
          "body": [
            "Excel recalculates automatically by default. In Manual mode, Microsoft's page explains, formulas update only when you recalculate, for example with F9, and Ctrl+Alt+F9 recalculates every formula in all open workbooks whether or not it changed. Write down a total, force a full recalculation, and look again. If the number moves, the workbook was stale and you have found at least one cause.",
            "Check this before anything else, because every later check is meaningless on stale results."
          ],
          "items": []
        },
        {
          "heading": "Numbers stored as text",
          "body": [
            "Microsoft describes numbers stored as text: they align left, often carry a small green triangle and cause calculation problems and confusing sort orders. They often arrive from imports, copies from other systems or cells that were formatted as text before a number was typed. A count of the column that is lower than the number of filled cells is a quick test.",
            "The page lists the fixes: Convert to Number from the error button, or Paste Special with Multiply by 1 for cells without a marker. Ignore Error only hides the marker and leaves the data unchanged. Extra spaces and nonprintable characters need TRIM and CLEAN. Some systems export a negative number with the minus sign after the value, which also arrives as text.",
            "Codes are different from amounts. Excel removes leading zeros from numbers and keeps 15 significant digits, so a postcode, account code or long reference number must be text from the start. Setting the column to Text, or the data type to Text when importing, keeps it."
          ],
          "items": []
        },
        {
          "heading": "Displayed is not stored",
          "body": [
            "A number format changes what you see, not the value underneath. Three amounts that each display as 1.30 can hold more decimals, and their sum can differ from the sum of the displayed figures. Excel also follows the IEEE 754 standard, so some decimals such as 0.1 cannot be stored exactly. Microsoft's own example, =(43.1-43.2)+1, displays 0.899999999999999 when shown to fifteen decimal places.",
            "The sensible remedy is the ROUND function, applied where the business rule says rounding happens. The workbook-wide option \"Set precision as displayed\" is risky: Microsoft says it permanently trims stored values, cannot be undone and can have cumulative effects that make data increasingly inaccurate. A penny difference between two quotes is usually a rounding-rule question, and the accompanying worked example shows one."
          ],
          "items": []
        },
        {
          "heading": "When it is a repair job, and when it is not",
          "body": [
            "If a workbook's formulas, text numbers or calculation setting are making outputs wrong, the fixed £195 formula repair covers one workbook of up to five sheets and eight outputs, accepted by check cases you calculated independently and a full recalculation that moves nothing. The price is an untested proposal and payment follows your sign-off.",
            "It does not decide your rounding policy, correct figures typed in by people, or give accounting advice. Say in the enquiry how many outputs you do not trust and whether macros are involved, and send no workbook at first."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-quote-line-rounding",
        "platform-microsoft-excel",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-excel-error-values-trace",
        "guide-mailmerge-fields-and-number-formats"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/excel-stale-or-text-numbers/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/excel-stale-or-text-numbers/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fexcel-stale-or-text-numbers%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fexcel-stale-or-text-numbers%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-excel-merged-cells-and-tidy-tables",
      "type": "guide",
      "title": "Merged cells stop Excel sorting and pivoting: how to flatten a printed-style report safely",
      "description": "Merged group labels, spacer rows and subtotal lines make a report readable on paper and unusable as data. See what merging does, what a flat table needs and how to prove nothing was lost.",
      "url": "/guides/excel-merged-cells-and-tidy-tables/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel"
      ],
      "buyer_role": "Office manager or administrator who inherited a report built for printing",
      "purchase_trigger": "Sorting fails because of merged cells, or a PivotTable will not build from the report",
      "problem": "A report designed to be read is not a table that can be filtered, sorted or summarised, and flattening it by hand risks assigning labels to the wrong rows.",
      "related": [
        "platform-microsoft-excel",
        "spreadsheet-flatten-merged-cell-report",
        "example-merged-report-flattened",
        "spreadsheet-make-workbook-set-import-ready",
        "collection-spreadsheet-messy-workbook-diagnosis-order"
      ],
      "provenance": [
        {
          "label": "Merge and unmerge cells (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/merge-and-unmerge-cells-5cbd15d5-9375-4540-907f-c673a93fcedf",
          "checked": "2026-10-11",
          "claims": [
            "When cells are merged only the upper-left cell's contents are kept and the contents of the others are deleted; unmerging moves the data to the left cell.",
            "Merge is unavailable when cells are formatted as an Excel table."
          ]
        },
        {
          "label": "Find merged cells (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/find-merged-cells-d02b2a5a-a08d-4641-8d4d-b3f233daca2c",
          "checked": "2026-10-11",
          "claims": [
            "Excel does not sort data in a column that contains merged cells.",
            "Find & Select, Find, Format, Alignment, Merge cells, Find All lists every merged cell."
          ]
        },
        {
          "label": "Create a PivotTable to analyze worksheet data (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576",
          "checked": "2026-10-11",
          "claims": [
            "Source data should be tabular with no blank rows or columns, one header row of distinct labels, no merged cells and one type of data per column.",
            "An Excel table as the source includes added rows when the PivotTable is refreshed; a PivotTable works from a snapshot and needs refreshing when the source changes."
          ]
        }
      ],
      "sections": [
        {
          "heading": "What merging actually does to your data",
          "body": [
            "Merging cells keeps only the upper-left cell's contents. Microsoft's page states that the contents of the other cells are deleted, and advises copying anything you need first. Unmerging reverses the shape, not the loss: the page says the data in the merged cell moves to the left cell, so a region name that appeared to sit above three rows now sits in one cell with two empty cells below it.",
            "This matters in two ways. A report where a label covers several rows has that label stored once. And if someone merged cells that each held different data some time ago, the discarded values are gone from the file."
          ],
          "items": []
        },
        {
          "heading": "What a flat table needs",
          "body": [
            "Microsoft's guidance for PivotTable source data describes what any analysis wants: tabular layout, no blank rows or columns, a single header row of distinct labels, no merged cells and one kind of data in each column. It also says an Excel table as the source includes rows added later when the PivotTable is refreshed.",
            "The practical consequence is that every record carries its own group label, so each row can be sorted or filtered alone. Spacer rows, repeated header blocks and subtotal lines are layout, not records. Left in, they get counted, double-counted or sorted into the middle of the data."
          ],
          "items": [
            "One header row.",
            "One record per row, with every label repeated on every row it applies to.",
            "No subtotal rows inside the data; totals are recomputed beside it.",
            "One data type per column."
          ]
        },
        {
          "heading": "Find the merges before you touch them",
          "body": [
            "Make a copy of the file. In Excel, choose Find & Select, then Find, then Format, then the Alignment tab, tick Merge cells and select Find All. Microsoft documents this route; it lists every merged cell. Count them and note which are title blocks, which are group labels and which are column headings. Excel does not sort a column that contains merged cells, so any merge inside the data area must go before sorting is possible."
          ],
          "items": []
        },
        {
          "heading": "Flatten without losing meaning",
          "body": [
            "Unmerge, then fill each label down onto the rows it covered, but only where you can say what it covered. Then list the spacer, repeated heading and subtotal rows by type before removing them. Prove that nothing else was lost: the number of original rows must equal the number of records plus the number of removed rows, each numeric column's total over the records must match the total in the original records, and each original subtotal should equal a recomputed one.",
            "Finish with a sample. Take twenty records at random and compare each one's group label with the label that covered it in the original. This is the check that catches a label filled onto the wrong rows, which no total will reveal."
          ],
          "items": []
        },
        {
          "heading": "Cases that do not fit, and the paid job",
          "body": [
            "A label that could belong to the rows above or the rows below cannot be flattened safely until someone who knows decides. Layouts that change from block to block need a rule for each. Data already lost to earlier merging cannot be recovered. The numbers in the original may also be wrong; flattening does not make them right.",
            "One sheet of up to 5,000 rows in one repeating layout can be flattened as a job from £145 (an untested proposal, with the final price confirmed after we see the layout description), accepted by the single header row, a merged-cell search that finds nothing, the row and total reconciliation and the label sample, with payment after your sign-off. It does not build a PivotTable or dashboard. The first enquiry needs a description of the layout and the row count, never the report."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-merged-report-flattened",
        "platform-microsoft-excel",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-spreadsheet-import-readiness"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/excel-merged-cells-and-tidy-tables/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/excel-merged-cells-and-tidy-tables/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fexcel-merged-cells-and-tidy-tables%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fexcel-merged-cells-and-tidy-tables%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-excel-date-serials-and-regional-order",
      "type": "guide",
      "title": "Dates sort wrongly or change between computers: serial numbers, text dates and day-month order",
      "description": "Excel stores dates as numbers and reads typed dates through regional settings. Learn how text dates, a four-year offset and day-first entries produce wrong sorting, and what to do with rows that cannot be settled.",
      "url": "/guides/excel-date-serials-and-regional-order/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel"
      ],
      "buyer_role": "Administrator who combines lists from several people or offices",
      "purchase_trigger": "A date column sorts out of order, or dates in the same file mean different things to different people",
      "problem": "A date column can mix real dates, text and day-first and month-first entries, and some of those cannot be told apart from the data alone.",
      "related": [
        "platform-microsoft-excel",
        "spreadsheet-normalise-mixed-dates-and-numbers",
        "spreadsheet-make-workbook-set-import-ready",
        "guide-spreadsheet-import-readiness",
        "collection-spreadsheet-messy-workbook-diagnosis-order"
      ],
      "provenance": [
        {
          "label": "Date systems in Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/date-systems-in-excel",
          "checked": "2026-10-11",
          "claims": [
            "Excel stores a date as a serial number counted from a start date; the 1900 and 1904 systems differ by 1,462 days (four years and one day).",
            "July 5, 2011 is 40729 in the 1900 system and 39267 in the 1904 system, and dates pasted between workbooks that use different systems can be shifted unless converted."
          ]
        },
        {
          "label": "Change the date system, format, or two-digit year interpretation (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/change-the-date-system-format-or-two-digit-year-interpretation-aaa2159b-4ae8-4651-8bce-d4707bc9fb9f",
          "checked": "2026-10-11",
          "claims": [
            "Two-digit years 00 to 29 are interpreted as 2000 to 2029 and 30 to 99 as 1930 to 1999; the page recommends four-digit years.",
            "A mismatch of dates between workbooks can be corrected by adding or subtracting 1462 with Paste Special."
          ]
        },
        {
          "label": "Convert dates stored as text to dates (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/convert-dates-stored-as-text-to-dates",
          "checked": "2026-10-11",
          "claims": [
            "Text dates are left-aligned; DATEVALUE returns a serial number that needs a date format applied, and January 1, 1900 is serial number 1.",
            "Error checking can flag text dates with two-digit years and offer to convert them to 20XX or 19XX."
          ]
        },
        {
          "label": "DATEVALUE function (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/functions/datevalue-function",
          "checked": "2026-10-11",
          "claims": [
            "DATEVALUE turns text into a serial number; a missing year uses the current year from the computer's clock, time information is ignored, and text outside January 1, 1900 to December 31, 9999 returns #VALUE!.",
            "The page warns that the system date setting may change the results shown."
          ]
        },
        {
          "label": "Stop automatically changing numbers to dates (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/stop-automatically-changing-numbers-to-dates",
          "checked": "2026-10-11",
          "claims": [
            "Excel changes entries such as 12/2 to 2-Dec, and the page says there is no way to turn this off.",
            "Preformatting cells as Text, or typing an apostrophe first, prevents the change."
          ]
        },
        {
          "label": "W3C Note: Date and Time Formats",
          "url": "https://www.w3.org/TR/NOTE-datetime",
          "checked": "2026-10-11",
          "claims": [
            "The profile gives YYYY-MM-DD as the complete date format and is meant for standards that need an unambiguous representation of dates and times."
          ]
        },
        {
          "label": "Format a date the way you want (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/format-a-date-the-way-you-want-8e10019e-d5d8-47a1-ba95-db95123d273e",
          "checked": "2026-10-11",
          "claims": [
            "When you type something Excel reads as a date, it formats it according to the default date setting in Control Panel.",
            "Date formats that begin with an asterisk change when the regional configuration changes; formats without an asterisk do not."
          ]
        },
        {
          "label": "How to correct a #VALUE! error (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/how-to-correct-a-value-error",
          "checked": "2026-10-11",
          "claims": [
            "For dates stored as text that do not match the system date format, a real date can be built with a formula such as =DATE(RIGHT(A1,4),MID(A1,4,2),LEFT(A1,2)), adjusted to the layout."
          ]
        }
      ],
      "sections": [
        {
          "heading": "A real date is a number",
          "body": [
            "Excel stores a date as a serial number: the count of days from a start date. Microsoft documents two systems. The 1900 system counts from January 1, 1900, and the 1904 system from January 1, 1904. They differ by 1,462 days, which is four years and one day. Microsoft's example: July 5, 2011 is 40729 in the 1900 system and 39267 in the 1904 system.",
            "When dates are pasted between workbooks that use different systems, they can arrive shifted by those 1,462 days. A tell-tale sign is a whole column of dates that is almost exactly four years out. Microsoft describes correcting this by adding or subtracting 1462 with Paste Special."
          ],
          "items": []
        },
        {
          "heading": "A text date is not a date",
          "body": [
            "Entries that look like dates but are text align to the left, will not sort chronologically and cannot be used in date arithmetic. DATEVALUE converts text to a serial number, which then needs a date format applied. Its documentation notes that a missing year takes the current year from the computer's clock, that time information is ignored and that text outside the supported range returns an error.",
            "When the text does not match what your system expects, Microsoft's #VALUE! page suggests building a real date from its parts, for example a DATE formula that picks the year, month and day out of a day-first text entry. Such a formula embeds a decision about order, so it is only safe for a column whose order you know."
          ],
          "items": [
            "Left-aligned dates are text.",
            "A COUNT of the column lower than the number of filled cells means some entries are text.",
            "Convert on a copy, keep the original column and look at a sample afterwards."
          ]
        },
        {
          "heading": "Day-month order is interpreted, not stored in the text",
          "body": [
            "The text 03/04/2026 does not say which number is the day. Microsoft's documentation shows Excel turning typed entries into dates automatically, such as 12/2 into 2-Dec, and says there is no way to turn this off; its workaround is to format cells as Text first or to type an apostrophe. Microsoft also says that when you type something Excel reads as a date, it formats it according to the default date setting in Control Panel, and that date formats beginning with an asterisk change when the regional configuration changes. The DATEVALUE page warns that the system date setting may change what it shows. The upshot is that a typed date depends on the computer it was typed on.",
            "The result is that a column built from several people's typing can hold some entries read as day-first and some as month-first, both valid and both wrong for someone. When both numbers are 12 or less and differ, nothing in the cell can settle it."
          ],
          "items": []
        },
        {
          "heading": "Two-digit years and the cutoff",
          "body": [
            "With two-digit years, Microsoft says Excel reads 00 to 29 as 2000 to 2029 and 30 to 99 as 1930 to 1999, and recommends four-digit years. A date typed as 31 is read as 1931, which is wrong for a list of this year's invoices. The cutoff is a rule that must be agreed for the file, not assumed."
          ],
          "items": []
        },
        {
          "heading": "Settling ambiguity, and the paid job",
          "body": [
            "The honest approach is a written rule per source. For example: dates typed by the Leeds office are day-first, dates from the system export are year-month-day. Where a source is known, convert by its rule. Where it is not, list the row and let the owner decide. Output in the year-month-day form, which the W3C date-and-time note describes as an unambiguous representation, so the problem is not recreated.",
            "The clean-up job, from £125 with the final price confirmed after we see the column list, covers one file of up to 20,000 rows with up to three date columns and two number columns (an untested proposal). It is accepted by the conversion counts, a reference set of at least thirty dates you confirm from source documents and a list of every ambiguous row, with payment after your sign-off. It does not guess dates, handle time zones or decide which dates are right for your business. Send the column list and what you know about each source, not the file."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-date-ambiguity-matrix",
        "platform-microsoft-excel",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-report-pack-repeatable-export",
        "guide-spreadsheet-import-readiness"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/excel-date-serials-and-regional-order/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/excel-date-serials-and-regional-order/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fexcel-date-serials-and-regional-order%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fexcel-date-serials-and-regional-order%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-spreadsheet-contact-duplicate-match-rules",
      "type": "guide",
      "title": "Cleaning duplicates in a contact list: write the match rules before you delete anything",
      "description": "Removing duplicates is a set of decisions, not a button. Compare exact and near matches, avoid merging people who only share a field, and keep a log so every merge can be undone.",
      "url": "/guides/spreadsheet-contact-duplicate-match-rules/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel"
      ],
      "buyer_role": "Office manager or owner responsible for a mailing, membership or customer list",
      "purchase_trigger": "The same organisation appears several times, or a quick clean-up removed rows that were different people",
      "problem": "A list can hold the same contact more than once and also hold different contacts who share an address, and one tool cannot tell the two apart without rules.",
      "related": [
        "platform-microsoft-excel",
        "spreadsheet-remove-duplicate-contacts",
        "spreadsheet-make-workbook-set-import-ready",
        "guide-spreadsheet-import-readiness",
        "collection-spreadsheet-messy-workbook-diagnosis-order"
      ],
      "provenance": [
        {
          "label": "Filter for unique values or remove duplicate values (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/filter-for-unique-values-or-remove-duplicate-values-ccf664b0-81d6-449b-bbe1-8daaec1e83c2",
          "checked": "2026-10-11",
          "claims": [
            "Remove Duplicates compares what appears in the cell, not the stored value: a date shown as 3/8/2006 and the same date shown as Mar 8, 2006 count as different.",
            "You choose the columns that form the key; a match removes the whole row, the first occurrence is kept and removal is permanent, so the page recommends copying the original data first."
          ]
        },
        {
          "label": "Keep or remove duplicate rows (Power Query) (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/keep-or-remove-duplicate-rows-power-query-d9cffc69-dc5d-4d94-8b66-72779688874d",
          "checked": "2026-10-11",
          "claims": [
            "Power Query removes duplicates based on the columns you select, which can be contiguous or discontiguous."
          ]
        }
      ],
      "sections": [
        {
          "heading": "A duplicate is defined by a rule, not by the sheet",
          "body": [
            "Two rows are the same contact because of something you decide counts: the same email address, the same organisation name and postcode, the same person at the same organisation. Any rule makes two kinds of mistake. It can miss a real duplicate, which leaves the list untidy. Or it can merge two different contacts, which loses information about a real person or organisation. The second is usually worse, so a cautious rule leaves uncertain pairs alone."
          ],
          "items": []
        },
        {
          "heading": "What Excel's Remove Duplicates actually does",
          "body": [
            "Microsoft's page describes it precisely. You choose the columns that form the key. If every ticked column matches, the whole row is removed, including the columns you did not tick, and the first occurrence is kept. The comparison uses what is displayed in the cell, not the stored value, so the same date shown as 3/8/2006 and as Mar 8, 2006 counts as different. The removal is permanent apart from Undo, and the page recommends copying the original data first. Power Query's Remove Duplicates likewise works on the columns you select.",
            "These are sound tools for exact repeats. They are not designed for near-matches such as \"Brook & Sons Ltd\" and \"Brook and Sons Limited\", and they have no memory of what was removed."
          ],
          "items": [
            "Exact repeat, same displayed values: safe to merge by rule.",
            "Near match with different spelling: a rule or a review is needed.",
            "Same value, different people: the tools cannot tell, so you must."
          ]
        },
        {
          "heading": "Normalise a copy for comparing, and keep the original columns",
          "body": [
            "Build separate comparison columns: trim spaces, ignore letter case, standardise company suffixes such as Ltd and Limited, remove punctuation and standardise postcode spacing. Compare on those, but never overwrite the original values. If a merge is wrong, you want the original text back.",
            "Watch for chains. If A matches B and B matches C, but A and C differ in important ways, merging all three on the strength of the chain may be wrong."
          ],
          "items": []
        },
        {
          "heading": "Shared fields are not proof",
          "body": [
            "Two people can share an address, a phone number or a role mailbox such as an information address. Two organisations can share a building. A surname is not an identity. Write down, with examples, the cases where a shared field must not cause a merge, and test the rule against a handful of look-alike pairs that you know are different."
          ],
          "items": [
            "Same email, different names: could be a shared mailbox; review.",
            "Same name and postcode, different email: possibly the same person; review or merge by an explicit rule.",
            "Same organisation, different contacts: keep both; they may be different people."
          ]
        },
        {
          "heading": "Make every merge reversible, and know the limits",
          "body": [
            "A merge log records each surviving row, each row merged into it and the rule that matched. The row counts must reconcile: original rows equal cleaned rows plus merged-away rows. Uncertain pairs go to a review file with both rows left in the list. The original file is never altered.",
            "The fixed-scope job from £195 (an untested proposal) works on one list of up to 10,000 rows of products, parts, assets or companies that holds no personal data, with rules you write. It is accepted by the row reconciliation, a test set of at least 20 true duplicate pairs and 20 look-alike pairs with half of them kept back from us until the rules are fixed, the review file and an unchanged original. Payment follows your sign-off.",
            "Lists of people, with names, email addresses or phone numbers, are not covered. They are out of scope until a secure handover route exists, and none has been set up yet. We do not contact anyone named on a list, check whether addresses exist or advise whether you may use a list for marketing or whether you comply with data-protection law; that is a legal question for you. Work starts only after a secure way to hand the file over has been agreed in writing. The first enquiry contains only column headings, counts and invented examples, never the list."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-microsoft-excel",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-spreadsheet-import-readiness"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/spreadsheet-contact-duplicate-match-rules/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/spreadsheet-contact-duplicate-match-rules/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fspreadsheet-contact-duplicate-match-rules%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fspreadsheet-contact-duplicate-match-rules%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-convert-legacy-office-fidelity",
      "type": "guide",
      "title": "Old .doc and .xls files: what converting really changes and how to check a whole batch",
      "description": "Converting one old Office file is easy. Converting a folder needs an inventory, copies instead of overwrites, a decision about macros and a check that the new files match the old.",
      "url": "/guides/convert-legacy-office-fidelity/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel",
        "microsoft-word"
      ],
      "buyer_role": "Administrator responsible for a shared drive of old Word and Excel files",
      "purchase_trigger": "Compatibility Mode keeps appearing, or a software change means old formats must go",
      "problem": "A batch conversion run without an inventory and a check leaves nobody able to say which files converted, which failed and which changed.",
      "related": [
        "platform-microsoft-excel",
        "convert-legacy-office-files-batch",
        "archive-digitise-and-normalise-one-archive",
        "guide-archive-rename-manifest-path-limits",
        "collection-pdf-to-spreadsheet-diagnosis-order"
      ],
      "provenance": [
        {
          "label": "Converting documents to a newer format (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/converting-documents-to-a-newer-format-34ec742e-f0f9-4d95-bbe3-3ee8e30a86fa",
          "checked": "2026-10-11",
          "claims": [
            "Compatibility Mode appears in the title bar when a document was made in an earlier version of Word; File, Info, Convert converts the document without saving a copy, and Save As makes a separate copy.",
            "The page gives no itemised list of layout changes; Convert clears the compatibility options so layout follows current Word behaviour."
          ]
        },
        {
          "label": "Saving .doc to .docx (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/word/saving-doc-to-docx-or-dorm",
          "checked": "2026-10-11",
          "claims": [
            "It advises having a copy or backup of the original files and verifying the content or layout for any changes in the converted file.",
            "Documents with macros can be converted to the macro-enabled .docm format instead, and the Developer tab lists any macros."
          ]
        },
        {
          "label": "Use Excel with earlier versions of Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/use-excel-with-earlier-versions-of-excel-2fd9ffcb-6fce-485b-85af-fecfd651a5ac",
          "checked": "2026-10-11",
          "claims": [
            "Excel opens Excel 97-2003 workbooks in Compatibility Mode, and File, Info, Convert replaces the original with a copy in a current format, while Save As keeps the old file.",
            "The Compatibility Checker lists features that do not carry over to earlier formats, including data beyond 65,536 rows by 256 columns; the page is marked as retired because the versions it covers are out of support."
          ]
        },
        {
          "label": "Saving .xls to .xlsx or .xlsm (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/saving-xls-to-xlsx-xlsm",
          "checked": "2026-10-11",
          "claims": [
            "Workbooks that contain macros can be converted to the macro-enabled .xlsm format; the Developer tab lists macros.",
            "It advises keeping a backup and verifying the content or layout for any changes in the converted file."
          ]
        },
        {
          "label": "Excel specifications and limits (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/excel-specifications-and-limits",
          "checked": "2026-10-11",
          "claims": [
            "The worksheet size limit is 1,048,576 rows by 16,384 columns."
          ]
        }
      ],
      "sections": [
        {
          "heading": "What Compatibility Mode is telling you",
          "body": [
            "When Word or Excel opens a file made in an older version, the title bar says Compatibility Mode. Microsoft explains that it temporarily disables newer features so people on the earlier version can still work on the file, and keeps the existing layout. Converting removes that restriction. For Word, File, Info, Convert converts the document without saving a copy, and Save As makes a separate one. Excel works the same way: convert replaces the original with a current-format copy, while Save As keeps the old file.",
            "The practical rule for a batch follows directly: never use the in-place convert on originals. Always produce a copy."
          ],
          "items": []
        },
        {
          "heading": "What can change, and what Microsoft says about it",
          "body": [
            "Microsoft's pages are cautious. They do not list every layout change; the Word page says only that converting clears the compatibility options so the layout follows current behaviour. Both the Word and Excel pages tell you to keep a backup of the original and to verify the content or layout of the converted file for changes. On size, Excel's documentation says data beyond 65,536 rows by 256 columns is lost when a workbook is saved in the old format, while its specifications page gives the current worksheet limit as 1,048,576 rows by 16,384 columns.",
            "Macros need a decision. Both pages say files with macros can be saved in the macro-enabled formats, .docm and .xlsm, and that the Developer tab lists macros. Because Microsoft directs macro files to those formats, macro files must be identified first and handled separately, not converted along with everything else."
          ],
          "items": [
            "No itemised list of layout differences is published; the checks below are what we propose, not a vendor guarantee.",
            "The Excel documentation referred to here is marked as retired because the versions it covered are out of support."
          ]
        },
        {
          "heading": "Build the inventory before converting anything",
          "body": [
            "Count the files by type, size and folder depth. Identify which contain macros, which are password-protected and which cannot be opened at all. Record each file's checksum so you can show later that no original changed. Decide the target format for each type and what happens to macro files. This list is the contract: at the end, every file on it must be converted or listed as an exception with a reason."
          ],
          "items": []
        },
        {
          "heading": "Check the result in a way that finds differences",
          "body": [
            "For a sample of converted files, compare with the originals. For a Word file, compare the page count, measuring the original and the converted file in the same application, version and settings so that a difference in the application is not mistaken for a difference in the file, and look at pages with tables, images and headers. For an Excel file, compare the number of sheets, the number of formulas and a few key totals. List every difference found rather than judging it acceptable on the spot.",
            "Then compare the checksums of the originals before and after the job. A conversion that touched an original is a failure however good the new files look."
          ],
          "items": [
            "Inventory count = converted files + files listed as exceptions.",
            "Every original has the same checksum before and after.",
            "Page counts for an original and its converted file are measured in the same application, version and settings.",
            "The sample report lists each difference, not just a pass or fail mark."
          ]
        },
        {
          "heading": "Where the paid job fits, and where it does not",
          "body": [
            "Up to 300 old .doc and .xls files in one folder tree can be converted as a one-off job from £295 (an untested proposal). Converted copies go in a parallel folder structure, and files with macros become .docm or .xlsm only if you agree. The job is accepted by the inventory reconciliation, a sample that opens in the named application without a repair prompt or compatibility label, the page, sheet and formula comparisons measured in that same application, and unchanged originals. The conversion tool and the checking application are named, with versions, in the written scope. Payment follows your sign-off. It does not edit content, repair formulas, rewrite macros or advise on whether originals may be deleted.",
            "A whole archive that also contains scans and needs renaming is a larger scope, covered by the archive project. The first enquiry needs counts and types, never the files. Files with personal data are not covered, and work starts only after a secure way to hand the files over has been agreed in writing; no upload portal exists yet."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-archive-rename-manifest",
        "buyer-doc-office-manager-paper-and-pdf-backlog",
        "guide-archive-rename-manifest-path-limits"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/convert-legacy-office-fidelity/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/convert-legacy-office-fidelity/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fconvert-legacy-office-fidelity%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fconvert-legacy-office-fidelity%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-archive-rename-manifest-path-limits",
      "type": "guide",
      "title": "Renaming thousands of files: rules, collisions, path limits and a manifest that proves nothing changed",
      "description": "A bulk rename can overwrite files, break on reserved names or exceed path limits. A dry run, a rule check and a checksum manifest make it safe and reversible.",
      "url": "/guides/archive-rename-manifest-path-limits/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "file-archives"
      ],
      "buyer_role": "Office manager or administrator tidying a shared drive or archive",
      "purchase_trigger": "Nobody can find anything, or files will not copy to the new storage because of their names",
      "problem": "Bulk renames fail in ways that are hard to see afterwards: collisions, rejected names, paths that are too long and no record of what each file used to be called.",
      "related": [
        "archive-rename-organise-with-manifest",
        "archive-digitise-and-normalise-one-archive",
        "example-archive-rename-manifest",
        "guide-convert-legacy-office-fidelity",
        "collection-pdf-to-spreadsheet-diagnosis-order"
      ],
      "provenance": [
        {
          "label": "Naming Files, Paths, and Namespaces (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file",
          "checked": "2026-10-11",
          "claims": [
            "Windows file names cannot contain < > : \" / \\ | ? * or control characters, cannot be the reserved names CON, PRN, AUX, NUL, COM1 to COM9 or LPT1 to LPT9 (with or without an extension), and should not end with a space or a period.",
            "Case sensitivity should not be assumed: OSCAR, Oscar and oscar should be treated as the same name."
          ]
        },
        {
          "label": "Maximum Path Length Limitation (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation",
          "checked": "2026-10-11",
          "claims": [
            "In the Windows API the maximum path length is MAX_PATH, defined as 260 characters; from Windows 10 version 1607 long paths are possible only if a registry setting is enabled and the application declares it is long-path aware.",
            "The page says you may hit the limit when cloning a repository with long file names into a folder that itself has a long name."
          ]
        },
        {
          "label": "RFC 8493: The BagIt File Packaging Format",
          "url": "https://www.rfc-editor.org/rfc/rfc8493",
          "checked": "2026-10-11",
          "claims": [
            "A payload manifest lists each payload file exactly once with a checksum and a relative path, so a set of files can be checked for completeness and fixity.",
            "Its portability notes warn about Windows reserved names, restricted characters, files that differ only in case and names that differ only in Unicode normalisation form.",
            "The checksums protect against corruption and are not designed to be secure against active attacks."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Write the naming rule as a sentence first",
          "body": [
            "A rule you can say in one sentence can be applied, checked and explained to the next person. For example: year and month, then the subject, then a three-digit number, with no spaces. If you cannot write the rule down, a program cannot apply it. Decide what the rule is built from: the file's current name, its folder, a date in the name, its file metadata or a list you supply. Reading document contents to choose names is a different and larger job.",
            "Decide the folder structure at the same time. A structure that mixes subjects and years under the same level will cause as many problems as the names."
          ],
          "items": []
        },
        {
          "heading": "Check names against the rules of where the files are going",
          "body": [
            "Names that work on one system can fail on another. Microsoft lists characters Windows does not allow in file names, namely < > : \" / \\ | ? * and control characters, and reserved names such as CON, PRN, AUX, NUL, COM1 to COM9 and LPT1 to LPT9, with or without an extension. Names should not end in a space or a period. Windows should not be assumed to be case-sensitive, so OSCAR, Oscar and oscar are the same name there, which makes two files that differ only in case a collision.",
            "Path length is a separate limit. In the Windows API the maximum path is 260 characters, and longer paths work only where a setting is enabled and the application supports it. Microsoft notes you may hit the limit when files with long names go into a folder that itself has a long name. Deeply nested folders add up."
          ],
          "items": [
            "Reserved names and disallowed characters: flag them in the dry run.",
            "Case-only differences: treat as collisions.",
            "Path length: agree a limit below the system maximum so the files still fit when moved."
          ]
        },
        {
          "heading": "Run a dry run and approve it before anything changes",
          "body": [
            "A dry run produces a table of old path and proposed new path, with problems flagged, and changes nothing. A named person reads it and approves it. Collisions get a decision: add a number, merge as duplicates after comparison or leave for a person to name. The approval is what makes the later changes yours."
          ],
          "items": []
        },
        {
          "heading": "A manifest shows that nothing was lost or altered",
          "body": [
            "A manifest lists every file once, with its old path, new path, size and a checksum. A file that cannot be named safely still gets its row, marked held, so the counts always add up. The BagIt specification describes the same idea for packaging files: a manifest line pairs a checksum with a relative path, so you can check that the set is complete and unchanged. It protects against corruption and mistakes; it is not designed to defeat someone deliberately tampering.",
            "Compare checksums of each source file with the file at its new path. Counts must match across the source, the copy and the manifest. Test the reversal on a sample: restoring the original names from the manifest should work for every file in the sample."
          ],
          "items": []
        },
        {
          "heading": "Where the paid job fits, and where it does not",
          "body": [
            "One folder tree of up to 5,000 files can be copied and reorganised as a one-off job from £245 (an untested proposal), accepted by a manifest with a row for every file, matching counts and checksums, a rule check with held files listed, an approved dry run and a tested reversal. Payment follows your sign-off. The files must be handed over as a copy through a secure route agreed in writing first, because the checks compare the contents of every file; no upload portal exists yet, and we do not supply a program for you to run on your own copy. File and folder names can themselves hold a person's name, so folders whose names or contents hold personal data are not covered.",
            "It does not delete originals, remove duplicates by content, choose names by reading documents or advise on retention or legal holds. The first enquiry needs counts and a few example names, never files."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-archive-rename-manifest",
        "guide-convert-legacy-office-fidelity"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/archive-rename-manifest-path-limits/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/archive-rename-manifest-path-limits/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Farchive-rename-manifest-path-limits%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Farchive-rename-manifest-path-limits%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-mailmerge-fields-and-number-formats",
      "type": "guide",
      "title": "Mail merge and quotation templates: why numbers arrive wrong and how to test a batch",
      "description": "A merge copies cell values into a template. Currency symbols, percentages and codes come through differently from how they look, so calculations and formats must be settled before the merge.",
      "url": "/guides/mailmerge-fields-and-number-formats/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel",
        "microsoft-word"
      ],
      "buyer_role": "Owner or office manager who produces many similar quotations, letters or documents from a spreadsheet",
      "purchase_trigger": "A merged document shows a number without its currency symbol, a postcode without its zero, or a total that differs from the spreadsheet",
      "problem": "The merge copies values rather than appearance, and a template built without testing quietly sends out wrong or unfilled fields.",
      "related": [
        "platform-microsoft-excel",
        "quote-pack-from-customer-brief",
        "example-quote-line-rounding",
        "guide-excel-stale-or-text-numbers",
        "collection-spreadsheet-messy-workbook-diagnosis-order"
      ],
      "provenance": [
        {
          "label": "Prepare your Excel data source for a Word mail merge (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/word/prepare-your-excel-data-source-for-a-word-mail-merge",
          "checked": "2026-10-11",
          "claims": [
            "Postal codes need text format or leading zeros are lost; a percentage format multiplies the cell value by 100, so storing such a column as text is suggested.",
            "Merged numbers come through without currency or percent symbols, so symbols are added before or after the merge field in the Word document."
          ]
        },
        {
          "label": "Mail merge using an Excel spreadsheet (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/word/mail-merge-using-an-excel-spreadsheet",
          "checked": "2026-10-11",
          "claims": [
            "Column names in the spreadsheet should match the field names; all data to be merged should be on the first sheet.",
            "Preview Results steps through the records so you can see how each looks, and Finish & Merge offers Print Documents or Send E-mail Messages."
          ]
        },
        {
          "label": "ROUND function (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/functions/round-function",
          "checked": "2026-10-11",
          "claims": [
            "ROUND(number, num_digits) rounds to the given number of digits; examples include =ROUND(2.15, 1) returning 2.2 and =ROUND(-1.475, 2) returning -1.48."
          ]
        },
        {
          "label": "Floating-point arithmetic may give inaccurate result in Excel (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/troubleshoot/microsoft-365-apps/excel/floating-point-arithmetic-inaccurate-result",
          "checked": "2026-10-11",
          "claims": [
            "Excel stores 15 digits of precision, cannot represent 0.1 exactly in binary, and offers the ROUND function to compensate for rounding errors."
          ]
        }
      ],
      "sections": [
        {
          "heading": "What a merge does",
          "body": [
            "A mail merge connects a Word template to a spreadsheet. Each column name becomes a merge field, and for every row Word produces a document with the fields replaced by that row's cell contents. Microsoft says column names in the spreadsheet should match the field names, that all the data to be merged should be on the first sheet and that Preview Results lets you step through records to see how each looks. Finishing the merge offers printing or sending as email messages; for quotations that go to customers, you will want to read each one first."
          ],
          "items": []
        },
        {
          "heading": "Numbers and codes arrive differently from how they look",
          "body": [
            "Microsoft documents three traps. Merged numbers come through without currency or percent symbols, so a price shows as 50.00 and a rate as 20 unless you add the symbol before or after the merge field in the Word document. A percentage format in Excel multiplies the stored value by 100 on display, so a percentage column is safer stored as text if you want it to appear as written. Postal codes need text format, or leading zeros are lost.",
            "The same care applies to dates and to long reference numbers. Decide how each should print, and make the spreadsheet hold it in that form."
          ],
          "items": [
            "Add currency and percent symbols in the template, outside the field.",
            "Hold postcodes and codes as text.",
            "Check one record of each kind in Preview Results."
          ]
        },
        {
          "heading": "Do the arithmetic in the workbook, and decide the rounding rule",
          "body": [
            "The template should show results, not calculate them. Work out line totals and the grand total in the workbook, where they can be checked, and merge the finished figures. Decide the rounding rule in words: round each line, then add, or add, then round? The two can differ by a penny, as the worked example shows with invented figures.",
            "Excel stores 15 digits of precision and cannot hold some decimals such as 0.1 exactly, so apply the ROUND function at the point your rule says rounding happens, rather than relying on number formatting. Microsoft's ROUND page shows its behaviour with examples such as 2.15 to one decimal place giving 2.2."
          ],
          "items": []
        },
        {
          "heading": "Test with a proof set before the batch",
          "body": [
            "Pick about ten records on purpose: a normal one, a zero quantity, a quantity at a price break, an item missing from the price list, a very long description, a name with an accent or an apostrophe, a blank optional field. Calculate their totals by hand under the written rules. Merge them and compare. Look for unfilled fields, wrong symbols and any price that is not on the price list.",
            "Then merge the batch and check that the sum of the document totals equals the sum in the workbook. A proof sheet that lists each brief, each lookup and each calculation makes later checking possible."
          ],
          "items": []
        },
        {
          "heading": "Where the paid job fits, and where it does not",
          "body": [
            "One approved template, one price list with unique codes and up to 40 briefs can be turned into a batch of quotation documents with a proof sheet as a one-off job from £495 (an untested proposal). It is accepted by ten hand-calculated test briefs, a scan for unfilled fields and non-list prices, template layout checks and a total reconciliation. Payment follows your sign-off. You keep the template and workbook.",
            "It does not set prices or discounts, decide how tax applies, advise whether a quotation is binding, send anything to customers or connect to other systems. The first enquiry needs your rules in words and the counts, never the price list or customer details."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-quote-line-rounding",
        "platform-microsoft-excel"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/mailmerge-fields-and-number-formats/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/mailmerge-fields-and-number-formats/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fmailmerge-fields-and-number-formats%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fmailmerge-fields-and-number-formats%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-report-pack-repeatable-export",
      "type": "guide",
      "title": "Rebuilding the same report every week or month: how to make it repeatable and checkable",
      "description": "A recurring report drifts when it is rebuilt by hand. Fix the export contract, make refreshing change only the data, and add control totals so each period can be checked against its source.",
      "url": "/guides/report-pack-repeatable-export/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel"
      ],
      "buyer_role": "Operations lead, office manager or finance administrator who builds the same report each period",
      "purchase_trigger": "The report takes hours each time and has gone out with a wrong total before",
      "problem": "The method lives in one person's habits, so each period differs slightly, and nothing shows whether this period's pack matches its data.",
      "related": [
        "platform-microsoft-excel",
        "report-pack-every-period",
        "buyer-report-operations-lead-weekly-pack",
        "guide-spreadsheet-import-readiness",
        "guide-excel-date-serials-and-regional-order"
      ],
      "provenance": [
        {
          "label": "Create a PivotTable to analyze worksheet data (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576",
          "checked": "2026-10-11",
          "claims": [
            "An Excel table as the source includes added rows when the PivotTable is refreshed; a PivotTable works from a snapshot and needs refreshing when the source changes.",
            "Source data should be tabular with one header row, no merged cells, no blank rows or columns and one type of data per column."
          ]
        },
        {
          "label": "Combine files overview (Power Query, Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/power-query/combine-files-overview",
          "checked": "2026-10-11",
          "claims": [
            "Files with the same schema can be combined into one table; they must have the same file type and structure, including the same columns.",
            "Power Query analyses an example file, by default the first in the list, and builds an example query and a function query that are applied to every file."
          ]
        },
        {
          "label": "Refresh an external data connection in Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/refresh-an-external-data-connection-in-excel",
          "checked": "2026-10-11",
          "claims": [
            "Connections can be refreshed manually or on open; Power Query keeps a local cache that is not refreshed automatically, and a yellow bar warns when a preview may be up to a number of days old.",
            "External data may be blocked until connections are enabled, and stored passwords are not encrypted and not recommended."
          ]
        },
        {
          "label": "Change formula recalculation, iteration, or precision in Excel (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/change-formula-recalculation-iteration-or-precision-in-excel-73fc7dac-91cf-4d36-86e8-67124f6bcce4",
          "checked": "2026-10-11",
          "claims": [
            "Automatic recalculation is the default; in Manual mode formulas update only when you recalculate."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Why hand-built reports drift",
          "body": [
            "Each period someone opens the export, filters, copies, pastes and adjusts. A filter is missed, a range stops one row short, a date boundary moves a day or a pasted value replaces a formula. Only the finished pack is looked at, so the errors reach readers. When the one person who knows the steps is away, the report either stops or changes."
          ],
          "items": []
        },
        {
          "heading": "Write the export contract and the period rule",
          "body": [
            "A contract says what the export must look like every period: the column names and their order, the type of each column, the date format and how the period is defined. For example: all records dated from the first to the last day of the month, inclusive, by the order date. Add the first and last record dates and the row count you expect, so a short or duplicated export is noticed before it is used.",
            "Anything that is a business definition, such as what counts as a completed order, belongs in the contract and is changed only by the person who owns the definition."
          ],
          "items": [
            "Column names, order and types.",
            "Date format and period boundaries.",
            "Definitions of each reported figure.",
            "Expected row count and first and last record dates."
          ]
        },
        {
          "heading": "Build so that refreshing changes only the data",
          "body": [
            "Keep the data and the report on separate sheets. Microsoft says an Excel table as a PivotTable source includes rows added later when the PivotTable is refreshed, that a PivotTable works from a snapshot and that it must be refreshed when the source changes. Without a table, you must change the source range yourself, which is exactly the step people forget.",
            "If each period arrives as a separate file in a folder, Power Query can combine files that have the same file type and structure, including the same columns. It analyses an example file and applies the same steps to every file. That is the reason the export contract matters: the combination assumes every file has the same columns, so a changed column should be caught by a check and not absorbed into the report.",
            "Refreshing has its own cautions. Microsoft notes that Power Query keeps a local cache which is not refreshed automatically, and a yellow bar can warn that a preview may be days old. Check that the report really used this period's data."
          ],
          "items": []
        },
        {
          "heading": "Control totals and a note for every pack",
          "body": [
            "For each period, compute a few totals directly from the export, not from the report, and compare them with the same totals in the pack: row count, sum of the main amount, count of distinct categories and the first and last record dates. Record the result in a short note, together with anything unusual, such as a new category or a missing day. The note is what lets a reader trust the pack without redoing it."
          ],
          "items": []
        },
        {
          "heading": "What to do when the export changes, and where the paid job fits",
          "body": [
            "When columns are renamed, a new category appears or the date format changes, stop. Report what changed and decide the rule; do not let the report absorb it. The rules and the rule version used should be on the note.",
            "The standing service builds the pack from your export each period by your written rules and checks it with control totals, from £295 a month for one monthly pack from one export (an untested proposal, quoted by proposal), with the first month used to set up the layout and rules. Weekly packs are quoted separately, and the service starts only after a secure way to send each period's export has been agreed in writing; no upload portal exists yet. It does not connect to live systems, send the pack, change your definitions or give financial advice, and it is not a guarantee of delivery time. The first enquiry needs a description of the report and the export's column headings, never the data."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-microsoft-excel",
        "buyer-report-operations-lead-weekly-pack"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/report-pack-repeatable-export/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/report-pack-repeatable-export/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Freport-pack-repeatable-export%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Freport-pack-repeatable-export%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-spreadsheet-import-readiness",
      "type": "guide",
      "title": "Before you import a spreadsheet into another system: the checks that stop a failed load",
      "description": "Loads fail on blanks, types, dates, codes and keys. A column-by-column readiness sheet, the right CSV handling and a test on a copy catch most problems before they reach a live system.",
      "url": "/guides/spreadsheet-import-readiness/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "microsoft-excel",
        "csv"
      ],
      "buyer_role": "Administrator or owner preparing spreadsheet data for new software or a database",
      "purchase_trigger": "An earlier import failed on its first bad row, or a migration is waiting on messy spreadsheets",
      "problem": "Import tools are strict about layout, types, blanks and keys, and a spreadsheet that looks fine to a person can break them.",
      "related": [
        "platform-microsoft-excel",
        "spreadsheet-make-workbook-set-import-ready",
        "guide-excel-merged-cells-and-tidy-tables",
        "guide-excel-date-serials-and-regional-order",
        "guide-spreadsheet-contact-duplicate-match-rules",
        "collection-spreadsheet-messy-workbook-diagnosis-order"
      ],
      "provenance": [
        {
          "label": "Create a PivotTable to analyze worksheet data (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576",
          "checked": "2026-10-11",
          "claims": [
            "An Excel table as the source includes added rows when the PivotTable is refreshed; a PivotTable works from a snapshot and needs refreshing when the source changes.",
            "Source data should be tabular with one header row, no merged cells, no blank rows or columns and one type of data per column."
          ]
        },
        {
          "label": "PostgreSQL 18 documentation: COPY",
          "url": "https://www.postgresql.org/docs/current/sql-copy.html",
          "checked": "2026-10-11",
          "claims": [
            "In CSV format the default null string is an unquoted empty string, while an empty string value is written and read with double quotes; FORCE_NOT_NULL makes empty values read as zero-length strings.",
            "The default delimiter is a comma in CSV format and must be a single one-byte character, and COPY fails by default on an error such as a value that cannot be converted to its column type."
          ]
        },
        {
          "label": "PostgreSQL 17 release notes",
          "url": "https://www.postgresql.org/docs/17/release-17.html",
          "checked": "2026-10-11",
          "claims": [
            "PostgreSQL 17 added the COPY option ON_ERROR ignore, which discards rows that cannot be converted and lets the copy continue; the default behaviour is ON_ERROR stop."
          ]
        },
        {
          "label": "PostgreSQL 16 documentation: COPY",
          "url": "https://www.postgresql.org/docs/16/sql-copy.html",
          "checked": "2026-10-11",
          "claims": [
            "The PostgreSQL 16 COPY page lists no ON_ERROR option, and says COPY stops operation at the first error."
          ]
        },
        {
          "label": "RFC 4180: Common Format and MIME Type for CSV Files",
          "url": "https://www.rfc-editor.org/rfc/rfc4180",
          "checked": "2026-10-11",
          "claims": [
            "Fields containing line breaks, double quotes or commas should be enclosed in double quotes, a double quote inside a field is written as two, and each line should contain the same number of fields.",
            "The memo does not specify an Internet standard and notes that implementations differ considerably."
          ]
        },
        {
          "label": "Import or export text (.txt or .csv) files (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/excel/get-started/import-or-export-text-txt-or-csv-files",
          "checked": "2026-10-11",
          "claims": [
            "Opening a .csv directly makes Excel apply its default data format settings to each column, while the import wizard gives more control, for example to keep leading zeros.",
            "The list separator is a Windows setting and changing it affects the whole computer; a limit of 1,048,576 rows and 16,384 columns applies to import and export."
          ]
        },
        {
          "label": "Excel formatting and features that are not transferred to other file formats (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/excel-formatting-and-features-that-are-not-transferred-to-other-file-formats-8fdd91a3-792e-4aef-a5bb-46f603d0e585",
          "checked": "2026-10-11",
          "claims": [
            "Saving as CSV or tab-delimited text keeps only the text and values as displayed in the active sheet; formatting, graphics, objects and other worksheet content are lost."
          ]
        },
        {
          "label": "Keeping leading zeros and large numbers (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/keeping-leading-zeros-and-large-numbers-1bf7b935-36e1-4985-842f-5dfa51f85fe7",
          "checked": "2026-10-11",
          "claims": [
            "Excel automatically removes leading zeros and has a maximum precision of 15 significant digits; the Text data type in Power Query keeps leading zeros."
          ]
        },
        {
          "label": "W3C Note: Date and Time Formats",
          "url": "https://www.w3.org/TR/NOTE-datetime",
          "checked": "2026-10-11",
          "claims": [
            "YYYY-MM-DD is the profile's complete date format, meant for an unambiguous representation of dates."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Why imports fail on row 4,000",
          "body": [
            "A person reads a spreadsheet by eye and forgives a lot. A loader does not. It expects every column to hold one kind of value, every row to have the same number of fields and every key to be unique. The first row that breaks any rule stops the load or, worse, loads wrongly. Fixing the data in the order of the load's rules is faster than fixing errors one at a time as they appear."
          ],
          "items": []
        },
        {
          "heading": "Build a readiness sheet, one line per column",
          "body": [
            "For every column, record the type it should hold, the number of blank values, the number of distinct values and, for numbers and dates, the smallest and largest. For the column that identifies a record, record whether every value is unique. Microsoft's guidance on PivotTable source data gives the shape a clean table has: one header row of distinct labels, no merged cells, no blank rows or columns and one kind of data in each column. A readiness sheet shows where a table departs from that.",
            "Mixed types in one column are the most common finding: numbers and text together, or dates and text. Resolve those before anything else."
          ],
          "items": [
            "Type, blank count, distinct count, smallest and largest.",
            "Is the key column unique?",
            "Do the header names match the target's column list?"
          ]
        },
        {
          "heading": "Blanks are not all the same",
          "body": [
            "An empty cell can mean not applicable, not known or zero. The target system may treat them differently. PostgreSQL's COPY documentation shows one example: in CSV format the default null string is an unquoted empty string, while an empty string value must be quoted, and an option exists to read empty values as zero-length strings instead. Whatever system you use, decide what each blank means and make it explicit before loading."
          ],
          "items": []
        },
        {
          "heading": "Dates, codes and the CSV file itself",
          "body": [
            "Hold dates in the year-month-day form, which the W3C date note describes as unambiguous. Keep identifiers that look like numbers as text: Excel removes leading zeros and keeps only 15 significant digits. Microsoft says that opening a .csv directly applies Excel's default format to each column, while its import route allows more control, such as keeping leading zeros.",
            "Microsoft says saving as CSV keeps only the text and values as they are displayed, and that formatting, graphics and other content are lost, so the file carries what a cell shows, which is not always the stored value. The CSV format itself is looser than it looks. RFC 4180 says fields containing commas, double quotes or line breaks should be enclosed in double quotes, that each line should have the same number of fields, and that implementations differ considerably. Check the target's own rules for delimiter and quoting."
          ],
          "items": []
        },
        {
          "heading": "Test on a copy, load under your own checks, and where the paid job fits",
          "body": [
            "PostgreSQL's documentation says COPY fails by default if it meets an error such as a value that cannot be converted to its column type. From PostgreSQL 17 an option, ON_ERROR ignore, skips such rows; earlier versions, such as 16, have no such option and stop at the first error. Neither behaviour is a plan. Load a copy into a test system first, count rows loaded against rows sent and keep the rejected rows.",
            "A defined set of up to eight workbooks can be repaired, flattened, date-fixed and, for product, parts, asset or company lists with no personal data, de-duplicated into consistent tables, each with a readiness sheet, as a project from £1,500 (an untested proposal, quoted after we read the list). The steps run in one fixed order: formulas, then layout, then dates and numbers, then duplicates. The project is accepted by per-step reconciliations, the readiness sheets and your written acceptance of each table. It does not load data into any live system, decide what the data means or give accounting advice, and a passing readiness sheet does not prove a target will accept the data. Workbooks of people's details are not covered, and work starts only after a secure way to hand the files over has been agreed in writing; no upload portal exists yet. The first enquiry needs a list of the workbooks and what they must feed, never the data."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-merged-report-flattened",
        "example-date-ambiguity-matrix",
        "platform-microsoft-excel",
        "collection-spreadsheet-messy-workbook-diagnosis-order",
        "guide-excel-date-serials-and-regional-order",
        "guide-spreadsheet-contact-duplicate-match-rules",
        "guide-report-pack-repeatable-export"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/spreadsheet-import-readiness/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/spreadsheet-import-readiness/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fspreadsheet-import-readiness%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fspreadsheet-import-readiness%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-pdf-text-layer-or-scan-check",
      "type": "guide",
      "title": "Can this PDF be read as data? Check for a text layer before you plan an extraction",
      "description": "A PDF can hold selectable text or only a picture of each page. A two-minute test tells you which, and that decides the method, the checks and what anyone can honestly promise.",
      "url": "/guides/pdf-text-layer-or-scan-check/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "pdf-documents"
      ],
      "buyer_role": "Office manager or administrator who has to get data out of a PDF",
      "purchase_trigger": "Someone says the PDF can be converted to a spreadsheet and you do not know whether that is true for your file",
      "problem": "Two PDFs that look identical on screen can need completely different work, and the difference is only visible with a quick test.",
      "related": [
        "platform-pdf-documents",
        "extract-pdf-price-lists-to-spreadsheet",
        "pdf-scanned-batch-to-searchable-text",
        "guide-extract-reconciliation-method",
        "guide-pdf-scan-ocr-search-check"
      ],
      "provenance": [
        {
          "label": "pdfplumber README: table extraction and its limits",
          "url": "https://github.com/jsvine/pdfplumber",
          "checked": "2026-10-11",
          "claims": [
            "The project says it works best on machine-generated rather than scanned PDFs.",
            "Its table finder looks for explicit or implied lines, finds their intersections and builds cells from them; line detection can use lines, text or explicit strategies.",
            "It offers no text recognition and lists weak support for tables from OCR'd documents as a missing feature."
          ]
        },
        {
          "label": "Power Query PDF connector (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/power-query/connectors/pdf",
          "checked": "2026-10-11",
          "claims": [
            "Pdf.Tables returns any tables found in a PDF; options include a start and end page, combining similar tables on consecutive pages and enforcing border lines as cell boundaries.",
            "Where multi-line rows are not identified properly, the page says the data may need cleaning with further steps in Power Query.",
            "To import several PDF files at once, it points to a multi-file connector such as the Folder connector."
          ]
        },
        {
          "label": "pdftotext manual page (Poppler utilities)",
          "url": "https://manpages.debian.org/bookworm/poppler-utils/pdftotext.1.en.html",
          "checked": "2026-10-11",
          "claims": [
            "The -layout option tries to maintain the original physical layout of the text; -f and -l set the first and last page.",
            "Its bugs section says text in PDFs with badly corrupted font encodings cannot be extracted short of text recognition."
          ]
        },
        {
          "label": "Tesseract documentation: improving the quality of the output",
          "url": "https://tesseract-ocr.github.io/tessdoc/ImproveQuality.html",
          "checked": "2026-10-11",
          "claims": [
            "Tesseract works best on images with a resolution of at least 300 dpi.",
            "Line segmentation quality falls significantly when a page is too skewed; noise and dark scanner borders can also reduce accuracy."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Two kinds of PDF that look the same",
          "body": [
            "A PDF exported from a word processor, accounting package or spreadsheet usually stores each character as text placed at a position on the page. A PDF made by scanning paper or photographing it usually stores one picture per page. Both look the same when you open them. Only the first can be searched, selected and read by a program as text. Some files mix the two, such as a digital cover page followed by scanned appendices.",
            "A PDF does not store a table as a table. It stores pieces of text at positions, sometimes with ruled lines drawn around them. Tools that extract tables have to infer rows and columns from those positions or lines. The pdfplumber project describes exactly this: it looks for explicit or implied lines, finds where they cross, and builds cells from the intersections."
          ],
          "items": [
            "Text-based PDF: you can select a line, search for a word, and paste readable text elsewhere.",
            "Scanned PDF: selecting drags a box around the whole page, and searching finds nothing.",
            "Mixed PDF: test the first, middle and last pages separately and write down which page numbers are images."
          ]
        },
        {
          "heading": "A two-minute test that sends nothing anywhere",
          "body": [
            "Open the file in your usual PDF reader. Search for a word you can plainly see on the page. Then drag across a line of text, copy it and paste it into a plain text editor. Try this on at least three pages, including the last one.",
            "Do this locally. Free online converters ask you to upload the file, which means sharing the document with a third party. If the document is not yours to share, that is a problem before any extraction has started."
          ],
          "items": [
            "Readable pasted text with numbers as digits means a text layer exists.",
            "A blank paste, or characters that are nonsense, means there is no usable text layer on that page.",
            "Note the page numbers that fail. A mixed file needs two methods, and the failed pages are a separate job."
          ]
        },
        {
          "heading": "What each result means for the method",
          "body": [
            "With a text layer, a table can usually be read from the positions of its text. Microsoft documents a PDF connector for Excel that returns the tables it finds, with options for page ranges and for whether similar tables on consecutive pages are combined. The same page says rows that spread over several lines may not be identified properly and may need cleaning afterwards. The pdftotext manual page describes a layout option that tries to keep the original physical arrangement of text.",
            "Without a text layer there is nothing to extract yet. A text-recognition step must first create text from the page images, and its quality depends on the scan. Tesseract's documentation says it works best on images of at least 300 dpi and that heavy skew, noise and dark scanner borders reduce quality. That is why a scanned document cannot honestly be given a promised accuracy before anyone has tried it.",
            "These tools are named only because their documentation explains how PDFs behave. What a buyer receives is a checked spreadsheet, not a tool."
          ],
          "items": []
        },
        {
          "heading": "Look-alike problems that are not a missing text layer",
          "body": [
            "Sometimes text can be selected but pastes as nonsense. The pdftotext manual page says text in PDFs with badly corrupted font encodings cannot be extracted short of text recognition. Sometimes the text is real but pastes in a scrambled order, because reading order differs from the visual layout. And sometimes a digital PDF contains a table that was inserted as a picture, so the page has text but the table does not.",
            "Password protection and restricted copying are separate problems. If a file refuses to open or copy, ask the person who issued it before trying to get around it."
          ],
          "items": [
            "Nonsense characters: the font encoding is the problem; treat the page like a scan.",
            "Scrambled order: the text exists but the layout needs rules; extraction can still work.",
            "A table pasted as a picture: no table text exists for that table, even though the page has other text."
          ]
        },
        {
          "heading": "Where the paid jobs fit, and what to send first",
          "body": [
            "One text-based price list or table of up to 60 pages in a single layout can be turned into a spreadsheet by the one-off extraction job, from £295 after we have seen a redacted sample page and agreed the columns. It is accepted by page-by-page row counts, a sample compared field by field and an exceptions list, not by a promised accuracy. A scanned batch of up to 500 pages can be made searchable by a separate job from £195, accepted by a search test of terms you choose. Both prices are untested proposals, and payment follows the agreed checks and your sign-off.",
            "For a first enquiry, send the page count, whether the text could be selected, how many different layouts the document has and your wanted column headings. Do not send the document, and never one with personal or confidential data. Secure handling is agreed after scoping."
          ],
          "items": [
            "This guide does not establish that anyone has requested or paid for these jobs; they are new offers.",
            "Neither job is legal, financial or tax advice, and neither promises that every row or word is read correctly."
          ]
        }
      ],
      "incoming": [
        "example-pdf-price-list-reconciliation",
        "platform-pdf-documents",
        "buyer-doc-office-manager-paper-and-pdf-backlog",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "guide-extract-reconciliation-method",
        "guide-pdf-form-field-data-export",
        "guide-pdf-scan-ocr-search-check"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/pdf-text-layer-or-scan-check/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/pdf-text-layer-or-scan-check/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fpdf-text-layer-or-scan-check%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fpdf-text-layer-or-scan-check%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-extract-reconciliation-method",
      "type": "guide",
      "title": "How to check a PDF extraction is right: count, compare, and list what could not be read",
      "description": "Extraction mistakes are silent. Counting rows page by page, comparing a pre-chosen sample and keeping an exceptions list shows where a spreadsheet differs from its document.",
      "url": "/guides/extract-reconciliation-method/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "pdf-documents",
        "microsoft-excel"
      ],
      "buyer_role": "Office or operations manager who has to rely on data taken from PDFs",
      "purchase_trigger": "A spreadsheet built from a document looks right but nobody can show that it is complete",
      "problem": "Data copied or extracted from a document goes wrong quietly, and a total that matches can hide two mistakes that cancel each other out.",
      "related": [
        "platform-pdf-documents",
        "extract-pdf-price-lists-to-spreadsheet",
        "extract-pdf-form-fields-to-spreadsheet",
        "extract-pdf-batches-every-month",
        "example-pdf-price-list-reconciliation",
        "guide-pdf-text-layer-or-scan-check"
      ],
      "provenance": [
        {
          "label": "Power Query PDF connector (Microsoft Learn)",
          "url": "https://learn.microsoft.com/en-us/power-query/connectors/pdf",
          "checked": "2026-10-11",
          "claims": [
            "Pdf.Tables returns any tables found in a PDF; options include a start and end page, combining similar tables on consecutive pages and enforcing border lines as cell boundaries.",
            "Where multi-line rows are not identified properly, the page says the data may need cleaning with further steps in Power Query.",
            "To import several PDF files at once, it points to a multi-file connector such as the Folder connector."
          ]
        },
        {
          "label": "pdfplumber README: table extraction and its limits",
          "url": "https://github.com/jsvine/pdfplumber",
          "checked": "2026-10-11",
          "claims": [
            "The project says it works best on machine-generated rather than scanned PDFs.",
            "Its table finder looks for explicit or implied lines, finds their intersections and builds cells from them; line detection can use lines, text or explicit strategies.",
            "It offers no text recognition and lists weak support for tables from OCR'd documents as a missing feature."
          ]
        },
        {
          "label": "Keeping leading zeros and large numbers (Microsoft Support)",
          "url": "https://support.microsoft.com/en-us/office/keeping-leading-zeros-and-large-numbers-1bf7b935-36e1-4985-842f-5dfa51f85fe7",
          "checked": "2026-10-11",
          "claims": [
            "Excel automatically removes leading zeros and converts large numbers to scientific notation, and has a maximum precision of 15 significant digits.",
            "Setting the column to Text format, or setting its data type to Text when importing with Power Query, keeps leading zeros."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Why \"it looks right\" is not a check",
          "body": [
            "A PDF stores text at positions, so any extraction rebuilds rows from layout. Rebuilding goes wrong in ordinary ways. A description that wraps onto a second line becomes an extra row with an empty code. Two tight rows are joined into one. A heading repeated at the top of each page is read as data. Columns shift when one cell is empty. The result still looks like a clean table, so a quick scroll finds nothing.",
            "Microsoft's documentation for Excel's PDF connector says rows that spread over several lines may not be identified properly and may need cleaning, and its options combine similar tables on consecutive pages by default. Both facts are reminders that the tool's guess about table structure is something to check, not something to trust."
          ],
          "items": []
        },
        {
          "heading": "Check one: count the rows on every page",
          "body": [
            "Count the data rows on each source page, by eye or from item numbers, and write the number down before looking at the extraction. Then count the extracted rows that came from each page. A difference on a page points straight to where to look.",
            "Count per page, not only in total. One missing row on page 2 and one extra row on page 9 give a correct total and two errors. The accompanying worked example shows exactly this case with invented data."
          ],
          "items": [
            "Keep the source page number on every extracted row so counts can be done by filter.",
            "Do not count header lines repeated at the top of each page as records.",
            "A page with no difference has passed this check only; it has not been shown correct."
          ]
        },
        {
          "heading": "Check two: compare a sample you chose first",
          "body": [
            "Decide the sample before looking at the results: its size, how rows are picked and which rows must be included, such as the first and last row of each page and rows with long or unusual descriptions. Then compare every field of every sampled row with the document, not just the one that looks risky.",
            "Report the result as counts: for example, rows compared, fields compared, mismatches found and how each was corrected. Do not turn that into a percentage accuracy for the whole file. A sample shows the method works on those rows; it does not certify the rows nobody compared."
          ],
          "items": [
            "Write the sample size and selection method into the agreement before extraction starts.",
            "Have the comparison done by someone who did not run the extraction.",
            "If the sample finds a pattern, such as every wrapped description is wrong, fix the pattern and sample again."
          ]
        },
        {
          "heading": "Check three: use any totals the document carries, and test the types",
          "body": [
            "If the document itself has item counts, page totals or a grand total, compare them with the spreadsheet. Check that numeric columns really are numbers, that units and currency symbols stayed in their own columns and that codes with leading zeros kept them. Excel removes leading zeros from numbers it reads as numeric, so a code column should be handled as text from the start.",
            "A column that mixes numbers and text usually means a row was misread. Filter the column for the entries that fail and look at them against the page."
          ],
          "items": []
        },
        {
          "heading": "Keep an exceptions list instead of guessing",
          "body": [
            "When a row cannot be read with confidence, do not put a best guess in the data. Move it to a separate exceptions sheet with its page, its position and the raw text, and say why. The person who owns the document then decides each one. Agree beforehand how many exceptions would make the whole job worth stopping and re-scoping.",
            "The one-off extraction jobs for price lists and filled forms use these checks as their acceptance tests, from £295 and £245 respectively, and the monthly service repeats them for each batch. Those prices are untested proposals, payment follows your sign-off, and no job promises accuracy beyond the counts reported. The first enquiry needs only the page count and column headings, never the document."
          ],
          "items": [
            "The checks cannot tell you whether the source document itself is correct.",
            "They are not a substitute for your own review of the exceptions."
          ]
        }
      ],
      "incoming": [
        "example-pdf-price-list-reconciliation",
        "platform-pdf-documents",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "guide-pdf-text-layer-or-scan-check",
        "guide-pdf-form-field-data-export"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/extract-reconciliation-method/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/extract-reconciliation-method/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fextract-reconciliation-method%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fextract-reconciliation-method%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-pdf-form-field-data-export",
      "type": "guide",
      "title": "Filled-in PDF forms: where the answers live and when they are really gone",
      "description": "Answers typed into a fillable PDF are stored as named fields inside each file. Learn how to tell whether a returned form still has them, and how to decide what checkboxes and blanks become in a spreadsheet.",
      "url": "/guides/pdf-form-field-data-export/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "pdf-documents"
      ],
      "buyer_role": "Office or site manager who collects the same PDF form from many people",
      "purchase_trigger": "A batch of returned forms is waiting to be copied into a spreadsheet by hand",
      "problem": "Some returned forms still contain their answers as readable data and some contain only a picture of them, and the two are easily confused.",
      "related": [
        "platform-pdf-documents",
        "extract-pdf-form-fields-to-spreadsheet",
        "extract-pdf-batches-every-month",
        "guide-extract-reconciliation-method",
        "guide-pdf-text-layer-or-scan-check"
      ],
      "provenance": [
        {
          "label": "pypdf documentation: interactions with PDF forms",
          "url": "https://pypdf.readthedocs.io/en/stable/user/forms.html",
          "checked": "2026-10-11",
          "claims": [
            "Form field values can be read by name with get_fields or get_form_text_fields; the field type property is Button, Text, Choice or Signature and the value is held in /V, whose format depends on the field type.",
            "Radio buttons: get_fields returns the parent group while page annotations return the individual child buttons.",
            "Flattening turns field contents into regular page content and the leftover widgets can then be removed.",
            "A form may carry an XFA entry, and the page says the XFA form overrides the page content."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Where a form's answers are stored",
          "body": [
            "A fillable PDF form holds its questions as named fields. When someone types an answer, the value is stored with the field inside the file. The pypdf documentation describes reading these values by field name, and lists the field types as button, text, choice and signature, with the value held in a property whose format depends on the field type. Fields can be nested, so a field may have a full name made of parts, and a radio button group is stored as a parent with child buttons.",
            "This is why a form can be read directly from its stored field values, without any text recognition: the answer is data, not a picture. What is stored and what a reader displays can still differ, as the XFA note below explains. It also means a form is only as readable as its fields. If the field structure is missing, the answers may be on the page and nowhere else."
          ],
          "items": []
        },
        {
          "heading": "Three ways a returned form stops holding data",
          "body": [
            "First, the form may have been printed and scanned, or photographed. The file then holds page images only. Second, it may have been flattened, for example by printing to a new PDF or by a program that merges the answers into the page. The pypdf documentation describes flattening as turning field contents into regular page content, after which the leftover form widgets can be removed. Third, some forms use a different dynamic form format called XFA; the same documentation notes that an XFA entry can override the page content, so what you see and what the standard fields hold may differ."
          ],
          "items": [
            "Printed and rescanned: no fields at all; text recognition would be needed, with no promised accuracy.",
            "Flattened: the answers look present, but no field holds them.",
            "Dynamic-form variant: test a sample before promising anything about the batch."
          ]
        },
        {
          "heading": "Decide what a checkbox or a blank becomes",
          "body": [
            "A spreadsheet needs one rule per field type, written down before anyone extracts. For a checkbox: what does ticked become, what does unticked become, and what does never touched become? For a choice list: do you want the stored value or the label people saw? For a text field: is an empty answer blank, or a \"no answer\" marker? For a radio group: one column with the chosen option, or one column per option?",
            "Test one form of each kind. A form where someone ticked one option, left one blank and typed a multi-line comment shows quickly whether the rules hold together."
          ],
          "items": [
            "Write the rules in a short table beside the blank form.",
            "Keep dates exactly as typed and clean them in a separate step if they are mixed formats.",
            "Keep the original file name on every row so any answer can be traced back."
          ]
        },
        {
          "heading": "A safe first check, and the count that proves completeness",
          "body": [
            "Open two returned files, one from a person using a desktop reader and one from a phone or a printer-to-PDF route, and click into an answer. If you can change it, the answer is a field. Keep the blank template so the field list can be compared with what comes back.",
            "The completeness check is a count. Files received must equal rows written plus files listed as exceptions, with every file name appearing once. A file that arrived but produced neither a row nor an exception is a bug in the process."
          ],
          "items": []
        },
        {
          "heading": "Where the paid job fits, and where it does not",
          "body": [
            "The one-off form extraction reads up to 200 returned files from one fillable template into one spreadsheet, from £245, and is accepted by the file-count reconciliation, a sample compared with what a PDF reader displays and a field list matching the blank template. The monthly version, from £395 a month, repeats it for each month's batch and is sold by proposal. Both prices are untested proposals. The one-off job is paid after your sign-off, and the monthly service is billed monthly under written terms you agree first. Neither starts until a secure way to hand over the files has been agreed in writing; no upload portal exists yet.",
            "It does not fit forms that ask about health, money, legal matters or other personal subjects. For those, do not send anything; whether and how they could be handled would need a separate conversation. It also does not fit scanned forms. The first enquiry needs the blank form or its field list and the number of returned files, never completed forms."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-pdf-documents",
        "collection-pdf-to-spreadsheet-diagnosis-order"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/pdf-form-field-data-export/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/pdf-form-field-data-export/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fpdf-form-field-data-export%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fpdf-form-field-data-export%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-pdf-scan-ocr-search-check",
      "type": "guide",
      "title": "A scanned PDF cannot be searched: what text recognition can fix and how to test it",
      "description": "Scanning produces pictures of pages. Text recognition can add a hidden text layer, but quality depends on the scan. Test it with words you know are on known pages rather than trusting a percentage.",
      "url": "/guides/pdf-scan-ocr-search-check/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "pdf-documents"
      ],
      "buyer_role": "Archive or records administrator with scanned documents that cannot be searched",
      "purchase_trigger": "Searching a folder of scans finds nothing, and people open files one at a time to look for a word",
      "problem": "A scanned PDF holds page images. Without a text layer nothing in it can be searched, and a text layer made by recognition can be incomplete without looking incomplete.",
      "related": [
        "platform-pdf-documents",
        "pdf-scanned-batch-to-searchable-text",
        "archive-digitise-and-normalise-one-archive",
        "guide-pdf-text-layer-or-scan-check",
        "collection-pdf-to-spreadsheet-diagnosis-order"
      ],
      "provenance": [
        {
          "label": "OCRmyPDF introduction",
          "url": "https://ocrmypdf.readthedocs.io/en/latest/introduction.html",
          "checked": "2026-10-11",
          "claims": [
            "It adds text layers to images in PDFs, making scanned image PDFs searchable, and can produce a minimally altered PDF.",
            "Its image-processing options, such as deskew, integrate the OCR layer into the processed image; it defaults to PDF/A-2b output; its limitations section says that when Ghostscript is used it may transcode grayscale and colour images, potentially lossily.",
            "It says its accuracy may not match commercial solutions, says output quality depends on input quality and says it cannot recognise handwriting."
          ]
        },
        {
          "label": "Tesseract documentation: improving the quality of the output",
          "url": "https://tesseract-ocr.github.io/tessdoc/ImproveQuality.html",
          "checked": "2026-10-11",
          "claims": [
            "Tesseract works best on images with a resolution of at least 300 dpi.",
            "Line segmentation quality falls significantly when a page is too skewed; noise and dark scanner borders can also reduce accuracy."
          ]
        },
        {
          "label": "Library of Congress: PDF/A, PDF for Long-term Preservation",
          "url": "https://www.loc.gov/preservation/digital/formats/fdd/fdd000318.shtml",
          "checked": "2026-10-11",
          "claims": [
            "PDF/A is intended for long-term preservation of page-oriented documents and preserves their static visual appearance.",
            "Level B files made from scanned page images do not necessarily support indexing of the document text."
          ]
        }
      ],
      "sections": [
        {
          "heading": "What scanning produced, and what recognition adds",
          "body": [
            "A scanner or a phone camera produces an image of each page. The PDF wraps the images, so it looks like the paper but holds no text. Text recognition reads the images and creates characters that are stored in an invisible layer behind the picture. The OCRmyPDF documentation describes this: it adds text layers to images in PDFs, making scanned image PDFs searchable, and can produce a minimally altered PDF.",
            "A minimally altered PDF is not the same as an untouched one. The same documentation says that its image-processing options, such as deskew, integrate the text layer into the processed image, that it generates PDF/A-2b output by default, and, in its limitations section, that when Ghostscript is used it may transcode some images, potentially lossily. So the aim is a hidden text layer behind the page pictures, but the pictures in the new file can differ from the originals. That is one reason the originals are kept untouched and the searchable files are separate copies."
          ],
          "items": []
        },
        {
          "heading": "What decides how good the text is",
          "body": [
            "Recognition quality depends on the input. The Tesseract documentation says it works best on images with a resolution of at least 300 dpi, that line segmentation falls significantly when a page is too skewed, and that noise and dark borders from the scanner can make it read extra characters. OCRmyPDF adds that its output quality depends on the quality of the input, that its accuracy may not match commercial solutions and that it cannot recognise handwriting.",
            "In practice a faint photocopy, a crooked page, a small font or a stained page will recognise worse than a clean, straight, high-resolution scan. If you can still rescan, the cheapest improvement is usually a better scan, not a smarter program."
          ],
          "items": [
            "Resolution: very low resolution scans lose small characters.",
            "Skew: a crooked page upsets the program's idea of where the lines are.",
            "Noise and borders: specks and scanner edges can turn into false characters.",
            "Handwriting: not recognised reliably, so a handwritten page is outside what this can honestly promise."
          ]
        },
        {
          "heading": "Test it with words you already know",
          "body": [
            "Do not ask for, or accept, a single accuracy percentage for a mixed folder. Instead pick 15 to 20 words or numbers that you know appear on particular pages: a surname, a reference code, a place name, a hyphenated word, a figure. Vary them on purpose: some in headings, some in small print, some on the worst scans. After processing, search for each one and record whether it was found on the page where you know it appears.",
            "Report the result as counts: found, not found. Also list pages where the recognised text is empty or very sparse. This tells you what a search can and cannot be trusted to find, which is the thing you actually need to know."
          ],
          "items": []
        },
        {
          "heading": "Archival format is not the same as searchable",
          "body": [
            "People often ask for PDF/A to make an archive searchable. PDF/A is a format for long-term preservation: the Library of Congress description says it aims to preserve a document's static visual appearance over time. It also says that level B files made from scanned page images do not necessarily support indexing of the document text. A PDF/A file of scans can therefore be a perfect archive of pictures and still be unsearchable.",
            "Ask for an archival format if your archive policy needs one, but still run the search test, whatever the format."
          ],
          "items": []
        },
        {
          "heading": "Where the paid job fits, and where it does not",
          "body": [
            "Making up to 500 already-scanned pages with no personal or confidential content searchable is a one-off job from £195 (an untested proposal). It is accepted by a count of every file with a searchable copy or a stated reason, a search test on your terms held to a pass floor agreed in writing before processing (every term on a page classed clean in advance must be found, and every miss is shown on the page image), a list of poor pages, matching page counts and originals that are unchanged by checksum. Payment follows your sign-off. No accuracy is promised for the batch as a whole.",
            "It does not scan paper, read handwriting, correct the recognised text or handle documents containing personal or confidential information. If the real goal is a spreadsheet of values from the scanned pages, that is a different job with different checks. The first enquiry needs a page count, the language and a few words you would search for, never the documents. Work starts only after a secure way to hand the files over has been agreed in writing; no upload portal exists yet."
          ],
          "items": []
        }
      ],
      "incoming": [
        "platform-pdf-documents",
        "collection-pdf-to-spreadsheet-diagnosis-order",
        "guide-pdf-text-layer-or-scan-check"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/pdf-scan-ocr-search-check/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/pdf-scan-ocr-search-check/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fpdf-scan-ocr-search-check%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fpdf-scan-ocr-search-check%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "platform-xero",
      "type": "platform",
      "title": "Xero integrations: identity, limits, accounts and currency decide whether a sync can be trusted",
      "description": "What Xero documents for integration builders (contacts, invoice numbers, idempotency, paging, limits, accounts, lock dates, demo company) and which failures map to which guide or outcome.",
      "url": "/platforms/xero/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "xero"
      ],
      "related": [
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "Xero Accounting API: contacts",
          "url": "https://developer.xero.com/documentation/api/accounting/contacts",
          "checked": "2026-10-11",
          "claims": [
            "ContactID is the identifier Xero recommends for referencing a contact, and Xero warns contact name may stop being unique in future.",
            "ContactNumber (up to 50 characters) can be set through the API and is shown in the Xero interface as Contact Code; AccountNumber is user-defined and is not stated to be unique.",
            "A PUT only creates and returns an error if an existing contact matches the name or contact number, while a POST can create or update.",
            "Archived contacts are left out of contact lists unless includeArchived is requested."
          ]
        },
        {
          "label": "Xero: limits FAQ",
          "url": "https://developer.xero.com/faq/limits",
          "checked": "2026-10-11",
          "claims": [
            "At the time checked the FAQ states per-connected-organisation limits of 60 calls per minute, 5 concurrent calls and a daily limit (5,000 per 24 hours), plus 10,000 calls per minute across all organisations for an app.",
            "Going over a limit returns HTTP 429 with a Retry-After header giving seconds to wait; responses carry headers showing the remaining daily, minute and app-minute allowance.",
            "Xero suggests combining creates or updates in one request (about 50 items is practical under the 3.5MB size cap), paging (100 records at a time) and the If-Modified-Since header."
          ]
        },
        {
          "label": "Xero: OAuth 2.0 FAQ",
          "url": "https://developer.xero.com/faq/oauth2",
          "checked": "2026-10-11",
          "claims": [
            "Access tokens last 30 minutes and unused refresh tokens expire after 60 days, after which the user must authorise the app again.",
            "Each successful refresh returns a new refresh token that must be stored in place of the old one.",
            "If a refresh request gets no response, the previous refresh token can be retried for 30 minutes before the user must re-authorise."
          ]
        },
        {
          "label": "Xero: idempotent requests",
          "url": "https://developer.xero.com/documentation/guides/idempotent-requests/idempotency/",
          "checked": "2026-10-11",
          "claims": [
            "Xero accepts an Idempotency-Key header on POST, PUT and PATCH requests and ignores it on other methods.",
            "A key is kept for six minutes from its first use, may be at most 128 characters, is checked per app, and re-using it with a different request returns a 400.",
            "An error cached against a key is returned again on re-run, and idempotency is checked after rate limits so repeated calls still count toward them.",
            "Xero advises checking with a GET whether a resource was already created before retrying with a new key."
          ]
        },
        {
          "label": "Xero: account and payment mapping",
          "url": "https://developer.xero.com/documentation/best-practices/categorising-transactions/account-mapping/",
          "checked": "2026-10-11",
          "claims": [
            "Integrations should let users choose accounts from a filtered list, exclude accounts with status ARCHIVED, and not hard-code an account such as the sales account.",
            "Users can change their chart of accounts after set-up, so Xero advises validating stored mappings, before each call for infrequent integrations, and sending the user back to fix a missing or archived account.",
            "Payment accounts are filtered by account type BANK or by EnablePaymentsToAccount."
          ]
        },
        {
          "label": "Xero: the demo company",
          "url": "https://central.xero.com/s/article/Use-the-demo-company",
          "checked": "2026-10-11",
          "claims": [
            "Data added to the demo company is deleted when it resets automatically after 28 days, and it can be reset manually.",
            "Invoices cannot be sent from the demo company, and only the person who adds data can see it."
          ]
        }
      ],
      "sections": [
        {
          "heading": "What Xero gives an integration builder",
          "body": [
            "Xero documents stable identifiers (a contact's ContactID and a ContactNumber you can set through the API), a unique invoice number for sales invoices, an Idempotency-Key header that is kept for six minutes, paging with a pagination object, an If-Modified-Since header for incremental reads, per-organisation limits with a Retry-After response, a chart of accounts with active and archived accounts, lock dates, and a demo company that resets itself after 28 days. A trustworthy sync uses these deliberately; most failures come from using text that changes or limits that nobody read."
          ],
          "items": [
            "Identity: store ContactID or your own number in ContactNumber; do not match on names alone.",
            "Limits: 60 calls a minute and 5 concurrent calls per organisation at the time checked, with a daily limit.",
            "Connection: refresh tokens lapse after 60 days of non-use."
          ]
        },
        {
          "heading": "Failure families and where to go next",
          "body": [
            "Customers split across contacts, orders missing after a busy run, lines posting to the wrong account, foreign invoices with the wrong rate, expense bills without receipts, duplicate invoices after a retry and a sync that has quietly stopped are different failures with different fixes. Each has a guide that explains the mechanism, a safe first investigation and the point at which a paid, bounded job fits."
          ],
          "items": [
            "Duplicate contacts: the contact identity guide and the contact job.",
            "Missing orders: the rate-limit guide and its pacing and checkpoint job.",
            "Wrong accounts: the mapping-table guide and its fixed-price job.",
            "Wrong currency or rate: the currency direction guide and job.",
            "Duplicate invoices: the request-key guide and the retry-safety job.",
            "A sync that stopped: the refresh-token guide and the standing health service."
          ]
        },
        {
          "heading": "A safe test looks like this",
          "body": [
            "Test against the Xero demo company, never live records. Use invented customers, orders and amounts; do not send invoices; expect the demo data to disappear when it resets. Keep counts and identifiers from each step so you can show what changed. Lock dates and bank-reconciled transactions are decisions for the organisation's administrator and accountant."
          ],
          "items": []
        },
        {
          "heading": "Choose the actual outcome",
          "body": [
            "Fixing one known failure is a bounded job. Several failures at once, in an order that matters, is a project. Watching a sync month after month is a standing service. None of them includes bookkeeping, tax or audit advice, changes to posted or locked records or live changes under our control; your authorised account holder applies changes. Prices on the linked pages are untested published prices, and nothing starts without a written agreement."
          ],
          "items": [
            "The first enquiry uses invented examples, not credentials, invoices or customer records.",
            "Real records are shared only after agreement, through a secure handoff."
          ]
        }
      ],
      "incoming": [],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/platforms/xero/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/platforms/xero/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fplatforms%2Fxero%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fplatforms%2Fxero%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "tool-csv-header-diff",
      "type": "tool",
      "tool_kind": "csv-header-diff",
      "title": "CSV header diff: find missing, added, renamed and duplicate columns between two files",
      "description": "Paste two CSV header rows, or read the header of two small files in your browser, and see missing, added and likely renamed columns, duplicate and empty headers and the delimiter. Nothing is uploaded.",
      "slug": "csv-header-diff",
      "url": "/tools/csv-header-diff/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "csv",
        "supplier-feeds"
      ],
      "provenance": [
        {
          "label": "Header comparison implementation",
          "url": "https://syntheticindustry.ai/tools/assets/csv-header-diff.js",
          "checked": "2026-10-11",
          "claims": [
            "Columns are matched in three passes: identical text, then the same name after normalising case, spacing, punctuation, hidden characters and Unicode compatibility forms, then a similarity score against an editable threshold (default 0.60).",
            "Only the first record of each input is read, delimiters tried are comma, semicolon, tab and pipe, and quoting follows RFC 4180 conventions.",
            "The tool makes no network request and stores nothing; chosen files are read with FileReader in the browser and only the header row is placed in the page."
          ]
        },
        {
          "label": "RFC 4180: Common Format and MIME Type for CSV Files",
          "url": "https://www.rfc-editor.org/rfc/rfc4180",
          "checked": "2026-10-11",
          "claims": [
            "There may be an optional header line as the first line, with the same number of fields as the rest of the file.",
            "Fields containing line breaks, double quotes or commas should be enclosed in double quotes, and a double quote inside such a field is escaped by another double quote.",
            "Each line should contain the same number of fields throughout the file, and the RFC defines the comma as the only separator; semicolon, tab and pipe are common variants the RFC does not define."
          ]
        },
        {
          "label": "MDN: FileReader",
          "url": "https://developer.mozilla.org/en-US/docs/Web/API/FileReader",
          "checked": "2026-10-11",
          "claims": [
            "FileReader lets web applications read the contents of files stored on the user's computer, and can only read files the user has explicitly selected."
          ]
        },
        {
          "label": "MDN: TextDecoder constructor",
          "url": "https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/TextDecoder",
          "checked": "2026-10-11",
          "claims": [
            "By default a TextDecoder skips the byte order mark; the ignoreBOM option controls whether it is kept in the decoded text, which this tool uses so a byte order mark can be reported."
          ]
        }
      ],
      "related": [
        "guide-zapier-field-mapping",
        "guide-sheets-xero-line-item-shape"
      ],
      "sections": [
        {
          "heading": "What it compares",
          "body": [
            "Paste the header row of the file you expect (A) and the header row of the new or changed file (B). The tool reports the columns missing from B, the columns added in B, columns that look renamed, columns whose position moved, duplicate names within each header, empty header cells and the delimiter it found in each. You can also choose a CSV file for A or B: the browser reads the first part of the file, keeps only its header row and fills the box. The end of the header is found with the same quoting rules the comparison uses, so a stray double quote inside a name does not pull the rows below it into the box. If the header's opening quote is never closed in the part read, only the first line is kept and the page says so. The rest of the file is never placed on the page, and nothing is uploaded.",
            "Do not paste confidential data. A header row rarely contains any, but a pasted row can include data by mistake, and the tool shows what you give it on your own screen."
          ],
          "items": [
            "Only header names are compared. Data rows, types and values are out of scope.",
            "Leading blank lines are skipped and only the first record is read; a quoted header cell may span lines.",
            "Files are decoded as UTF-8, or UTF-16 when a byte order mark says so; other encodings can show replacement characters.",
            "Work limits keep the page responsive: up to 1,000 columns and 1,000,000 characters per side are accepted, and the similarity step has its own bounds, described under how columns are matched."
          ]
        },
        {
          "heading": "How columns are matched",
          "body": [
            "Matching runs in three passes and uses each column once. First, identical text. Second, the same name after normalising: Unicode compatibility forms are applied, hidden characters (byte order mark, zero-width characters, soft hyphen) are removed, and case, spaces and punctuation are ignored, so 'Unit Price', 'unit_price' and 'UNIT-PRICE' are one name. Accents are kept: 'Cafe' and 'Café' are different names, though similar enough to be offered as a guess.",
            "Third, for columns still unmatched, a similarity score. The score is the larger of two numbers: one minus the edit distance divided by the longer normalised name, and the overlap of the two word sets (words are split at spaces, punctuation and lower-to-upper camel-case boundaries). A pair is offered as a likely rename when the score is at least the threshold, which you can change from 0.40 to 1.00 (default 0.60). Pairs are assigned from the highest score down. A rename candidate is a guess to confirm, not a fact. To keep the page responsive, a name longer than 200 letters and digits (after normalising) is never scored for similarity, and scoring is skipped for all remaining columns if they would need more than 250,000 column pairs or more than 100 million character comparisons. When either bound applies the page says so in its warnings; identical and same-after-normalising matches are always checked."
          ],
          "items": [
            "'Order Qty' to 'Order Quantity' scores 0.62 and is offered at the default threshold but not at 0.70.",
            "'Qty' to 'Quantity' scores 0.38 and is not offered: very short abbreviations are missed, so check the missing and added lists as well.",
            "Repeated names are paired in the order they appear, so a second 'Price' with no partner is reported as missing or added.",
            "A name longer than 200 letters and digits can still match by identical text or the same name after normalising, but no rename guess is offered for it."
          ]
        },
        {
          "heading": "Delimiter, quoting and the problems it flags",
          "body": [
            "Each of comma, semicolon, tab and pipe is tried on the first record, honouring double quotes, and the one giving the most cells (at least two) wins; ties go to the earlier one. You can override either side. RFC 4180, the common description of CSV, defines only the comma, and says fields containing commas, double quotes or line breaks should be quoted with a quote inside a field doubled; semicolon, tab and pipe are common variants it does not define. If one file uses a semicolon and the other a comma, a parser set to one delimiter will read the other as a single column, so the tool warns about that.",
            "It also flags duplicate names inside one header (exact or only after normalising), empty header cells including a trailing delimiter, cells with no letters or digits, leading or trailing spaces, hidden characters, a byte order mark on only one side, and stray or unterminated quotes. A positional import that maps by column number needs the position list too: the tool says whether the shared columns kept their relative order and which ones moved."
          ],
          "items": [
            "A trailing delimiter makes an empty last header cell.",
            "Padded names such as ' Price' break exact-name matching in some importers even though they look the same on screen.",
            "A byte order mark can be glued onto the first column name by a parser that does not strip it."
          ]
        },
        {
          "heading": "What it cannot tell you",
          "body": [
            "It cannot tell you whether the data under a column means the same thing, whether units, currencies or date formats changed, or whether rows are the right shape: header names only. A renamed-looking pair may be two different fields and a genuinely renamed pair may not look similar. A header that was never a header (a file with no header row) will be compared as if it were one.",
            "For why a changed supplier layout needs a declared header contract and row-shape checks, and for a checklist of import checks, see the supplier-feed header drift guide and the supplier-feed import checks collection linked on this page. If you receive a new version of a supplier feed before each import, the first related outcome is a change report comparing two named versions of one feed by product key, with price and stock moves over thresholds you set; it does not decide whether to import the new file. If an import breaks on quotes, line breaks, a byte order mark or accents, the two CSV outcomes listed with this tool each cover one named file. None of them is needed to use this tool."
          ],
          "items": [
            "No file leaves your browser, and no input is put in a URL or stored.",
            "The result is a comparison of two strings of text, not a validation of your CSV."
          ]
        }
      ],
      "incoming": [],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/tools/csv-header-diff/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/tools/csv-header-diff/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Ftools%2Fcsv-header-diff%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Ftools%2Fcsv-header-diff%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "tool-invoice-rounding-bench",
      "type": "tool",
      "tool_kind": "invoice-rounding-bench",
      "title": "Invoice rounding bench: compare per-line and total rounding, half-up and half-even",
      "description": "Enter invoice lines and see net, tax and gross under four rounding rules side by side, in exact whole-number minor units, with the difference between summing lines and rounding the total. Not tax or accounting advice.",
      "slug": "invoice-rounding-bench",
      "url": "/tools/invoice-rounding-bench/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "accounting-sync",
        "google-sheets",
        "xero"
      ],
      "provenance": [
        {
          "label": "Rounding bench implementation",
          "url": "https://syntheticindustry.ai/tools/assets/invoice-rounding-bench.js",
          "checked": "2026-10-11",
          "claims": [
            "Quantity, unit price and tax rate are read as decimal text and scaled to whole-number integers; no floating-point value is used in any money calculation.",
            "Four rules are computed: round each line or round once on the total, each with half-up or half-even rounding of exact ties.",
            "Results are checked in the tool's test suite against an independent exact-fraction implementation over 400 seeded random invoices."
          ]
        },
        {
          "label": "Python decimal module: rounding modes",
          "url": "https://docs.python.org/3/library/decimal.html",
          "checked": "2026-10-11",
          "claims": [
            "ROUND_HALF_UP rounds to nearest with ties going away from zero.",
            "ROUND_HALF_EVEN rounds to nearest with ties going to the nearest even integer."
          ]
        }
      ],
      "related": [
        "zapier-sheets-to-xero-missing-columns",
        "guide-invoice-tax-amount-basis",
        "example-invoice-tax-rounding-reconciliation",
        "guide-sheets-invoice-value-rendering",
        "buyer-finance-manager-invoice-integration"
      ],
      "sections": [
        {
          "heading": "What it computes",
          "body": [
            "Enter one or more invoice lines (quantity, unit price and tax rate in percent), pick the number of minor-unit digits your currency has, and choose the rounding rule you want to compare against. The tool calculates net, tax and gross under four rules and shows them side by side, flags every figure that differs from your chosen rule, and reports 'line sum minus total': how far apart summing rounded lines and rounding once on the total are, in minor units (pence, cents). It also shows the exact, unrounded figures and which lines land on an exact half.",
            "Example values are loaded when the page opens so the difference is visible straight away. Replace them with your own. Nothing you enter is sent or stored."
          ],
          "items": [
            "Round each line: the line net (quantity times unit price) is rounded to a whole minor unit, tax is calculated on that rounded net and rounded again, and the invoice totals are the sums of the rounded lines.",
            "Round once on the total: exact line amounts are added up unrounded, then the invoice net and the invoice tax are each rounded once; gross is rounded net plus rounded tax.",
            "Half-up rounds an exact half away from zero (0.5 to 1, -0.5 to -1). Half-even rounds an exact half to the even neighbour (0.5 to 0, 1.5 to 2, 2.5 to 2); the two differ only on exact halves.",
            "Negative quantities or prices are treated as credit lines and round symmetrically."
          ]
        },
        {
          "heading": "A worked example you can check by hand",
          "body": [
            "Three invented lines, each quantity 1, unit price 0.03 and tax rate 20%, in a currency whose minor unit is 0.01. Each line's exact tax is 0.006, which is 0.6 of a minor unit. Rounding each line gives 1 minor unit of tax per line, so 3 in total. Adding the exact tax first gives 1.8 minor units, which rounds to 2. The two rules disagree by one minor unit of tax and so of gross. A second example: one line at 0.50 with 5% tax has exact tax of 2.5 minor units, an exact half, so half-up gives 3 and half-even gives 2.",
            "Both figures are arithmetic on invented numbers. They are not a recommendation to apply that rate or that rounding rule to any real supply."
          ],
          "items": [
            "With 500 identical copies of the first line, summing rounded lines gives 5.00 of tax and rounding the total once gives 3.00, a gap of 2.00: the difference grows with the number of lines.",
            "Which rule is right depends on your accounting system, your invoice format and the tax rules that apply to you; this tool does not decide that."
          ],
          "code": "Net  0.09 (3 x 0.03) at 20%: exact tax 0.018, exact gross 0.108\n\nRule                                Net    Tax    Gross\nRound each line, half-up            0.09   0.03   0.12\nRound each line, half-even          0.09   0.03   0.12\nRound once on the total, half-up    0.09   0.02   0.11\nRound once on the total, half-even  0.09   0.02   0.11\n\nLine sum minus total: tax +1 minor unit, gross +1 minor unit"
        },
        {
          "heading": "Assumptions and the arithmetic",
          "body": [
            "Every value is read as decimal text and scaled to a whole number: quantity, unit price and tax rate may each have up to four decimal places, up to fifteen digits before the decimal point, and the tool uses exact integer arithmetic throughout, so there is no floating-point drift. Sub-minor-unit prices such as 0.0125 are exact. Use a full stop as the decimal separator and no thousands separators: a comma is refused rather than guessed. Tax rates must be from 0 to 100 percent."
          ],
          "items": [
            "One tax rate per line; no discounts, shipping, tax-inclusive prices or currency conversion.",
            "Some systems round tax per rate group, per line on the gross amount, or only at the invoice level; check which one yours uses before relying on any column.",
            "Up to 500 lines can be calculated; the page offers 100."
          ]
        },
        {
          "heading": "What it cannot tell you",
          "body": [
            "It is not tax or accounting advice and does not know which jurisdiction, invoice type or accounting system you use. Accounting systems differ in where they round, and it cannot reproduce a specific product's behaviour. If your destination total differs from your source total, this tool can show whether a rounding rule alone could explain the gap, but a gap larger than the spread it shows has another cause, such as an amount entered as tax-inclusive that was treated as tax-exclusive. For that, see the invoice tax amount basis guide and the synthetic reconciliation example linked on this page.",
            "If an agreed column is missing from a Sheets-to-Xero mapping, the related outcome is to send every mapped Google Sheets invoice field to Xero using one disposable test row. That outcome does not include fixing tax configuration or historical invoices."
          ],
          "items": [
            "A one-unit tolerance can hide a real policy mismatch; agree the policy first.",
            "Nothing here is a quote or an offer to fix an invoice integration."
          ]
        }
      ],
      "incoming": [],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/tools/invoice-rounding-bench/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/tools/invoice-rounding-bench/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Ftools%2Finvoice-rounding-bench%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Ftools%2Finvoice-rounding-bench%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "tool-pack-unit-price-calculator",
      "type": "tool",
      "tool_kind": "pack-unit-price-calculator",
      "slug": "pack-unit-price-calculator",
      "url": "/tools/pack-unit-price-calculator/",
      "title": "Pack and case unit-price calculator: compare supplier prices per single unit",
      "description": "Enter supplier price lines priced per unit, pack or case. Get the exact price per single unit, the working, and the lines whose pack basis is unclear. No floating point.",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "provenance": [
        {
          "label": "Calculator implementation and tests",
          "url": "https://syntheticindustry.ai/tools/assets/pack-unit-price-calculator.js",
          "checked": "2026-10-11",
          "claims": [
            "Prices are held as exact fractions of integers; lines are compared by cross-multiplication, never by rounded or floating-point values.",
            "Rounding happens only when a value is displayed, half up, and the exact value is shown beside it."
          ]
        },
        {
          "label": "SIX Group: ISO 4217 currency list (list one), published 2026-09-17",
          "url": "https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-one.xml",
          "checked": "2026-10-11",
          "claims": [
            "Minor-unit digits used for display: 2 for GBP, USD, EUR and most listed currencies; 0 for JPY, KRW, CLP, ISK, UGX and VND; 3 for KWD, BHD, JOD, OMR and TND."
          ]
        },
        {
          "label": "Python documentation: fractions",
          "url": "https://docs.python.org/3/library/fractions.html",
          "checked": "2026-10-11",
          "claims": [
            "Fraction(1.1) does not return Fraction(11, 10): a binary float is not exactly the decimal that was typed."
          ]
        },
        {
          "label": "Python documentation: decimal",
          "url": "https://docs.python.org/3/library/decimal.html",
          "checked": "2026-10-11",
          "claims": [
            "Decimal numbers can be represented exactly, whereas numbers like 1.1 and 2.2 do not have exact representations in binary floating point.",
            "ROUND_HALF_UP is round to nearest with ties going away from zero."
          ]
        },
        {
          "label": "W3C: Model for Tabular Data and Metadata on the Web",
          "url": "https://www.w3.org/TR/tabular-data-model/",
          "checked": "2026-10-11",
          "claims": [
            "Units of measure are not a built-in part of the tabular data model (section 4.5), so a number in a supplier file carries no unit unless it is stated separately."
          ]
        }
      ],
      "related": [
        "extract-pdf-price-lists-to-spreadsheet"
      ],
      "sections": [
        {
          "heading": "What you enter and what is computed",
          "body": [
            "Enter one supplier price line per row: a label or SKU, the listed price, what that price is for (unit, pack or case), how many units are in a pack, how many packs are in a case, and optionally a currency code. Paste straight from a spreadsheet if you like; columns can be separated by tabs or commas. The tool divides each price by the number of single units it covers, shows the working, and sorts the lines by price per single unit, cheapest first, within each currency.",
            "A price per unit is used as it is. A price per pack is divided by units per pack. A price per case is divided by units per pack times packs per case. You can also ask for the price of a number of units, for example 100 bottles."
          ],
          "items": [
            "Example, invented: 19.99 per pack of 12 is 1999/1200 per unit, shown as 1.665833 rounded to six places; 120.00 per case of 6 packs of 12 is 5/3, shown as 1.666667. At two decimal places both would look like 1.67, but the exact comparison shows the first is the cheaper.",
            "Columns: label, price, unit | pack | case, units per pack, packs per case, currency. The last three may be left empty when they do not apply.",
            "Prices use a decimal point and no thousands separators; a decimal comma is rejected with an explanation rather than guessed."
          ]
        },
        {
          "heading": "Exact arithmetic, and why",
          "body": [
            "Computers store most decimals such as 0.1 or 1.15 in binary floating point as a close approximation, not exactly. Python's documentation notes that numbers like 1.1 and 2.2 do not have exact representations in binary floating point, and that Fraction(1.1) is not Fraction(11, 10). Add rounding on top and two prices that are genuinely equal can compare as different, or a total built from rounded unit prices can disagree with the supplier's own amount by a few pence.",
            "So this tool keeps every price as a fraction of whole numbers and compares lines by cross-multiplying, which cannot be wrong by a rounding error. Rounding appears only when a value is shown, half up, to the currency's minor-unit digits plus four more places (more, when the value would otherwise show as zero), and the exact fraction is printed next to it. Minor-unit digits come from the ISO 4217 list: two for pounds, dollars and euros, none for yen, three for Kuwaiti dinar."
          ],
          "items": [
            "Prices with more decimals than the currency uses, such as 0.0125 per screw, are kept exactly and flagged, not rounded.",
            "Ties keep the order you entered them in.",
            "Lines in different currencies are ranked separately: no exchange rate is applied.",
            "A line that costs more than the cheapest is shown with its exact percentage difference to one decimal place; a difference under 0.1% is shown as 'less than 0.1%', never rounded up."
          ]
        },
        {
          "heading": "When the basis is unclear",
          "body": [
            "A line is held back from the ranking, with a question to ask the supplier, when its pack size is missing or zero, when a case price has no packs-per-case figure, or when the price is zero (more likely a missing price than a free item). A line whose price basis is not one of unit (or each, item), pack or case is refused with the reason, not guessed; a word such as box or carton has to be mapped to pack or case first. A line is still calculated but flagged when it says per unit yet also gives a pack size greater than 1, because the price may really be for the pack. This is the failure the linked guide describes: a number accepted without its purchase unit.",
            "Pack and case mean whatever the supplier says they mean. One supplier's case can hold a different number of packs from another's, and the same word can mean a carton, a box or a multipack. The tool cannot know the supplier's definition; it only divides the numbers you give it. Ask the supplier, or take the figures from their own price list."
          ],
          "items": [
            "Pack sizes are whole numbers; weighed, measured or variable-size goods need a different calculation.",
            "Tax, freight, discounts, price breaks and minimum order quantities are not modelled.",
            "The W3C tabular data model says units are not part of a table's numbers by default, which is why the unit has to be written down."
          ]
        },
        {
          "heading": "Privacy and scope",
          "body": [
            "Your price lines stay in this page. They are not sent anywhere, not stored and not added to the web address. Use invented or non-confidential figures if you prefer; the tool does not need supplier names."
          ],
          "items": []
        },
        {
          "heading": "If you want this done for you",
          "body": [
            "This tool compares numbers you type; it does not read your supplier files. If someone reformats a supplier or ERP stock-and-price export by hand before each import, the first related outcome is a tested script that turns one named export layout into the exact import file your store accepts, rejecting rows it cannot convert with a reason. It does not decide which supplier's pack definition is right or which line to buy. If your supplier's price lists arrive as text-based PDFs, the second related outcome turns one list into a checked spreadsheet with a page-by-page row-count reconciliation and a list of rows it could not read with confidence. There is no separate outcome yet for a full supplier-feed import review; the collection listed with this tool describes the import checks in general. Scope and price are agreed before work; describe your layout with invented prices, pack sizes and the expected result, not private price lists."
          ],
          "items": []
        }
      ],
      "incoming": [],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/tools/pack-unit-price-calculator/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/tools/pack-unit-price-calculator/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Ftools%2Fpack-unit-price-calculator%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Ftools%2Fpack-unit-price-calculator%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-api-webhook-signature-test-vector",
      "type": "example",
      "title": "Synthetic webhook test vector: why a re-formatted body makes a genuine event fail verification",
      "description": "Five versions of one made-up event, each shown in full with its real SHA-256 digest, show that re-formatting the body changes the hash and that a one-character change is rejected.",
      "url": "/examples/api-webhook-signature-test-vector/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "webhooks",
        "nextjs"
      ],
      "related": [
        "webhook-receiver-verified-signature-duplicate-safe",
        "guide-api-webhook-retries-idempotency"
      ],
      "provenance": [
        {
          "label": "GitHub: Validating webhook deliveries",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries",
          "checked": "2026-10-11",
          "claims": [
            "HMAC SHA-256 over the payload, hex-encoded; proxies must not alter the payload; compare in constant time; documented test secret, payload and signature, which this page also reproduces locally."
          ]
        },
        {
          "label": "Node.js crypto documentation",
          "url": "https://nodejs.org/api/crypto.html",
          "checked": "2026-10-11",
          "claims": [
            "crypto.createHmac(algorithm, key) with update and digest('hex') computes a keyed hash; the module documentation (v26) shows an HMAC SHA-256 example."
          ]
        }
      ],
      "sections": [
        {
          "heading": "A made-up event and a made-up secret",
          "body": [
            "This is a synthetic worked example, not a customer case and not a test of any provider. The secret below is invented for this page and protects nothing. The event is an invented order notification. The five bodies are printed exactly as they are hashed: each is the indented line under its label, with no space or newline added at the end. Verification means: take the exact bytes you received, compute the keyed hash with your secret, and compare the result with the signature the sender provided."
          ],
          "items": [
            "Secret (synthetic): synthetic-test-secret-not-real",
            "The sender's signature header carries the digest of body A, the bytes as sent.",
            "Bodies B to E are what your endpoint would hash if it altered the body first, or what an attacker might send. In D the six characters \\u00eb are written out in place of the letter ë."
          ],
          "code": "A (original bytes, as sent)\n  {\"event\":\"order.created\",\"id\":\"evt_0001\",\"total\":1250,\"customer\":\"Zoë\"}\nB (space after each colon and comma)\n  {\"event\": \"order.created\", \"id\": \"evt_0001\", \"total\": 1250, \"customer\": \"Zoë\"}\nC (keys in another order)\n  {\"id\":\"evt_0001\",\"event\":\"order.created\",\"total\":1250,\"customer\":\"Zoë\"}\nD (ë written as \\u00eb)\n  {\"event\":\"order.created\",\"id\":\"evt_0001\",\"total\":1250,\"customer\":\"Zo\\u00eb\"}\nE (total changed from 1250 to 1260)\n  {\"event\":\"order.created\",\"id\":\"evt_0001\",\"total\":1260,\"customer\":\"Zoë\"}"
        },
        {
          "heading": "The digest of each body",
          "body": [
            "Each digest is HMAC SHA-256 of exactly the body printed above, with the secret above, written in hex. The last column is the verdict when that body is checked against the sender's signature for A. The snippet at the end of the page recomputes every digest and verdict from the same five bodies."
          ],
          "items": [],
          "code": "case | SHA-256 digest of that body (hex)                                  | checked against the sender's signature for A\nA    | caa1a4bfbed4ed837f3ef71816b8765fb375ceba129c43346169639c6657c897 | accept\nB    | 195e20b7deccb58d318bfa4dbe79e9e018a73cfebc37ebd03f13eb97ec79bcff | reject\nC    | 1bd0e5ca28cc3aa81c94787f38966166515a668d192b274f3bc1473815d29071 | reject\nD    | e385a45c1b64a48e0c7843581edd970bcdc23116a66d1bdd1044c3eb65361ed3 | reject\nE    | 87d7d9c26bd69b501e334b69c9567d0de63bf700a83381364a9aa4f01189e07f | reject"
        },
        {
          "heading": "Reading the five cases",
          "body": [
            "Case A matches itself, so a correct endpoint accepts it. Case B has exactly the same fields and values, but the text was parsed and written out again with a space after each colon and comma, as some serialisers do by default, so the bytes differ and so does the digest: re-formatting like this is a common reason a genuine event is rejected. Case C keeps the same fields with the keys in another order, and case D writes the same letter in another JSON notation; both mean the same thing to a person and give different digests. Case E changes one figure and must be rejected. Only A matches."
          ],
          "items": [
            "B, C and D show why verification must use the untouched received text, before any parsing.",
            "E shows what verification is for: a changed body must fail.",
            "An endpoint that accepts B, C or D after normalising the body would also accept an attacker who knows how it normalises; the sender's bytes are the contract."
          ]
        },
        {
          "heading": "A published value to test your own function",
          "body": [
            "GitHub documents a test for its webhook scheme: the secret \"It's a Secret to Everybody\" and the payload \"Hello, World!\" produce the signature below. Running the same computation here gives the same value, which confirms the method used for the table. If your verification function returns this value for those inputs, the hashing is right and a live failure is about the bytes or the secret."
          ],
          "items": [
            "Expected value: 757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17"
          ]
        },
        {
          "heading": "Run it yourself",
          "body": [
            "Save the snippet as a file and run it with Node. It holds the same five bodies, takes the signature header as a separate value (as a real request supplies it), hashes the body it receives and compares the two in constant time. Expected output: A accept; B, C, D and E reject; a body made by adding one trailing space to A rejects; then GitHub's documented value above."
          ],
          "items": [
            "The verdict depends only on the received body and the supplied signature: change either and the answer changes."
          ],
          "code": "const { createHmac, timingSafeEqual } = require('node:crypto');\nconst secret = 'synthetic-test-secret-not-real';\nconst bodies = {\n  A: '{\"event\":\"order.created\",\"id\":\"evt_0001\",\"total\":1250,\"customer\":\"Zoë\"}',\n  B: '{\"event\": \"order.created\", \"id\": \"evt_0001\", \"total\": 1250, \"customer\": \"Zoë\"}',\n  C: '{\"id\":\"evt_0001\",\"event\":\"order.created\",\"total\":1250,\"customer\":\"Zoë\"}',\n  D: '{\"event\":\"order.created\",\"id\":\"evt_0001\",\"total\":1250,\"customer\":\"Zo\\\\u00eb\"}',\n  E: '{\"event\":\"order.created\",\"id\":\"evt_0001\",\"total\":1260,\"customer\":\"Zoë\"}',\n};\n// The signature header the sender sent with body A (taken from the table above).\nconst signatureHeader = 'sha256=caa1a4bfbed4ed837f3ef71816b8765fb375ceba129c43346169639c6657c897';\n// Verify: hash the body you received, then compare with the signature you were sent, in constant time.\nfunction verify(receivedBody, header) {\n  const expected = Buffer.from('sha256=' + createHmac('sha256', secret).update(receivedBody).digest('hex'));\n  const received = Buffer.from(header);\n  return received.length === expected.length && timingSafeEqual(received, expected);\n}\nfor (const [name, body] of Object.entries(bodies)) console.log(name, verify(body, signatureHeader) ? 'accept' : 'reject');\nconsole.log('A plus one trailing space', verify(bodies.A + ' ', signatureHeader) ? 'accept' : 'reject');\n// GitHub's documented test value for its own scheme:\nconsole.log(createHmac('sha256', \"It's a Secret to Everybody\").update('Hello, World!').digest('hex'));"
        },
        {
          "heading": "What this example does not show",
          "body": [
            "It does not test any provider's service, any framework's body handling or any real endpoint, and it does not mean any integration has been delivered. Providers differ in header names, encodings and whether a timestamp is signed as well; read your provider's documentation. The example also leaves out repeat deliveries, which are a separate problem from signature checks: see the retries guide and our webhook receiver job."
          ],
          "items": [
            "Real endpoints also need a timestamp or identifier policy where the provider offers one.",
            "The matching paid job is our webhook signature repair (posted test price from £295, untested). Send the provider, the event type and the status your endpoint returns, not your secret or real events."
          ]
        }
      ],
      "incoming": [
        "guide-api-webhook-retries-idempotency"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/api-webhook-signature-test-vector/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/api-webhook-signature-test-vector/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Fapi-webhook-signature-test-vector%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Fapi-webhook-signature-test-vector%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-api-webhook-retries-idempotency",
      "type": "guide",
      "title": "The same webhook arrives twice and runs twice? Acknowledge fast and make a repeat do nothing",
      "description": "Providers redeliver events that time out or fail. An endpoint that does the work before answering, with no memory of what it has seen, will run the action twice.",
      "url": "/guides/api-webhook-retries-idempotency/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "webhooks",
        "nextjs"
      ],
      "related": [
        "webhook-receiver-verified-signature-duplicate-safe",
        "example-api-webhook-signature-test-vector"
      ],
      "provenance": [
        {
          "label": "GitHub: Best practices for using webhooks",
          "url": "https://docs.github.com/en/webhooks/using-webhooks/best-practices-for-using-webhooks",
          "checked": "2026-10-11",
          "claims": [
            "Respond with a 2XX status within 10 seconds or GitHub treats the delivery as failed; a redelivered request keeps the original X-GitHub-Delivery header; the delivery header can be used to check that each delivery is unique; acknowledge immediately and process in the background; the page says nothing about delivery order."
          ]
        },
        {
          "label": "GitHub: Webhook events and payloads",
          "url": "https://docs.github.com/en/webhooks/webhook-events-and-payloads",
          "checked": "2026-10-11",
          "claims": [
            "X-GitHub-Delivery is a globally unique identifier for the event; X-Hub-Signature-256 is the HMAC hex digest of the request body, generated with SHA-256 and the webhook secret as the key."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Why one event can reach you more than once",
          "body": [
            "A webhook is a promise to tell you something happened, and the provider cannot know whether you received it unless you answer. GitHub's documentation says your server should return a 2XX status within 10 seconds; if it takes longer the connection is closed and the delivery is treated as failed. A failed delivery can then be delivered again, and GitHub notes that a redelivered request keeps the same X-GitHub-Delivery header as the original. Other providers have their own retry rules, so read yours: how many attempts, how long between them and how long they keep trying.",
            "An endpoint that does its real work (sending an email, creating a record, charging a card) before it returns, and is slow, invites exactly this: the provider gives up, redelivers, and the work runs again while the first run is still going."
          ],
          "items": [
            "Find the provider's documented retry and timeout rules.",
            "Look in its delivery log for the same event identifier appearing more than once.",
            "Compare your processing time with its timeout."
          ]
        },
        {
          "heading": "Acknowledge first, work afterwards",
          "body": [
            "GitHub recommends acknowledging a delivery immediately and processing the payload in the background, so a slow job does not hold up later deliveries. In practice this means: verify the signature, record the event, return success, and let a separate step perform the action. If recording the event itself fails, return an error so the provider tries again. Do not return success for an event you have not safely recorded."
          ],
          "items": [
            "Verify first, then store, then answer, then act.",
            "Keep the stored record small: the event identifier, the type and the raw body.",
            "A background step needs its own failure handling and its own record of what it has finished."
          ]
        },
        {
          "heading": "Make a repeat harmless without losing the event",
          "body": [
            "Even with fast answers, assume a repeat can arrive. The standard defence is an idempotency key: a stable identifier for the event, stored with a uniqueness rule so the same event cannot be claimed twice. GitHub suggests its X-GitHub-Delivery header, which stays the same on redelivery, as a way to check that each delivery is unique. Its signature, though, is a hash of the request body, so that header is not covered by it: someone holding one genuine signed request could resend the same body with a different header value. Our recommendation is to key on an identifier inside the signed body where the provider gives one, or on a hash of the exact received body, so the key cannot be changed without breaking the signature. The cost is that two different events with byte-for-byte identical bodies would look like one, so check that your provider's bodies carry a unique id or timestamp.",
            "Recording and acting must also be coordinated, or an event can be lost. If you insert the identifier first and act only if the insert succeeded, a crash between the two leaves a row but no effect: the provider's redelivery finds the row, is skipped, and the work never happens. The safer order is claim, then complete. Insert the identifier with the status received, perform the effect (in the same database transaction when the effect is a change to your own data), then mark it processed. A redelivery that finds processed is acknowledged and ignored. One that finds received for longer than the work should take is retried, because the first attempt probably died, taking over the claim with a single conditional update so that two retries cannot both do it; one that finds a recent received row is still in progress and should not start a second copy. Because the claim is a single insert with a uniqueness rule, two simultaneous copies cannot both claim it. For an effect outside your database, such as an email, a crash after the effect and before processed means a retry repeats it, so check whether that service accepts an idempotency key. Do not assume deliveries arrive in order; the cited documentation makes no promise about order, so an older event should not overwrite newer state."
          ],
          "items": [
            "One identifier, one claim, enforced by the database rather than by a code check alone.",
            "Status received, then processed: a processed event is acknowledged and ignored; a stale received one is retried.",
            "Return success for a repeat of a processed event, so the provider stops retrying.",
            "Where order matters, compare the event's own timestamp or version with what you hold."
          ]
        },
        {
          "heading": "A safe first investigation",
          "body": [
            "Start with the provider's delivery log, not your code. Look for repeated identifiers, the status each attempt received and how long your endpoint took. Then check your own records for two effects from one identifier. Use a test endpoint and the provider's sample or test delivery tool, and never replay real customer events to experiment. Replaying or repairing past events is a separate decision with its own risks."
          ],
          "items": [
            "Repeats with long response times: the endpoint is too slow to acknowledge.",
            "Repeats with error statuses: the endpoint is failing, perhaps at the signature check.",
            "Two effects with different identifiers: not a repeat, a different fault."
          ]
        },
        {
          "heading": "What fixes it and how the paid job is accepted",
          "body": [
            "The fix is to record each event under a stable key with a uniqueness rule, acknowledge quickly, and let a repeat of a finished event do nothing, inside your existing data model. It does not repair history or change provider accounts. That is a build job, not part of our webhook signature repair: our signed, repeat-safe webhook receiver (posted test price from £495, untested, paid only after sign-off) is accepted when the same delivery sent twice, including at the same moment, causes one action and a success response for the repeat. The signature repair (from £295, untested) is the narrower job for an endpoint that refuses genuine events. Send the provider, the event type and what ran twice in your first enquiry, not real event bodies or secrets."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-api-webhook-signature-test-vector"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/api-webhook-retries-idempotency/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/api-webhook-retries-idempotency/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fapi-webhook-retries-idempotency%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fapi-webhook-retries-idempotency%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-slack-signature-and-replay-window-cases",
      "type": "example",
      "title": "Synthetic test vectors for a Slack events endpoint: signature, five-minute window and repeated events",
      "description": "Five invented requests with their computed signatures show which a verifier should accept and which it should reject, and why an event ID must be claimed before the work is queued so a repeat or a simultaneous duplicate does not run it twice.",
      "url": "/examples/slack-signature-and-replay-window-cases/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "slack",
        "slack-api"
      ],
      "buyer_role": "Developer building or reviewing a Slack Events API endpoint",
      "purchase_trigger": "Nobody has tested a forged, altered, stale or repeated request against the verifier",
      "problem": "The verifier is only tried with genuine requests, so its rejections and its replay handling are untested.",
      "related": [
        "guide-slack-events-signature-timestamp-and-three-second-ack",
        "webhook-receiver-verified-signature-duplicate-safe",
        "example-webhook-delivery-ledger-repeat-safe"
      ],
      "provenance": [
        {
          "label": "Slack: verifying requests from Slack",
          "url": "https://docs.slack.dev/authentication/verifying-requests-from-slack",
          "checked": "2026-10-11",
          "claims": [
            "The base string is v0, the timestamp and the raw body joined by colons; the signature is v0= followed by the HMAC-SHA256 hex digest with the signing secret.",
            "Requests whose timestamp is more than five minutes from local time should be dropped as possible replays."
          ]
        },
        {
          "label": "Slack: Events API",
          "url": "https://docs.slack.dev/apis/events-api/",
          "checked": "2026-10-11",
          "claims": [
            "event_id is globally unique; failed deliveries are retried up to three times, the first retry nearly immediately, with x-slack-retry-num and x-slack-retry-reason headers."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Fixtures and how the numbers were made",
          "body": [
            "Everything here is invented. The signing key demo-signing-key-0001 is a made-up string for this example and protects nothing. The server's clock reads 1760000300 seconds. The body is a short invented event with the event ID EvSYNTH0001. Each signature below was computed with a standard HMAC-SHA256 routine over the string v0, a colon, the timestamp, a colon and the body, then prefixed with v0=. These are test vectors for your own implementation, computed independently of any Slack code; no Slack request was made."
          ],
          "items": [
            "Body: {\"type\":\"event_callback\",\"event_id\":\"EvSYNTH0001\",\"team_id\":\"TSYNTH01\",\"event\":{\"type\":\"message\",\"text\":\"hello\"}}",
            "Case D alters the last letter of the text from o to O after signing.",
            "Case E signs with a timestamp 5000 seconds earlier than the one sent."
          ],
          "code": "server time = 1760000300\n\nA  genuine         ts=1760000290 sig=v0=cab8cd78dd3da47be7f322e682c03e44f87230792feb436f981596885f24ff6d\nB  genuine, 300 s  ts=1760000000 sig=v0=e92c0b417e36fff1efd69fc3101e024c8b24b95da8a3643631296fe63e1f5774\nC  genuine, 301 s  ts=1759999999 sig=v0=ba3b60daa1291f0a3d57795d619735fe9d634a8bc1fe84c0762f15b911c34f97\nD  body altered    ts=1760000290 sig=v0=cab8cd78dd3da47be7f322e682c03e44f87230792feb436f981596885f24ff6d\nE  ts mismatch     ts=1760000290 sig=v0=ba8c9c542133664c2086443cde0d994fb95075fb5ddd148db3af6ec3a844c691"
        },
        {
          "heading": "Expected decisions",
          "body": [
            "A is accepted: the signature matches and the timestamp is ten seconds old. B is accepted under this example's reading that exactly 300 seconds is not more than five minutes; the documentation says to drop requests more than five minutes away, so the boundary is a choice to state in your own tests. C carries a genuine signature but is 301 seconds old, so it is rejected for age. D uses A's signature with a changed body and must fail the signature. E has a signature computed over a different timestamp and must fail."
          ],
          "items": [
            "A: accept, start the work once.",
            "B: accept at the boundary under the stated reading.",
            "C: reject, stale.",
            "D: reject, signature mismatch.",
            "E: reject, signature mismatch."
          ]
        },
        {
          "heading": "Repeated and simultaneous events are cases six to eight",
          "body": [
            "Deliver case A a second time, still inside the window, as an attacker's replay would; a Slack retry of the same event is delivered again too. The signature and timestamp are both valid, so the verifier alone cannot reject it. The endpoint must recognise EvSYNTH0001 as already claimed, reply with success and not start the work again. The time window limits how long a captured request is useful; the event ID is what prevents the work from running twice.",
            "The order of the steps matters. Slack sends its first retry nearly immediately after a missed three-second reply, so a retry can arrive while the first job is still running. If the event ID is recorded only when the work finishes, that retry looks new and a second job starts. The safe order is: verify the signature and timestamp, claim the event ID with one atomic insert (a unique key, state received), queue the job from that record, reply 2xx, and let the job mark the row done. Case six is the sequential repeat. Case seven is two copies of case A arriving at the same moment: exactly one insert succeeds, one job is queued, and both requests get a success reply. Case eight is a worker stopped after the claim: the row stays received, and a sweep re-queues it after a timeout longer than the job's longest run."
          ],
          "items": [
            "Claim the event ID atomically before queueing the work, and only after verification passes, so a forged request cannot use up a genuine ID.",
            "Mark it done when the job finishes; re-queue a claim still received after the agreed timeout.",
            "A worker that dies after doing the work but before marking it done will be re-run, so the action should be safe to repeat; the claim alone is not exactly once.",
            "A second request with the same ID but a different body is worth logging as an anomaly.",
            "A reply is still required within Slack's time limit."
          ],
          "code": "Illustrative timeline (figures invented, a job that takes 5 seconds)\n\nRecorded when the work finishes (race):\n t=0.0 s  delivery 1 arrives, EvSYNTH0001 not recorded, job 1 starts\n t=3.0 s  no reply yet, Slack counts the delivery as failed and retries\n t=3.0 s  delivery 2 arrives, EvSYNTH0001 still not recorded, job 2 starts\n t=5.0 s  job 1 finishes and records the ID; job 2 also runs\n -> the work ran twice\n\nClaimed before queueing (unique key, state received):\n t=0.0 s  delivery 1 arrives, insert succeeds, job queued, 200 sent\n t=3.0 s  delivery 2 arrives, insert fails on the unique key, 200 sent, nothing queued\n t=5.0 s  job 1 finishes, row set to done\n -> the work ran once"
        },
        {
          "heading": "What was and was not exercised",
          "body": [
            "Only the arithmetic was exercised: the five signatures were recomputed with a standard HMAC-SHA256 routine and match, and the decisions follow from the stated rules. The timeline in the repeated-events section is an invented illustration of ordering, not a recorded run. No endpoint, framework, queue, database or Slack workspace was run, and nothing here shows that any particular handler behaves correctly. Use the vectors and the three repeat cases to test your own. The single events receiver outcome is priced at a fixed £445 as an untested test price, with payment after the agreed checks pass and you sign off; never send a real signing secret in an enquiry."
          ],
          "items": []
        }
      ],
      "incoming": [
        "guide-slack-events-signature-timestamp-and-three-second-ack"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/slack-signature-and-replay-window-cases/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/slack-signature-and-replay-window-cases/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Fslack-signature-and-replay-window-cases%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Fslack-signature-and-replay-window-cases%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-slack-events-signature-timestamp-and-three-second-ack",
      "type": "guide",
      "title": "Slack events hit your endpoint twice, or not at all: signature, timestamp and the three-second rule",
      "description": "How Slack signs events, why the raw body and a five-minute window matter, and how to reply in time without running the work twice.",
      "url": "/guides/slack-events-signature-timestamp-and-three-second-ack/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "slack",
        "slack-api"
      ],
      "buyer_role": "Developer receiving Slack Events API deliveries in a web app",
      "purchase_trigger": "Slack shows delivery failures, or the same action runs two or three times",
      "problem": "The handler does slow work before replying, verifies after parsing, and either has no memory of events it has already seen or writes that memory only when the work ends, so a retry that arrives while the first job is still running starts a second one.",
      "related": [
        "example-slack-signature-and-replay-window-cases",
        "webhook-receiver-verified-signature-duplicate-safe",
        "guide-webhook-signature-raw-body",
        "example-webhook-delivery-ledger-repeat-safe"
      ],
      "provenance": [
        {
          "label": "Slack: verifying requests",
          "url": "https://docs.slack.dev/authentication/verifying-requests-from-slack",
          "checked": "2026-10-11",
          "claims": [
            "The signature is HMAC-SHA256 of version, timestamp and raw body with the signing secret, prefixed v0=.",
            "Requests more than five minutes from the local clock should be dropped as possible replays.",
            "The raw body must be read before parsing."
          ]
        },
        {
          "label": "Slack: Events API",
          "url": "https://docs.slack.dev/apis/events-api/",
          "checked": "2026-10-11",
          "claims": [
            "The app must return a 2xx within three seconds.",
            "Slack retries up to three times (the first nearly immediately, then after one minute and after five minutes) with x-slack-retry-num and x-slack-retry-reason headers.",
            "event_id is globally unique across workspaces.",
            "Slack follows at most two redirects and checks the endpoint's certificate; event subscriptions can be temporarily disabled when more than 95 per cent of delivery attempts fail within 60 minutes, and apps receiving fewer than 1,000 events an hour are not automatically disabled."
          ]
        },
        {
          "label": "Slack: HTTP request URL verification",
          "url": "https://docs.slack.dev/apis/events-api/using-http-request-urls",
          "checked": "2026-10-11",
          "claims": [
            "Slack sends a url_verification request with a challenge that must be echoed in a 200 response."
          ]
        }
      ],
      "sections": [
        {
          "heading": "Setup handshake and what comes after",
          "body": [
            "When you enter the request URL in the app's event settings, Slack posts a url_verification request containing a random challenge. Your endpoint must answer 200 with that challenge value, as plain text, form-encoded or JSON. After that, real events arrive as event_callback payloads. A failing handshake is a common first symptom and usually means the route is unreachable, redirects, or the framework rejected an unexpected content type."
          ],
          "items": [
            "The setup request needs no scope or event subscription, so a failure there is about reachability, not permissions.",
            "Slack also checks the endpoint's certificate.",
            "Follow redirects sparingly: Slack follows at most two."
          ]
        },
        {
          "heading": "Verify the signature on the raw bytes",
          "body": [
            "Each request has an X-Slack-Signature and an X-Slack-Request-Timestamp header. Build the base string by joining the version label v0, the timestamp and the raw request body with colons, compute an HMAC with SHA-256 using your signing secret as the key, prefix the hex digest with v0= and compare it with the header using a function meant for comparing signatures. The body must be the exact bytes received; a framework that parses and re-serialises it first will make genuine requests fail."
          ],
          "items": [
            "Treat the signing secret as plain UTF-8 text from your app's settings.",
            "Slack's own SDKs can do this for you when given the secret.",
            "Regenerate the secret in the app settings if it is exposed."
          ]
        },
        {
          "heading": "The timestamp is the replay protection",
          "body": [
            "The signature binds the timestamp to the body, so an attacker who captured a genuine request cannot change either without breaking it. They can, however, resend it unchanged. Slack's documentation says to drop a request whose timestamp is more than five minutes from your clock. That limits how long a captured request stays useful, and it makes your server's clock part of your security, so check that it is synchronised."
          ],
          "items": [
            "Reject too-old and too-new timestamps.",
            "A request inside the window can still be repeated; claim the event ID before queueing the work (next section), and only after the signature and timestamp checks pass.",
            "Test both edges of the window."
          ]
        },
        {
          "heading": "Reply first, work second, once",
          "body": [
            "Slack requires a 2xx within three seconds, or the delivery counts as failed and is retried up to three times, the first retry nearly immediately. If your handler calls a slow service before replying, Slack retries and the work runs again. Reply immediately and queue the work. Record the event_id so a repeat does nothing, but record it first, not last: if the record is written only when the work finishes, the near-immediate retry arrives while the first job is still running, passes the \"not processed yet\" check, and a second job starts. The safe order is to verify the signature and timestamp, then claim the event_id with one atomic insert (a unique key on event_id with a state of received), then queue the job from that record, then reply 2xx. A delivery whose insert finds an existing claim replies 2xx and queues nothing. The job marks the row done when it finishes. Slack's retries carry retry-number and retry-reason headers, which help diagnose timeouts but should not be your only duplicate defence."
          ],
          "items": [
            "event_id is unique across workspaces, so it is a sound key.",
            "Claim only after verification passes, so a forged request cannot use up a genuine event ID.",
            "If the claim cannot be written, reply with a server error so Slack retries; a success you cannot honour loses the event.",
            "Crash after the claim: queue the job from the claim record (or in the same transaction), have workers hold a time-limited lease, and run a sweep that re-queues or flags claims still received after a timeout longer than the job's longest run.",
            "A worker that dies after doing the work but before marking it done will be re-run by the sweep, so make the action safe to repeat or protect it at the service it calls; the claim alone cannot promise exactly once.",
            "Heavy failure rates can lead Slack to pause subscriptions for busier apps.",
            "Buttons and slash commands have their own acknowledgement rules."
          ]
        },
        {
          "heading": "How the paid outcome is accepted",
          "body": [
            "The Slack events receiver outcome is accepted when the handshake succeeds, forged, altered and stale requests start no work and claim no event ID, a ten-second downstream delay does not delay the reply, a repeated event ID is not processed again, two identical deliveries at the same moment queue one job, a worker stopped after the claim is recovered by the sweep so the action happens once, and an unavailable store gets an error reply rather than a success. If your endpoint is for a sender other than Slack, the generic signed-webhook receiver job is the fit; if it only rejects genuine events as unsigned, the endpoint repair job is the smaller scope. The fixed £445 price is untested and payment follows your sign-off. You hold the signing secret."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-slack-signature-and-replay-window-cases"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/slack-events-signature-timestamp-and-three-second-ack/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/slack-events-signature-timestamp-and-three-second-ack/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fslack-events-signature-timestamp-and-three-second-ack%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fslack-events-signature-timestamp-and-three-second-ack%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-integrate-pipedrive-person-lead-handoff-and-limits",
      "type": "guide",
      "title": "Website form to Pipedrive: store first, match by email, create a linked lead, respect the limits",
      "description": "The order of calls, the API versions, the matching rule, the rate limits and the outage design for pushing a form enquiry into Pipedrive without losing it, and the lookup that keeps a lost reply from creating a second lead.",
      "url": "/guides/integrate-pipedrive-person-lead-handoff-and-limits/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "buyer_role": "Founder or sales lead connecting a website form to Pipedrive",
      "purchase_trigger": "Enquiries are retyped by hand, duplicates appear, or enquiries went missing during an outage",
      "problem": "The form calls the CRM inside the visitor's request and has no record, no matching rule, no lookup after an unknown result and no plan for limits or failure.",
      "related": [
        "crm-duplicate-contacts-from-web-forms",
        "crm-web-lead-pipeline-repair",
        "zapier-facebook-leads-to-salesforce"
      ],
      "provenance": [
        {
          "label": "Pipedrive: persons API",
          "url": "https://developers.pipedrive.com/docs/api/v1/Persons",
          "checked": "2026-10-11",
          "claims": [
            "The page documents adding a person as POST /api/v2/persons, with only a name required and emails as an array of value, primary and label.",
            "Person search is GET /api/v2/persons/search; it takes a term, optional fields and an exact-match flag.",
            "The page shows no duplicate handling for adding a person."
          ]
        },
        {
          "label": "Pipedrive: leads API",
          "url": "https://developers.pipedrive.com/docs/api/v1/Leads",
          "checked": "2026-10-11",
          "claims": [
            "Adding a lead is POST /api/v1/leads; a lead must be linked to a person or an organisation, creating one returns 201 and API-created leads carry the source API.",
            "The page shows no idempotency key or duplicate protection for adding a lead.",
            "Lead search is GET /api/v2/leads/search over title, notes and custom fields, with an exact-match option; leads inherit the custom fields of deals."
          ]
        },
        {
          "label": "Pipedrive changelog: deprecated API v1 endpoints are out of support",
          "url": "https://developers.pipedrive.com/changelog/post/deprecated-apiv1-endpoints-become-out-of-support",
          "checked": "2026-10-11",
          "claims": [
            "Announced 29 July 2026: the listed deprecated v1 endpoints are out of support from 1 August 2026, meaning no maintenance, bug fixes or availability guarantees, and they may be changed or removed without notice.",
            "The list includes POST /v1/persons and GET /v1/persons/search, with v2 replacements; it lists no leads endpoints."
          ]
        },
        {
          "label": "Pipedrive: API rate limiting",
          "url": "https://pipedrive.readme.io/docs/core-api-concepts-rate-limiting",
          "checked": "2026-10-11",
          "claims": [
            "There is a shared daily token budget per account, burst limits per token over a rolling two seconds, and a stricter cap for search; 429 is returned when the daily budget is exhausted.",
            "Continued heavy api_token traffic after 429s can lead to a 403 block.",
            "Searching costs more of the budget than fetching one item, and v2 endpoints cost fewer tokens than v1.",
            "Webhooks are listed among the ways to reduce pressure on the limits."
          ]
        }
      ],
      "sections": [
        {
          "heading": "The sequence",
          "body": [
            "A reliable form handler does its work in a fixed order. It validates the submission and saves it in your own database with a reference of its own and a pending status. It then claims the submission so only one worker sends it. It looks for an existing person by email; if none exists it creates one; and finally it creates a lead linked to that person, with the submission's reference stored on the lead. Pipedrive's documentation says a lead must be linked to a person or an organisation, and that a person needs only a name, with emails given as a list of value, primary flag and label. Only after the lead is created does the saved submission become delivered."
          ],
          "items": [
            "Saving first means an outage cannot lose the enquiry.",
            "The visitor's page should not wait for Pipedrive.",
            "Put the submission's reference on the lead, in a custom lead field an administrator creates (leads inherit the custom fields of deals) or, failing that, in the lead title, so the lead can be found again.",
            "Pipedrive's lead documentation shows no idempotency key or duplicate protection, so the reference is for looking up a lead, not for making a repeat safe.",
            "Lead search covers title, notes and custom fields, but Pipedrive's page does not say which custom field types, so prove the lookup against your real test account before relying on it."
          ]
        },
        {
          "heading": "Which API version to build against",
          "body": [
            "Pipedrive's documentation now shows the person calls as API v2: adding a person is POST /api/v2/persons and searching persons is GET /api/v2/persons/search. Its changelog, announced on 29 July 2026, says the deprecated v1 endpoints, including POST /v1/persons and GET /v1/persons/search, are out of support from 1 August 2026: they may still respond, but Pipedrive gives no maintenance, bug fixes or availability guarantees and may change or remove them without notice. Adding a lead is still POST /api/v1/leads, with no v2 equivalent shown on the leads page when it was checked on 11 October 2026, and the changelog list contains no leads endpoints; lead search is GET /api/v2/leads/search. So a new build uses v2 for people and v1 for creating a lead, and re-reads the leads page before relying on that. The rate-limit page also says v2 endpoints cost fewer tokens than v1."
          ],
          "items": [
            "Write down each endpoint and version the client uses, in one module, so a change is one edit.",
            "Check Pipedrive's changelog when you next touch the integration; a v1 lead endpoint could be announced later.",
            "Do not copy older tutorials that use the v1 persons paths."
          ]
        },
        {
          "heading": "Matching by email, and its limits",
          "body": [
            "Person search accepts a term, a list of fields to search and an exact-match option, so a search on the email field with exact match finds people with that address. It cannot make duplicates impossible: two people already sharing an address may both match, and two simultaneous submissions can both find nothing. Handle the second case by delivering one submission at a time for any one email address, and by keeping the Pipedrive person id against the email once you have it. When more than one person matches, flag the submission for a human decision instead of picking one. Cleaning existing duplicates is a separate data task."
          ],
          "items": [
            "Decide in advance what a repeat enquiry does: a new lead on the same person, a note, or a review.",
            "Search calls use more of the rate budget than a plain fetch.",
            "Normalise the email's case and whitespace before searching."
          ]
        },
        {
          "heading": "Know the limits",
          "body": [
            "Pipedrive gives each account a daily token budget shared by all users and integrations, resetting at midnight in the server's time zone, and applies burst limits per token over a rolling two seconds. Search has its own flat cap and costs more of the budget than fetching one item. When the budget is exhausted, requests are rejected with 429 until the reset, and the documentation warns that continuing heavy token traffic after 429s can lead to a block with a 403 page. Response headers report the remaining capacity, and the rate-limit page lists using webhooks among the ways to reduce pressure on the limits."
          ],
          "items": [
            "A small form rarely threatens the budget, but other integrations share it.",
            "On 429, stop for the stated time; do not loop.",
            "Track the headers if volume grows."
          ]
        },
        {
          "heading": "Design for the outage",
          "body": [
            "The saved submission has these states: pending, sending, delivered and needs attention. A delivery job claims a pending item, so two workers cannot send it, and waits as asked on 429. A 429 means Pipedrive rejected the call before acting, so retrying after the wait is safe. A timeout, a dropped connection or a server error is different: the lead may have been created and the reply lost. Pipedrive documents no idempotency for creating a lead, so a blind retry can create a second lead. Before any retry after such an unknown outcome, search for the lead by the submission's reference. If it is found, mark the item delivered; if it is not found, retry the create; if the search itself cannot be made, mark the item as needing attention for a person and do not retry. A rejected credential should not be retried either; it should alert the person who holds the Pipedrive administrator rights. After the fix, one run resolves the item. Keep the credential in server configuration and never in the page."
          ],
          "items": [
            "Show the owner a count of items that need attention.",
            "Never show the visitor an error for a CRM fault.",
            "Test with a stand-in that returns 429 and 503, that creates a lead and then drops the reply, that refuses the lookup, and that rejects the credential.",
            "Keep a reconciliation check that lists delivered submissions with no lead, or with more than one lead, because this design lowers the chance of a duplicate and cannot remove it."
          ]
        },
        {
          "heading": "How the paid outcome is accepted",
          "body": [
            "The Pipedrive outcome is accepted when a test submission creates one person and one linked lead, a repeat reuses the person and adds one lead under your rule, a 429 is delivered after the wait, a lead created with a lost reply is found by its reference and not created twice, an unavailable lookup flags the item instead of retrying, the reconciliation check lists seeded problems, and no credential appears in page source or logs. It does not promise that a duplicate can never occur. The fixed £345 test price is untested and payment follows your sign-off. Other CRMs, duplicate cleaning and consent wording are outside it. For HubSpot, the job that stops form duplicates in HubSpot and the one that repairs the form-to-salesperson path are the neighbours."
          ],
          "items": []
        }
      ],
      "incoming": [],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/integrate-pipedrive-person-lead-handoff-and-limits/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/integrate-pipedrive-person-lead-handoff-and-limits/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fintegrate-pipedrive-person-lead-handoff-and-limits%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fintegrate-pipedrive-person-lead-handoff-and-limits%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-invoice-tax-rounding-reconciliation",
      "type": "example",
      "title": "Synthetic invoice reconciliation: identical net lines, two different tax totals",
      "description": "Three invented small-value lines show how line-level and invoice-level tax rounding can disagree even when net values and line counts match.",
      "url": "/examples/invoice-tax-rounding-reconciliation/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "accounting-sync",
        "google-sheets",
        "xero"
      ],
      "buyer_role": "Finance owner approving source-to-destination invoice reconciliation",
      "purchase_trigger": "Every line arrives but the destination's tax and gross totals disagree with the source",
      "problem": "The integration compares grand totals without reconciling the two systems' declared tax rounding boundaries.",
      "related": [
        "guide-invoice-tax-amount-basis",
        "guide-sheets-xero-line-item-shape",
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "HMRC VAT Notice 700, invoice and retailer rounding distinctions",
          "url": "https://www.gov.uk/guidance/vat-guide-notice-700",
          "checked": "2026-10-11",
          "claims": [
            "Sections 17.5 and 17.6 distinguish invoice-trader and retailer treatments. Neither hypothetical policy below is declared applicable to the reader."
          ]
        },
        {
          "label": "Existing mapping scope excludes tax configuration",
          "url": "https://syntheticindustry.ai/services/zapier-sheets-to-xero-missing-columns/",
          "checked": "2026-10-11"
        }
      ],
      "sections": [
        {
          "heading": "Invented inputs, not an invoice or tax recommendation",
          "body": [
            "Use three distinct synthetic lines A, B and C, each quantity one and net amount 0.03 in an invented GBP calculation. Assume 20% solely for arithmetic and nonnegative half-up rounding to two decimals. Compare two hypothetical policies: round tax per line before summing, or sum unrounded tax and then round once. This is a worked specification, not a tested accounting connector, customer result or determination of lawful tax treatment."
          ],
          "items": [],
          "code": "line | net  | unrounded tax | half-up line tax\nA    | 0.03 | 0.006         | 0.01\nB    | 0.03 | 0.006         | 0.01\nC    | 0.03 | 0.006         | 0.01\n\nnet total                           = 0.09\npolicy L: sum rounded line tax       = 0.03; gross = 0.12\npolicy I: round total tax 0.018 once = 0.02; gross = 0.11\ndifference                          = 0.01"
        },
        {
          "heading": "Reconcile components before accepting the total",
          "body": [
            "Both hypothetical outputs retain three lines and 0.09 net. They disagree on tax and gross because their declared boundaries differ. Label each comparison with the policy, line identity and net/tax/gross components. A one-cent tolerance can hide a real policy mismatch; agreeing a tolerance does not choose the lawful policy or justify changing stored values."
          ],
          "items": [
            "The finance owner or adviser confirms the applicable policy and rates before implementation.",
            "A source formula and destination calculation must be compared under that same approved policy.",
            "Do not infer line tax from a rounded grand total or add a balancing line without agreement."
          ]
        },
        {
          "heading": "What was actually exercised",
          "body": [
            "The draft's mechanical evidence independently recomputes this arithmetic with integer rational values. That verifies the numbers only. No Sheet formula, tax engine, API, database, connector or posted invoice was exercised, and neither rounding policy is self-approved as accounting advice. HMRC guidance is UK-specific and distinguishes businesses and calculation methods; do not import this hypothetical half-up choice into a live setup."
          ],
          "items": [
            "Negative adjustments, discounts, mixed rates, exchange rates and legal invoice requirements are outside this example."
          ]
        },
        {
          "heading": "Use the existing offer only for its eligible mapping job",
          "body": [
            "If an approved net or tax field is simply missing, the existing £145 Sheets-to-Xero mapping service may fit its working trigger, five-field ceiling and authorised disposable draft. Resolving tax-policy disagreement, configuring tax, adding a calculation engine or correcting past invoices is not included. Free official guidance, your existing integrator and finance adviser are alternatives. Send invented component expectations and the missing field, never actual invoices, tax records or credentials. The price is untested; payment follows agreed checks and sign-off."
          ],
          "items": []
        }
      ],
      "incoming": [
        "tool-invoice-rounding-bench",
        "collection-finance-invoice-integrity-checks",
        "guide-invoice-tax-amount-basis"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/invoice-tax-rounding-reconciliation/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/invoice-tax-rounding-reconciliation/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Finvoice-tax-rounding-reconciliation%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Finvoice-tax-rounding-reconciliation%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "example-invoice-retry-identity-matrix",
      "type": "example",
      "title": "Synthetic invoice retry matrix: source operation, attempt and target draft are different IDs",
      "description": "Concrete made-up references specify when a retry retains one target draft and when changed contents, a second organisation or multiple matches need a separate decision.",
      "url": "/examples/invoice-retry-identity-matrix/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "accounting-sync",
        "zapier",
        "xero"
      ],
      "buyer_role": "Finance owner reviewing an integration's duplicate-prevention acceptance cases",
      "purchase_trigger": "Replaying one source row creates another unsent invoice or matches the wrong draft",
      "problem": "Transport-attempt identity is mistaken for immutable business-operation identity or the target lookup ignores organisation and content.",
      "related": [
        "guide-invoice-create-unknown-outcome",
        "buyer-finance-manager-invoice-integration",
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "IETF RFC 9110 section 9.2.2",
          "url": "https://www.rfc-editor.org/rfc/rfc9110.html",
          "checked": "2026-10-11",
          "claims": [
            "Safe automatic repetition requires known request semantics or evidence the original request was not applied; vendor-specific duplicate protection is not established."
          ]
        },
        {
          "label": "Existing mapping offer's disposable repeated-input check",
          "url": "https://syntheticindustry.ai/services/zapier-sheets-to-xero-missing-columns/",
          "checked": "2026-10-11"
        }
      ],
      "sections": [
        {
          "heading": "An authored identity policy",
          "body": [
            "All labels are invented. Organisation O-A and source operation S-17 identify one immutable intended invoice creation. Attempt A-1 times out after target draft D-81 may have been created. A-2 is another transport attempt, not another invoice. Content token P means equality of the agreed business fields, not an actual checksum or vendor idempotency key. The operator must separately verify supported vendor mechanisms and safe concurrency."
          ],
          "items": [],
          "code": "organisation / operation | observed target state      | expected authored disposition\nO-A / S-17, content P     | one D-81, content P        | retain D-81; no new create\nO-A / S-17, content P     | D-81 and D-82, both P      | hold duplicate; no deletion\nO-A / S-17, content Q     | one D-81, content P        | hold content conflict\nO-A / S-17, content P     | lookup unavailable         | hold unknown\nO-A / S-17, content P     | no visible match           | unresolved until retry safety established\nO-B / S-17, content P     | D-81 exists only in O-A     | never reuse O-A's draft"
        },
        {
          "heading": "A changed source is not silently a retry",
          "body": [
            "Under this example's immutable-operation rule, editing S-17 from P to Q needs a separately authorised correction decision; generating a fresh attempt ID must not bypass that rule. Two source operations with equal amounts are not necessarily duplicates. A row number that changes after sorting is not a proven stable operation identity. A real integration needs a durable documented identity contract, not these made-up strings alone."
          ],
          "items": [
            "Test changed contents and cross-organisation matching separately from repeating an identical input.",
            "No draft may be deleted, sent or posted merely to make the matrix pass."
          ]
        },
        {
          "heading": "Specification, not a tested connector",
          "body": [
            "The matrix is an authored acceptance specification. No provider lookup, idempotency key, concurrency mechanism or recovery process has been executed. The HTTP source supports the caution about repeating uncertain writes; it does not validate this policy or establish a particular vendor's response. A real test would retain target identities, counts and dispositions on an authorised isolated route."
          ],
          "items": [
            "An eventual zero-match lookup is not automatically authoritative evidence that no write occurred.",
            "No invented event here is a customer enquiry, invoice or payment."
          ]
        },
        {
          "heading": "A narrow contextual priced route",
          "body": [
            "Use this matrix to ask the existing operator how their duplicate rule works. The £145 Sheets-to-Xero offer is still a five-field repair with one disposable row and agreed repeated-input checks, not a reconciliation or idempotency subsystem. Request it only if that mapping scope fits; recovery, historical cleanup and new architecture require separate agreement. Send invented identities and expected dispositions, not customer references, invoices or keys. The price is untested and payment follows the agreed passing checks and sign-off."
          ],
          "items": []
        }
      ],
      "incoming": [
        "collection-finance-invoice-integrity-checks",
        "guide-invoice-create-unknown-outcome"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/examples/invoice-retry-identity-matrix/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/examples/invoice-retry-identity-matrix/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fexamples%2Finvoice-retry-identity-matrix%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fexamples%2Finvoice-retry-identity-matrix%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "collection-finance-invoice-integrity-checks",
      "type": "category",
      "title": "Invoice integration review: verify shape, representation, tax basis and repeat safety",
      "description": "An ordered review worksheet helps a finance owner stop at the first broken contract before requesting a bounded existing mapping repair.",
      "url": "/collections/finance-invoice-integrity-checks/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "accounting-sync",
        "google-sheets",
        "zapier",
        "xero"
      ],
      "buyer_role": "Finance owner preparing a low-trust source-to-draft acceptance request",
      "purchase_trigger": "An automation runs but its invoice cannot be accepted without manual reconstruction",
      "problem": "One successful workflow or matching grand total substitutes for separate checks of invoice identity, fields and side effects.",
      "related": [
        "guide-sheets-xero-line-item-shape",
        "guide-sheets-invoice-value-rendering",
        "guide-invoice-tax-amount-basis",
        "example-invoice-tax-rounding-reconciliation",
        "guide-invoice-create-unknown-outcome",
        "example-invoice-retry-identity-matrix",
        "buyer-finance-manager-invoice-integration",
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "Google Sheets value representations",
          "url": "https://developers.google.com/sheets/api/reference/rest/v4/ValueRenderOption",
          "checked": "2026-10-11",
          "claims": [
            "Formatted and unformatted outputs can represent the same cell differently."
          ]
        },
        {
          "label": "IETF RFC 9110 retry boundaries",
          "url": "https://www.rfc-editor.org/rfc/rfc9110.html",
          "checked": "2026-10-11",
          "claims": [
            "A lost response does not itself justify automatically repeating a non-idempotent request."
          ]
        },
        {
          "label": "Existing bounded mapping price and exclusions",
          "url": "https://syntheticindustry.ai/services/zapier-sheets-to-xero-missing-columns/",
          "checked": "2026-10-11"
        }
      ],
      "sections": [
        {
          "heading": "Use the worksheet with invented data",
          "body": [
            "This sequence is authored review advice, not a vendor integration recipe or evidence of demand. Name one intended organisation and unsent synthetic draft, with the expected invoice and line counts. Stop at the first disagreement; fixing a later total cannot correct a wrongly grouped or assigned invoice."
          ],
          "items": [
            "1. Shape: compare invoice count and each line's description, quantity and own amount; reuse the existing line-shape guide.",
            "2. Representation: compare the connector's captured date and amount types with destination requirements.",
            "3. Basis: finance approves currency, net/gross meaning and tax treatment before any mapping change.",
            "4. Components: reconcile net, tax and gross under one approved rounding policy.",
            "5. Identity: distinguish source operation, attempt and target draft; specify unknown and repeated-input outcomes."
          ]
        },
        {
          "heading": "Keep a result for each boundary",
          "body": [
            "For each worksheet row retain an invented input, expected result, observed result when actually tested, and the responsible approver. Until a test is executed, observed stays unverified. A missing mandatory field must produce the agreed exception; a timeout must not silently become another creation. The worked examples supply specifications, not passing integration evidence."
          ],
          "items": [
            "Do not require private invoices or customer data to prepare first-contact cases.",
            "Inspect an authorised disposable draft; a test button may write connected records.",
            "The organisation holder controls live posting and sending."
          ]
        },
        {
          "heading": "Choose a correction rather than a new accounting package",
          "body": [
            "If the issue is a stale mapping within an already-working five-field route, the existing £145 Sheets-to-Xero offer is relevant after fit confirmation. Variable grouping, a failed trigger, tax setup, historical corrections, new retry architecture and live sending are not included. Ask the existing maintainer or vendor support first when their documented configuration resolves the problem. Ask a qualified finance adviser for undecided tax policy."
          ],
          "items": [
            "This collection is not a new paid bundle and does not make the six linked checks six purchasable repairs.",
            "QuickBooks users may reuse the business questions, but the Xero offer does not become a QuickBooks package."
          ]
        },
        {
          "heading": "A scope-bearing enquiry",
          "body": [
            "Send invented headers and expected/actual results for the first failing boundary, confirm the working trigger and disposable test route, and identify the finance approver and budget holder. State your response to £145 only if that existing scope fits. No source code, financial exports, credentials or invitations initially. The price is an untested proposal; an enquiry creates no booking, account access or financial authority and payment follows agreed checks and sign-off."
          ],
          "items": []
        }
      ],
      "incoming": [],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/collections/finance-invoice-integrity-checks/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/collections/finance-invoice-integrity-checks/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fcollections%2Ffinance-invoice-integrity-checks%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fcollections%2Ffinance-invoice-integrity-checks%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-sheets-invoice-value-rendering",
      "type": "guide",
      "title": "Invoice values look right in Sheets but arrive as currency text or a date number",
      "description": "Separate a spreadsheet's displayed currency and date from the values a connector reads, then agree the destination types before repairing a mapping.",
      "url": "/guides/sheets-invoice-value-rendering/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "google-sheets",
        "zapier",
        "xero"
      ],
      "buyer_role": "Finance owner approving the types of mapped invoice fields",
      "purchase_trigger": "A populated source cell reaches an invoice action in an unexpected numeric or textual representation",
      "problem": "Display formatting, formula text and evaluated values are mistaken for the same connector input.",
      "related": [
        "guide-zapier-field-mapping",
        "guide-sheets-xero-line-item-shape",
        "guide-invoice-tax-amount-basis",
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "Google Sheets API value render options",
          "url": "https://developers.google.com/sheets/api/reference/rest/v4/ValueRenderOption",
          "checked": "2026-10-11",
          "claims": [
            "Formatted values apply spreadsheet locale and cell formatting; unformatted values evaluate formulas without formatting; FORMULA returns formula text."
          ]
        },
        {
          "label": "Google Sheets values.get defaults",
          "url": "https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get",
          "checked": "2026-10-11",
          "claims": [
            "valueRenderOption defaults to FORMATTED_VALUE; dateTimeRenderOption is ignored with FORMATTED_VALUE."
          ]
        },
        {
          "label": "Google Sheets number and date formats",
          "url": "https://developers.google.com/sheets/api/guides/formats",
          "checked": "2026-10-11",
          "claims": [
            "Dates and times have serial-number representations; locale influences rendering. A currency format is presentation, not currency conversion."
          ]
        },
        {
          "label": "Existing five-field Sheets-to-Xero mapping offer",
          "url": "https://syntheticindustry.ai/services/zapier-sheets-to-xero-missing-columns/",
          "checked": "2026-10-11"
        }
      ],
      "sections": [
        {
          "heading": "A visible cell is only one representation",
          "body": [
            "With invented data, a numeric 1234.50 may display with a currency symbol and grouping characters. A date can be displayed as calendar text while the underlying value is a serial number. Record the visible cell, the connector's captured value and type, and the action's required type separately. This is not the missing-header problem: the value exists but its representation may not fit the destination."
          ],
          "items": [
            "Record spreadsheet locale, cell format and the intended invoice currency separately.",
            "Distinguish a formula such as =A1 from its evaluated result.",
            "Do not parse an ambiguous date such as 03/04/2026 by guessing its locale."
          ]
        },
        {
          "heading": "The API explains the distinction, not your Zap's controls",
          "body": [
            "Google's API offers formatted, unformatted and formula representations. Its values.get method defaults to formatted output, and the date-time option has no effect in that mode. Sheets date serials count from December 30, 1899 and can contain a fractional day. These are documented API behaviours, not a claim that the current Zapier trigger exposes an option with the same name or uses a particular mode. Inspect the actual captured sample through the authorised operator; do not change a live connector based on an API recipe."
          ],
          "items": [
            "The connector version and selected action must be verified separately.",
            "A currency symbol does not establish the currency code or an exchange rate.",
            "If conversion is necessary, agree the exact date and monetary input contract first."
          ]
        },
        {
          "heading": "A useful acceptance case changes the display, not the business meaning",
          "body": [
            "Use invented amounts and an unambiguous invoice date on an authorised disposable route. Declare whether changing only cell presentation should leave the destination amount and date unchanged. Compare the mapped amount, date and currency after each agreed presentation variant. If a serial or formatted string is unsupported, require a visible exception rather than silent zero, today's date or a guessed conversion. These are proposed checks; no Sheet, connector or invoice was exercised for this draft."
          ],
          "items": [
            "Inspect destination types and values, not just a screenshot that looks similar.",
            "Keep a missing-value case distinct from a wrong-type case."
          ]
        },
        {
          "heading": "DIY first, and preserve the £145 boundary",
          "body": [
            "An authorised maintainer may resolve the issue by inspecting the trigger sample and correcting an existing mapping; Google documentation is free to read. The existing £145 Sheets-to-Xero offer concerns an already-working trigger, up to five named fields and one disposable draft. It is a contextual route, not a promise to build a new date parser, currency converter or variable line-item transformation at that price. Tax setup, historical corrections and sending invoices are excluded. Send invented cell values, displayed forms and expected destination types initially, not financial exports, code, credentials or invitations. The price is untested; work requires agreed scope and payment follows passing checks and sign-off."
          ],
          "items": []
        }
      ],
      "incoming": [
        "tool-invoice-rounding-bench",
        "collection-finance-invoice-integrity-checks",
        "guide-invoice-tax-amount-basis"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/sheets-invoice-value-rendering/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/sheets-invoice-value-rendering/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Fsheets-invoice-value-rendering%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Fsheets-invoice-value-rendering%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-invoice-tax-amount-basis",
      "type": "guide",
      "title": "An invoice integration adds tax twice: agree whether each amount is net or gross",
      "description": "Name the tax basis of each mapped line and the approved tax treatment before mistaking a populated amount field for a correct invoice.",
      "url": "/guides/invoice-tax-amount-basis/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "google-sheets",
        "xero",
        "accounting-sync"
      ],
      "buyer_role": "Finance owner responsible for invoice amount and tax-policy sign-off",
      "purchase_trigger": "The destination total differs even though all source amounts were populated",
      "problem": "A tax-inclusive amount is treated as tax-exclusive, or tax calculation policy is silently changed at a mapping boundary.",
      "related": [
        "example-invoice-tax-rounding-reconciliation",
        "guide-sheets-invoice-value-rendering",
        "buyer-finance-manager-invoice-integration",
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "HMRC VAT Notice 700, sections 17.5 and 17.6",
          "url": "https://www.gov.uk/guidance/vat-guide-notice-700",
          "checked": "2026-10-11",
          "claims": [
            "Invoice-trader rounding concessions and retailer treatment differ; line-calculation methods require consistency. This is UK-specific guidance, not a universal rounding rule or a determination of a reader's tax treatment."
          ]
        },
        {
          "label": "Google Sheets formatting is presentation",
          "url": "https://developers.google.com/sheets/api/guides/formats",
          "checked": "2026-10-11",
          "claims": [
            "Number patterns and spreadsheet locale affect rendered values; they do not choose business tax policy."
          ]
        },
        {
          "label": "Existing mapping repair excludes Xero tax configuration",
          "url": "https://syntheticindustry.ai/services/zapier-sheets-to-xero-missing-columns/",
          "checked": "2026-10-11"
        }
      ],
      "sections": [
        {
          "heading": "Put a basis beside the amount",
          "body": [
            "For each invented line, name currency, quantity, unit amount, whether the unit amount includes tax, the approved tax treatment and any permitted discount. A field labelled amount is insufficient. Formatting it to two decimals does not tell the destination whether tax is included. The finance owner or their qualified adviser supplies the applicable treatment; this guide does not decide rates, exemptions or tax liability."
          ],
          "items": [
            "Separate monetary basis from which account or tax identifier is mapped.",
            "Do not assume a blank tax field means exempt or zero-rated.",
            "The actual connector's supported fields must be verified; no Xero or QuickBooks action configuration is prescribed here."
          ]
        },
        {
          "heading": "A synthetic double-tax counterexample",
          "body": [
            "Assume solely for invented arithmetic a net amount of 20.00 and a 20% rate. The declared result is 4.00 tax and 24.00 gross. If 24.00 is passed as a net amount and another 20% is added, the result becomes 28.80. The 4.80 difference is a basis mistake, not merely a penny-rounding issue. These numbers are not a recommendation to apply that rate to any real supply."
          ],
          "items": [
            "Assert the net, tax and gross components, not only the grand total.",
            "If the approved basis is unknown, hold the proposed draft instead of trying both settings on real invoices."
          ]
        },
        {
          "heading": "Rounding and tax setup belong to an explicit policy",
          "body": [
            "HMRC's UK guidance distinguishes invoice traders from retailers and describes different rounding treatments. It cannot safely be reduced to always round down or always round each line. Agree the policy that applies to the business, then record where rounding occurs and how the destination applies it. The related synthetic example exposes a line-versus-invoice difference without choosing a legally applicable policy."
          ],
          "items": [
            "Do not adjust a final total merely to hide disagreeing component amounts.",
            "No live invoice, tax configuration or financial correction has been performed."
          ]
        },
        {
          "heading": "Mapping repair is not tax advice",
          "body": [
            "Use the existing maintainer, vendor help or your finance adviser when the policy or account setup is unresolved. A missing field under an already approved treatment may be discussed through the existing £145 five-field Sheets-to-Xero mapping offer, only with its working trigger and authorised disposable-draft route. Fixing tax configuration, deciding tax policy, custom arithmetic, historical invoices and live sending are outside that offer. Send invented net/tax/gross expectations and the missing mapping, not tax returns, customer details or credentials. Scope must be confirmed; the price is untested and payment follows agreed checks and sign-off."
          ],
          "items": []
        }
      ],
      "incoming": [
        "tool-invoice-rounding-bench",
        "example-invoice-tax-rounding-reconciliation",
        "collection-finance-invoice-integrity-checks",
        "guide-sheets-invoice-value-rendering"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/invoice-tax-amount-basis/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/invoice-tax-amount-basis/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Finvoice-tax-amount-basis%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Finvoice-tax-amount-basis%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    },
    {
      "id": "guide-invoice-create-unknown-outcome",
      "type": "guide",
      "title": "Invoice creation timed out: reconcile the unknown result before retrying",
      "description": "Distinguish a confirmed rejected creation from a lost response after a possible write, and define zero, one and conflicting-match dispositions before a replay.",
      "url": "/guides/invoice-create-unknown-outcome/",
      "version": 1,
      "lifecycle": "candidate",
      "updated": "2026-10-11",
      "platforms": [
        "accounting-sync",
        "zapier",
        "xero"
      ],
      "buyer_role": "Finance owner deciding whether an accounting automation may retry",
      "purchase_trigger": "An action reports a timeout and nobody knows whether the unsent invoice already exists",
      "problem": "A failed transport response is treated as proof that invoice creation had no effect.",
      "related": [
        "example-invoice-retry-identity-matrix",
        "buyer-finance-manager-invoice-integration",
        "guide-zapier-field-mapping",
        "zapier-sheets-to-xero-missing-columns"
      ],
      "provenance": [
        {
          "label": "IETF RFC 9110 section 9.2.2: retry semantics",
          "url": "https://www.rfc-editor.org/rfc/rfc9110.html",
          "checked": "2026-10-11",
          "claims": [
            "A client should not automatically retry a non-idempotent method without knowing its semantics are idempotent or detecting that the original request was never applied. This does not establish any accounting vendor's idempotency support."
          ]
        },
        {
          "label": "Existing mapping offer and repeated-input acceptance",
          "url": "https://syntheticindustry.ai/services/zapier-sheets-to-xero-missing-columns/",
          "checked": "2026-10-11"
        }
      ],
      "sections": [
        {
          "heading": "Unknown is a separate outcome",
          "body": [
            "A timeout can occur before a write or after a write whose response was lost. Preserve the minimal synthetic source reference, target organisation, attempted operation and result state. Do not replace unknown with failed-and-empty merely because the workflow is red. RFC 9110 explains why automatic retries of non-idempotent requests need more evidence; it does not promise a safe replay facility in Xero, QuickBooks or Zapier."
          ],
          "items": [
            "A new attempt identifier is not a new business invoice.",
            "A human-readable invoice number alone is not proven globally unique.",
            "Do not infer that switching a creation request to PUT makes the business operation safe."
          ]
        },
        {
          "heading": "Specify reconciliation before another create",
          "body": [
            "Under an explicitly authored test policy, one matching target draft with the agreed source reference and expected contents means retain that target, not create another. More than one match, or one match with different contents, is an exception for the authorised finance owner. Zero visible matches is not automatically permission to retry: delayed visibility or another in-flight attempt can still exist. The implementer must establish the actual vendor's lookup, duplicate-prevention and concurrency semantics separately."
          ],
          "items": [
            "Keep organisation and immutable source-operation identity together.",
            "Record confirmed rejected, confirmed created and unknown separately.",
            "A read-then-create sequence alone is not concurrency protection."
          ]
        },
        {
          "heading": "Acceptance includes the lost-response branch",
          "body": [
            "An isolated synthetic specification simulates creation followed by loss of the response. Its repeated operation must retain one draft or enter the approved unresolved state, never silently create a second draft. Also specify a confirmed rejection and conflicting-target case. The related matrix provides concrete identities and expected dispositions. No API, replay, target lookup, concurrent worker or invoice has been run for this draft."
          ],
          "items": [
            "Inspect target count and identity beside the workflow result.",
            "Never bulk replay historical invoices as diagnosis.",
            "Deleting a duplicate or changing a posted document is a separate financial action."
          ]
        },
        {
          "heading": "The £145 offer is a mapping route, not an idempotency redesign",
          "body": [
            "An authorised operator may investigate existing run and draft state using vendor support, with no new paid integration required. The existing £145 mapping repair includes an agreed repeated-input check on one disposable row, not a new reconciliation store, queue, retry architecture or historical cleanup. Enquire there only for the eligible five-field mapping problem with a working trigger and a safe draft test; unknown-state recovery or missing duplicate safeguards need separately agreed scope. Send an invented operation identity and expected result initially, not real invoice exports, source code or keys. The price remains untested; agreement precedes access and payment follows checks and sign-off."
          ],
          "items": []
        }
      ],
      "incoming": [
        "example-invoice-retry-identity-matrix",
        "collection-finance-invoice-integrity-checks"
      ],
      "request": {
        "method": "POST",
        "endpoint": "https://backoffice.syntheticindustry.ai/api/enquiries",
        "media_type": "application/json",
        "required": [
          "contact",
          "need",
          "idempotency_key",
          "website"
        ],
        "fields": {
          "outcome": "custom-outcome",
          "source": "/guides/invoice-create-unknown-outcome/",
          "website": ""
        },
        "idempotency_key": "UUID v4; reuse unchanged answers on retry",
        "receipt": "Durable receipt; server email delivery may be pending. Not an order.",
        "fallback": "Email with SI-* headers",
        "to": "hello@syntheticindustry.ai",
        "headers": {
          "SI-Outcome": "custom-outcome",
          "SI-Mode": "general",
          "SI-Page": "https://backoffice.syntheticindustry.ai/guides/invoice-create-unknown-outcome/",
          "SI-Ref": "unknown"
        },
        "url": "https://backoffice.syntheticindustry.ai/request/?outcome=custom-outcome&source=%2Fguides%2Finvoice-create-unknown-outcome%2F",
        "mailto": "mailto:hello@syntheticindustry.ai?subject=custom-outcome&body=SI-Outcome%3A%20custom-outcome%0ASI-Mode%3A%20general%0ASI-Page%3A%20https%3A%2F%2Fbackoffice.syntheticindustry.ai%2Fguides%2Finvoice-create-unknown-outcome%2F%0ASI-Ref%3A%20unknown%0A%0AName%3A%0ACompany%3A%0AWhat%20you%20need%3A%0ASoftware%20or%20systems%3A%0AContext%20link%20(optional)%3A%0AContact%20email%3A%0A%0APlease%20leave%20out%20passwords%2C%20keys%2C%20recovery%20links%2C%20confidential%20code%20and%20customer%20data.",
        "stored": "Website POST saves a durable first-party receipt; the host records it in the ledger and sends email. Success means saved, not delivery or acceptance."
      }
    }
  ]
}
