# Reading Reply

Opens the KPI reading form from the Slack digest, saves the Observation with evidence and redraws the checklist.

| Platform | Workflow ID | Status | Version | Last saved | Nodes | Systems |
| --- | --- | --- | --- | --- | --- | --- |
| n8n | wp6F0Xo5M8Nc0YfF | Active | 5a45f2f8-9869-440e-a6ee-361409aa90bf | 2026-11-06T19:40:38.652Z | 50 | KPIs & OKRs, Tooling and Access |

Reading Reply handles what a person does with a KPI reading request in Slack. The [Daily Digest](/automations/daily-digest) posts a checklist of readings; when someone ticks an item, Reading Reply opens a form, saves the value, note and evidence as an Observation in KPIs & OKRs, and rewrites the checklist so the item shows as recorded. Closing the form or a click it cannot read only refreshes the checklist.

The KPIs & OKRs maintainer owns the schedules and the recorded Observations; the Tooling and Access maintainer owns the Slack bot accounts the workflow borrows its token from.

## Flow

Generated from the n8n version in the box above: every box is a node and every arrow is a connection in n8n. Pink pills are triggers, yellow hexagons are If and Switch nodes with their outputs on the arrows, blue cylinders read (Airtable, Data Table, HTTP GET), green cylinders write, purple boxes call another workflow, and dashed arrows are error outputs.

```mermaid
flowchart TD
  subgraph g0["Receive and verify"]
    n40(["Slack Interaction"]):::trigger
    n10["Config"]:::logic
    n31["Parse Payload"]:::logic
    n47{{"Valid Interaction"}}:::decision
    n1["Ack Reply"]:::logic
    n0["Ack Error"]:::logic
  end
  subgraph g1["Slack token"]
    n17["Get Cached Bot<br/><small>Slack — Workspace Token Cache</small>"]:::subflow
    n36["Resolve Slack Account"]:::logic
    n42{{"Token Available?"}}:::decision
    n35["Refresh Workspace in Background<br/><small>Slack — Workspace Token Cache</small>"]:::subflow
    n5{{"Can Reply to Interaction?"}}:::decision
    n37[("Restore Task Selection<br/><small>POST Slack response URL</small>")]:::write
    n28[("Notify Retry<br/><small>POST Slack response URL</small>")]:::write
  end
  subgraph g2["Route"]
    n29{{"Open Form First"}}:::decision
    n43{{"Trigger Still Fresh?"}}:::decision
    n30[("Open Loading Modal<br/><small>POST Slack views.open</small>")]:::write
    n26["Modal Opened"]:::logic
    n2["Airtable Schema"]:::logic
    n41{{"Switch Route"}}:::decision
  end
  subgraph g3["Open the form"]
    n20[("Get Schedule<br/><small>GET KPIs Recurrence Configuration</small>")]:::read
    n16[("Find Previous Reading<br/><small>GET KPIs Observation</small>")]:::read
    n3["Build Modal"]:::logic
    n45[("Update Modal<br/><small>POST Slack views.update</small>")]:::write
    n7["Check Modal Update"]:::logic
  end
  subgraph g4["Record the reading"]
    n21[("Get Submitted Schedule<br/><small>GET KPIs Recurrence Configuration</small>")]:::read
    n15[("Find Existing<br/><small>GET KPIs Observation</small>")]:::read
    n33["Prepare Observation"]:::logic
    n38[("Save Observation<br/><small>PATCH KPIs Observation</small>")]:::write
    n39["Saved Observation"]:::logic
    n22{{"Has New Files"}}:::decision
    n24["List Evidence Files"]:::logic
    n18[("Get Evidence Info<br/><small>GET Slack files.info</small>")]:::read
    n48["Validate Evidence"]:::logic
    n11[("Download Evidence<br/><small>GET Slack file download</small>")]:::read
    n12["Encode Evidence"]:::logic
    n46[("Upload Evidence<br/><small>POST KPIs Observation · Evidence Files°</small>")]:::write
    n13["Evidence Uploaded"]:::logic
    n32["Prepare Completion"]:::logic
    n9[("Complete Observation<br/><small>PATCH KPIs Observation</small>")]:::write
  end
  subgraph g5["Refresh the digest message"]
    n14[("Fetch Digest Message<br/><small>GET Slack conversations.history</small>")]:::read
    n34["Reading Keys"]:::logic
    n19[("Get Recorded Readings<br/><small>GET KPIs Observation</small>")]:::read
    n4["Build Updated Blocks"]:::logic
    n44[("Update Digest Message<br/><small>POST Slack chat.update</small>")]:::write
    n8["Check Slack Update"]:::logic
  end
  subgraph g6["Failure"]
    n6["Capture Failure"]:::logic
    n23{{"Has Saved Record"}}:::decision
    n25[("Mark Failed<br/><small>PATCH KPIs Observation</small>")]:::write
    n27[("Notify Failure<br/><small>POST Slack chat.postEphemeral</small>")]:::write
  end
  n30 --> n26
  n30 -.->|error| n6
  n20 --> n16
  n20 -.->|error| n6
  n16 --> n3
  n16 -.->|error| n6
  n45 --> n7
  n45 -.->|error| n6
  n21 --> n15
  n21 -.->|error| n6
  n15 --> n33
  n15 -.->|error| n6
  n38 --> n39
  n38 -.->|error| n6
  n18 --> n48
  n18 -.->|error| n6
  n11 --> n12
  n11 -.->|error| n6
  n46 --> n13
  n46 -.->|error| n6
  n9 --> n14
  n9 -.->|error| n6
  n14 --> n34
  n14 -.->|error| n6
  n19 --> n4
  n19 -.->|error| n6
  n44 --> n8
  n44 -.->|error| n6
  n36 --> n42
  n36 -.->|error| n35
  n26 --> n2
  n26 -.->|error| n6
  n3 --> n45
  n3 -.->|error| n6
  n7 --> n14
  n7 -.->|error| n6
  n33 --> n38
  n33 -.->|error| n6
  n39 --> n22
  n39 -.->|error| n6
  n24 --> n18
  n24 -.->|error| n6
  n48 --> n11
  n48 -.->|error| n6
  n12 --> n46
  n12 -.->|error| n6
  n13 --> n32
  n13 -.->|error| n6
  n32 --> n9
  n32 -.->|error| n6
  n34 --> n19
  n34 -.->|error| n6
  n4 --> n44
  n4 -.->|error| n6
  n8 -.->|error| n6
  n40 --> n10
  n10 --> n31
  n31 --> n47
  n47 -->|true| n1
  n47 -->|false| n0
  n1 --> n17
  n2 --> n41
  n6 --> n23
  n25 --> n27
  n41 -->|open| n20
  n41 -->|record| n21
  n41 -->|refresh| n14
  n22 -->|true| n24
  n22 -->|false| n32
  n23 -->|true| n25
  n23 -->|false| n27
  n29 -->|true| n43
  n29 -->|false| n2
  n17 --> n36
  n17 -.->|error| n35
  n42 -->|true| n29
  n42 -->|false| n35
  n43 -->|true| n30
  n43 -->|false| n5
  n35 --> n5
  n5 -->|true| n37
  n37 --> n28
  classDef trigger fill:#fde7f3,stroke:#d6409f,color:#111
  classDef decision fill:#fff7e0,stroke:#c99a00,color:#111
  classDef read fill:#e8f1ff,stroke:#3b82f6,color:#111
  classDef write fill:#e7f8ee,stroke:#16a34a,color:#111
  classDef subflow fill:#f3e8ff,stroke:#9333ea,color:#111
  classDef wait fill:#f1f1f1,stroke:#888,color:#111
  classDef logic fill:#ffffff,stroke:#999,color:#111
```

## Systems it touches

| System | Table | Reads | Writes |
| --- | --- | --- | --- |
| KPIs & OKRs | Recurrence Configuration | Get Schedule, Get Submitted Schedule | Nothing |
| KPIs & OKRs | Observation | Find Previous Reading, Find Existing, Get Recorded Readings | Save Observation, Upload Evidence, Complete Observation, Mark Failed |
| Tooling and Access | Tool Account, through [Slack — Workspace Token Cache](/automations/workspace-token-cache) | Get Cached Bot | Nothing; Refresh Workspace in Background asks the cache to reload |

Airtable calls are HTTP Request nodes with the credential "Airtable PAT (Fully Scoped)", and they read and write by field ID. Airtable Schema holds the map of base, table and field IDs that the code nodes use; it also lists Communication and Tooling tables, but no node reads or writes them.

Slack calls use the credential "Systems Slack Automation - Dynamic", which uses the bot token the Token Cache returned for the person's workspace. The workflow calls `views.open`, `views.update`, `conversations.history`, `chat.update`, `chat.postEphemeral` and `files.info`, downloads evidence files, and posts to the interaction's response URL.

## Trigger and signature

Slack Interaction accepts a POST from the Slack app's interactivity URL and keeps the raw body. It answers only through Ack Reply or Ack Error, so Slack gets its answer after Parse Payload has checked the request and before any Airtable call.

Config sets the values the rest of the workflow reads:

| Config field | Value | Used for |
| --- | --- | --- |
| slackSigningSecret | The Slack app's signing secret | Signature check |
| timezone | America/Chicago | The time shown next to a recorded item |
| confirmInThread | false | Not used by any node |
| requireEvidence | false | Whether the evidence field is required |
| allowDecimalValue | false | Whether the quantity accepts decimals |
| maxUploadBytes | 5000000 | Largest evidence file accepted in the form |
| testSlackUserId | A single Slack user ID | Only this user is accepted while it is set |

Parse Payload rejects the request with a message, which Ack Error returns to Slack, when:

- the body is missing, the `x-slack-request-timestamp` header is missing or 300 seconds old or more, or the `x-slack-signature` HMAC-SHA256 does not match (timing-safe comparison);
- the payload cannot be decoded;
- `testSlackUserId` is set and the person is someone else ("Collection is being tested; please try again later");
- the item belongs to another person, the workspace ID is not a Slack team ID, or the schedule, membership or period in the item has the wrong format;
- the original channel or message is unknown.

For a form submission, Ack Error shows the error on the quantity or evidence field instead.

### What Parse Payload reads from the interaction

| Interaction | Route | Item taken from |
| --- | --- | --- |
| Checkbox click (`reading_reply` or `checkboxes-action`) that changes exactly one item | open | The changed option's value: schedule record, period, membership record and Slack user |
| Checkbox click that changes zero or several items | refresh | Nothing; the checklist is redrawn |
| Button with the same action IDs | open | The JSON in the button value |
| Form submission (`task_quantity`) | record | The form's private metadata, plus quantity, note and files |
| Form closed | refresh | The form's private metadata |
| Anything else | Rejected | "Unsupported interaction" |

The quantity must be a non-negative number (a whole number while `allowDecimalValue` is false). Up to four files, each at most `maxUploadBytes`. An item's Occurrence Key is `<schedule record>:<period>` and its Submission Key is `<schedule record>:<period>|<membership record>`.

## Slack token

After Ack Reply, Get Cached Bot calls [Slack — Workspace Token Cache](/automations/workspace-token-cache) with the workspace ID. For the open route it asks for the cached token only; for record and refresh it lets the cache load from Tooling when needed. Resolve Slack Account accepts the answer only when it is for the same workspace, has a token and does not expire in the next second.

Without a usable token, Refresh Workspace in Background asks the cache to reload this workspace and does not wait. When Slack sent a response URL, Restore Task Selection puts the checklist back as it was and Notify Retry tells the person "The form is not ready yet. Please click the task again in a few seconds. No response was recorded."

## Open the form

Slack only accepts a form opened within three seconds of the click. Trigger Still Fresh? lets the open route continue only within 2.5 seconds of the click; after that it goes to the retry message above.

1. Open Loading Modal opens a "Loading reading" form right away, and Modal Opened keeps its view ID.
2. Get Schedule reads the Recurrence Configuration and Find Previous Reading looks for an Observation with the same Submission Key.
3. Build Modal stops when the schedule's Accountable Membership* does not include the person's membership. It then builds the form: the schedule name, the period, the goal ("At least", "At most" or "Target", from Target (Value) and Target (Direction)), a quantity field in Target (Unit Text), a note and an evidence field (up to 4 images or PDFs). A previous reading fills the quantity and note.
4. Update Modal replaces the loading form, and Check Modal Update fails when Slack refuses.
5. The flow then refreshes the digest message (below), so the checkbox the person just ticked goes back to its recorded state.

## Record the reading

1. Get Submitted Schedule reads the Recurrence Configuration and Find Existing looks for Observations with the Submission Key.
2. Prepare Observation stops when:
   - the schedule does not belong to the membership;
   - more than one Observation has that key ("Duplicate collection keys need reconciliation");
   - the existing one is Processing and less than two minutes old;
   - the schedule has no Target (Metric).

   It then builds the upsert with Collection Status* Processing and works out which files are new, by comparing with Slack File IDs and the file names already in Evidence Files°.
3. Save Observation upserts on Submission Key and Saved Observation keeps the record ID.
4. When there are new files, each one goes through Get Evidence Info, Validate Evidence, Download Evidence, Encode Evidence and Upload Evidence. Only `files.slack.com` URLs, at most 5 MB, of type png, jpg, jpeg, gif, webp or pdf, are accepted. They are stored as `<Slack file ID>__<name>`.
5. Prepare Completion checks that every new file was uploaded, and Complete Observation sets Recorded.
6. The flow then refreshes the digest message.

## Refresh the digest message

Fetch Digest Message reads the original message, and Reading Keys lists the Submission Keys in its checkboxes. Get Recorded Readings reads those Observations, up to 100.

Build Updated Blocks then redraws the checklist:
- Recorded items are struck through with the time in the Config time zone.
- The header shows "&lt;n&gt; of &lt;total&gt; responses recorded".
- "All responses in this message are recorded." is added when every item is done.

Update Digest Message writes it back, and Check Slack Update fails when Slack refuses.

## Failure path

From Open Loading Modal on, every Airtable and Slack call and every code node except Airtable Schema sends its errors to Capture Failure. Capture Failure keeps the first 400 characters of the error and removes anything that looks like a Slack token. It also works out whether a record was saved:

- **Saved but not completed:** Mark Failed sets Collection Status* to Failed with the error in Collection Error, then Notify Failure tells the person "Your reading could not be completed. Please try again."
- **Completed, then the message update failed:** Notify Failure says the reading was saved but the message could not be refreshed.
- **Nothing saved on the open route:** Notify Failure says the form could not be opened and nothing was recorded.

Notify Failure is a private message in the channel.

## Fields written

### Observation

| Field | Save Observation | Upload Evidence | Complete Observation | Mark Failed |
| --- | --- | --- | --- | --- |
| Submission Key | The item's Submission Key | Not changed | Not changed | Not changed |
| Occurrence Key | The item's Occurrence Key | Not changed | Not changed | Not changed |
| Recurrence Configuration° | The schedule | Not changed | Not changed | Not changed |
| Observation About (Membership)* | The membership | Not changed | Not changed | Not changed |
| Variable Measured* | The schedule's Target (Metric) | Not changed | Not changed | Not changed |
| Value* | The quantity | Not changed | Not changed | Not changed |
| Note° | The note | Not changed | Not changed | Not changed |
| Period* | The period | Not changed | Not changed | Not changed |
| Unit Text* | The schedule's Target (Unit Text) | Not changed | Not changed | Not changed |
| Observation Date* | Now | Not changed | Not changed | Not changed |
| Collection Status* | Processing | Not changed | Recorded | Failed |
| Collection Error | Empty | Not changed | Empty | The error, at most 400 characters |
| Evidence° | The previous value, or `slack:<channel>:<message ts>` | Not changed | Previous value, the Slack message reference and each file's permalink | Not changed |
| Evidence Files° | Not changed | Adds each new file | Not changed | Not changed |
| Slack File IDs | Not changed | Not changed | Known IDs plus the submitted ones, one per line | Not changed |

No write uses typecast, so a renamed Collection Status* option makes the write fail instead of creating a new option.

### Recurrence Configuration (read only)

| Field | Used for |
| --- | --- |
| Name* | Form title and header |
| Accountable Membership* | Ownership check |
| Target (Value), Target (Direction), Target (Unit Text) | Goal text and quantity label |
| Target (Metric) | Variable Measured* of the Observation |

## Nodes

The workflow has 50 nodes: 49 that run and one sticky note. For an If node, output 0 is true and output 1 is false. For a node with an error output, output 1 is the error.

| Node | Type | What it does |
| --- | --- | --- |
| Slack Interaction | webhook | Receives the POST from Slack with the raw body; the answer comes from Ack Reply or Ack Error |
| Config | set | Signing secret, time zone, evidence rules and the test user |
| Parse Payload | code | Checks timestamp and signature, decodes the interaction, picks the route |
| Valid Interaction | if | No error from Parse Payload |
| Ack Reply | respondToWebhook | Answers Slack: clears the form on a submission, empty otherwise |
| Ack Error | respondToWebhook | Answers Slack with the error, on the form field when it is a submission |
| Get Cached Bot | executeWorkflow | Calls Slack — Workspace Token Cache for the workspace bot token |
| Resolve Slack Account | code | Accepts the token only if it is for this workspace and not about to expire |
| Token Available? | if | A usable bot token exists |
| Refresh Workspace in Background | executeWorkflow | Asks the cache to refresh this workspace and does not wait |
| Can Reply to Interaction? | if | Slack sent a response URL |
| Restore Task Selection | httpRequest | Puts the checklist back as it was |
| Notify Retry | httpRequest | Tells the person to click again in a few seconds |
| Open Form First | if | Route is open |
| Trigger Still Fresh? | if | Less than 2.5 s since the click |
| Open Loading Modal | httpRequest | Opens a "Loading reading" form with the trigger |
| Modal Opened | code | Fails when Slack did not open the form; keeps its view ID |
| Airtable Schema | code | Fixed map of base, table and field IDs |
| Switch Route | switch | Sends open, record or refresh to its branch |
| Get Schedule | httpRequest | The Recurrence Configuration of the item |
| Find Previous Reading | httpRequest | Observation with the same Submission Key |
| Build Modal | code | Checks ownership and builds the quantity, note and evidence form |
| Update Modal | httpRequest | Replaces the loading form with the real one |
| Check Modal Update | code | Fails when Slack rejected the update |
| Get Submitted Schedule | httpRequest | The Recurrence Configuration of the submission |
| Find Existing | httpRequest | Observation with the same Submission Key |
| Prepare Observation | code | Checks ownership, duplicates and Metric; builds the upsert as Processing |
| Save Observation | httpRequest | Upsert on Submission Key |
| Saved Observation | code | Keeps the saved record ID |
| Has New Files | if | The submission has files not yet stored |
| List Evidence Files | code | One item per new Slack file |
| Get Evidence Info | httpRequest | Slack file metadata |
| Validate Evidence | code | Checks URL, size and type |
| Download Evidence | httpRequest | Downloads the file from Slack |
| Encode Evidence | code | Base64, at most 5 MB |
| Upload Evidence | httpRequest | Uploads into Evidence Files° |
| Evidence Uploaded | code | Counts the uploads |
| Prepare Completion | code | Checks every file arrived; builds the Recorded patch |
| Complete Observation | httpRequest | PATCH to Recorded |
| Fetch Digest Message | httpRequest | Reads the original Daily Digest message |
| Reading Keys | code | Lists the Submission Keys in that message |
| Get Recorded Readings | httpRequest | Observations for those keys (up to 100) |
| Build Updated Blocks | code | Strikes recorded items and counts done of total |
| Update Digest Message | httpRequest | Rewrites the message |
| Check Slack Update | code | Fails when Slack rejected the update |
| Capture Failure | code | Builds a short error without tokens; finds the saved record |
| Has Saved Record | if | An Observation was saved but not completed |
| Mark Failed | httpRequest | PATCH Collection Status Failed with the error |
| Notify Failure | httpRequest | Tells the person privately what happened |
| Collection v5 | stickyNote | Setup note on the canvas; does not run |

## Connections

| From | Output | To |
| --- | --- | --- |
| Slack Interaction | 0 | Config |
| Config | 0 | Parse Payload |
| Parse Payload | 0 | Valid Interaction |
| Valid Interaction | 0 | Ack Reply |
| Valid Interaction | 1 | Ack Error |
| Ack Reply | 0 | Get Cached Bot |
| Get Cached Bot | 0 | Resolve Slack Account |
| Get Cached Bot | 1 | Refresh Workspace in Background |
| Resolve Slack Account | 0 | Token Available? |
| Resolve Slack Account | 1 | Refresh Workspace in Background |
| Token Available? | 0 | Open Form First |
| Token Available? | 1 | Refresh Workspace in Background |
| Refresh Workspace in Background | 0 | Can Reply to Interaction? |
| Can Reply to Interaction? | 0 | Restore Task Selection |
| Restore Task Selection | 0 | Notify Retry |
| Open Form First | 0 | Trigger Still Fresh? |
| Open Form First | 1 | Airtable Schema |
| Trigger Still Fresh? | 0 | Open Loading Modal |
| Trigger Still Fresh? | 1 | Can Reply to Interaction? |
| Open Loading Modal | 0 | Modal Opened |
| Open Loading Modal | 1 | Capture Failure |
| Modal Opened | 0 | Airtable Schema |
| Modal Opened | 1 | Capture Failure |
| Airtable Schema | 0 | Switch Route |
| Switch Route | 0 | Get Schedule |
| Switch Route | 1 | Get Submitted Schedule |
| Switch Route | 2 | Fetch Digest Message |
| Get Schedule | 0 | Find Previous Reading |
| Get Schedule | 1 | Capture Failure |
| Find Previous Reading | 0 | Build Modal |
| Find Previous Reading | 1 | Capture Failure |
| Build Modal | 0 | Update Modal |
| Build Modal | 1 | Capture Failure |
| Update Modal | 0 | Check Modal Update |
| Update Modal | 1 | Capture Failure |
| Check Modal Update | 0 | Fetch Digest Message |
| Check Modal Update | 1 | Capture Failure |
| Get Submitted Schedule | 0 | Find Existing |
| Get Submitted Schedule | 1 | Capture Failure |
| Find Existing | 0 | Prepare Observation |
| Find Existing | 1 | Capture Failure |
| Prepare Observation | 0 | Save Observation |
| Prepare Observation | 1 | Capture Failure |
| Save Observation | 0 | Saved Observation |
| Save Observation | 1 | Capture Failure |
| Saved Observation | 0 | Has New Files |
| Saved Observation | 1 | Capture Failure |
| Has New Files | 0 | List Evidence Files |
| Has New Files | 1 | Prepare Completion |
| List Evidence Files | 0 | Get Evidence Info |
| List Evidence Files | 1 | Capture Failure |
| Get Evidence Info | 0 | Validate Evidence |
| Get Evidence Info | 1 | Capture Failure |
| Validate Evidence | 0 | Download Evidence |
| Validate Evidence | 1 | Capture Failure |
| Download Evidence | 0 | Encode Evidence |
| Download Evidence | 1 | Capture Failure |
| Encode Evidence | 0 | Upload Evidence |
| Encode Evidence | 1 | Capture Failure |
| Upload Evidence | 0 | Evidence Uploaded |
| Upload Evidence | 1 | Capture Failure |
| Evidence Uploaded | 0 | Prepare Completion |
| Evidence Uploaded | 1 | Capture Failure |
| Prepare Completion | 0 | Complete Observation |
| Prepare Completion | 1 | Capture Failure |
| Complete Observation | 0 | Fetch Digest Message |
| Complete Observation | 1 | Capture Failure |
| Fetch Digest Message | 0 | Reading Keys |
| Fetch Digest Message | 1 | Capture Failure |
| Reading Keys | 0 | Get Recorded Readings |
| Reading Keys | 1 | Capture Failure |
| Get Recorded Readings | 0 | Build Updated Blocks |
| Get Recorded Readings | 1 | Capture Failure |
| Build Updated Blocks | 0 | Update Digest Message |
| Build Updated Blocks | 1 | Capture Failure |
| Update Digest Message | 0 | Check Slack Update |
| Update Digest Message | 1 | Capture Failure |
| Check Slack Update | 1 | Capture Failure |
| Capture Failure | 0 | Has Saved Record |
| Has Saved Record | 0 | Mark Failed |
| Has Saved Record | 1 | Notify Failure |
| Mark Failed | 0 | Notify Failure |

## Settings and error handling

| Setting | Value |
| --- | --- |
| Execution order | v1 |
| Error workflow | None |
| Save successful executions | No |
| Save failed executions | No |
| Save manual executions | No |
| Save execution progress | No |
| Retry on fail | Off on every node |
| On error | Error output on Get Cached Bot, Resolve Slack Account and every node from Open Loading Modal on except Airtable Schema and the If and Switch nodes; Notify Retry, Restore Task Selection and Refresh Workspace in Background continue on error; the receiving nodes stop the workflow |
| Caller policy | Workflows from the same owner |
| Available in MCP | Yes |
| HTTP timeout | 20 seconds (5 seconds for the response URL calls) |

n8n keeps no execution data for this workflow, so a failure leaves no trace in n8n: look at Collection Status* and Collection Error on the Observation. The only saved executions are failed runs of an earlier version, from 2026-10-20.

## Known gaps

These come from the node configuration; none of the current version's runs is saved in n8n.

| Gap | Effect |
| --- | --- |
| `testSlackUserId` is set in Config | Every other person gets "Collection is being tested; please try again later" and nothing is saved |
| The Slack signing secret sits in the Config node, not in an n8n credential | Anyone who can open the workflow can read it, and a workflow export carries it |
| A submission when no token is available | Ack Reply has already closed the form; Slack sends no response URL for a form submission, so the reading is dropped with no message |
| Mark Failed and Notify Failure have error outputs with nothing connected | When marking or notifying fails, the execution ends quietly and, with no execution data saved, nothing records it |
| Get Recorded Readings reads one page of 100 | A checklist with more than 100 items shows the rest as not recorded |
| Find Existing and Find Previous Reading put the Submission Key in the formula without escaping | Safe today because Parse Payload checks the format of every part of the key; a looser format would break the lookup |
| Two Observations with the same Submission Key | Every new submission for that item fails until someone deletes one |
| `confirmInThread` in Config | No node reads it |

## Recover a partial write

Look at the Observation with the item's Submission Key before asking the person to try again.

| What was saved | What to do |
| --- | --- |
| Observation in Processing | Wait two minutes; then a new submission replaces it |
| Observation in Failed with Collection Error | Read the error and fix its cause; the person submits again and the record is reused |
| Recorded, but the checklist still shows the item open | Tick any item and close the form; closing redraws the checklist from the Observations |
| Some evidence files missing | The person submits again with the files; files already stored are skipped by their Slack file ID |
| Two Observations for one Submission Key | Keep the right one, then delete the other |

## Keep this page in sync

The box at the top shows the n8n version this page describes. Open the workflow in n8n: when its last save is newer than the date in the box, this page is out of date until the next review. Every node and every connection of that version is listed in [Nodes](#nodes) and [Connections](#connections).