# Record Attendance

Write the outcome — attended, no-show or canceled — onto this contact's event registration, with an optional minutes-attended figure for engagement grading.

## Getting here

**Automate → Workflows**, open a workflow, click **+** on the canvas, then pick **Record Attendance** under **Add Steps**.

## Before you start

- An **owner**, **admin** or **agent** can add this step and save the workflow. Publishing that workflow — the thing that makes it run on live conversations — is an **owner** or **admin** action.
- Building and running workflows depends on your plan; see [pricing](/pricing).
- The contact must already be registered for the event — **Register for Event** first. This step only updates an existing registration; it never creates one.

## Set up

1. Open the workflow and click **+** where the record belongs.
2. Pick **Record Attendance**.
3. Choose **Attended**, **No show** or **Canceled**.
4. A fresh step pre-fills the minutes field with `{{payload.minutes}}` — clear it if this run has no such field, or it will fail every time; see Limits.
5. Leave the event dropdown on **the most recent event they registered for**, or name one.
6. Save the step, then publish the workflow. A saved but unpublished workflow records nothing.

## Fields

| Field | What it means | Default | Limits |
| --- | --- | --- | --- |
| Attended / No show / Canceled (dropdown) | The outcome to record. This dropdown carries no on-screen label in the drawer. | Attended | — |
| Minutes attended (placeholder text, no label) | How long they stayed. | `{{payload.minutes}}` on a freshly added step — not blank; see Set up. | If set (including the default), it must resolve to a real number or the whole step fails — see When it fails. |
| The most recent event they registered for (dropdown) | Which registration to update. This dropdown carries no on-screen label either, and lists only events that have not started yet — see Limits for what that means for naming a past one. | The most recent event they registered for | — |

## How it works

- The step resolves the contact behind this conversation and updates their existing registration for the named event — or, left unnamed, for whichever event they registered for that has most recently started. It never registers anyone; that is **Register for Event**'s job.
- Without a named event, the pick is deliberately "the session that has already happened, most recently" — not the nearest event in either direction — so a report about last night's session cannot land on next week's registration. A NAMED event skips that check entirely: it is not required to have started, so the guard against grading a session that has not happened yet only applies when the event is left unnamed.
- If **Minutes attended** is set, it must resolve to a real, non-negative number or the step fails without saving anything — a status saved with no duration would silently send every attendee down whichever lane has no minutes condition, so it refuses rather than guesses.
- The status and the minutes are independent: a run can save the status with minutes left blank, and a later run with the same status can add minutes to that same registration — each field only writes when it actually changed.
- Recording the same status and the same minutes a second time — a replayed webhook, or two reports saying the same thing — is a no-op success: nothing changes and nothing fires again.
- A real status change fires this workspace's **Event Registration Changed** trigger for other workflows. It is not conditioned on the registration's own conversation: any workflow run naming a status change fires it, since a run always has a conversation.
- A minutes-only correction, with the status unchanged, does **not** fire that trigger — it is not treated as a state transition, so a flow chained off the status change does not re-run for a correction.

## When it fails

- This conversation has no linked contact: the step fails with **this conversation has no contact whose attendance to record**.
- **Minutes attended** was set but does not resolve to a number (a missing placeholder, negative text): the step fails with **minutes attended did not resolve to a number (got “<text>”)**, and nothing is saved. This is also what happens if the default `{{payload.minutes}}` is left in place on a run whose trigger never supplies `payload.minutes`.
- The contact has no registration to update — for the named event, or for any event at all: the step fails with **this contact is not registered for that event** or **this contact is not registered for any event**.
- Left unnamed, none of the contact's registrations are for an event that has started yet: the step fails with **no event this contact registered for has started yet**.
- A database error reading or saving the registration fails the step with a specific reason (**could not read this contact's registrations** or **could not save the attendance**).
- A failed record does not stop the run: the step is recorded failed and the flow moves straight to the next one.

## Limits

- It cannot register a contact — only update a registration **Register for Event** already created.
- Left unnamed, it only ever considers events that have already started, and picks the most recently started one — a contact with two past sessions on file gets only the newer one graded by this step.
- The event dropdown lists only events that have not started yet — the same list **Register for Event** offers. A past event cannot be picked; to grade one, leave the dropdown on **The most recent event they registered for**.
- A fresh step defaults **Minutes attended** to `{{payload.minutes}}`, which fails the step on any run whose trigger does not populate that field — clear the field if the status alone is what this run should record.
- It does not repeat a status transition's trigger for a minutes-only correction — a flow reacting to attendance will not see a second run just because the minutes were adjusted afterwards.

## Use cases

- A webinar platform posts each attendee's report to an **Incoming Webhook** workflow; this step records **Attended**, and the default `{{payload.minutes}}` reads the minutes from that report.
- Mark **Canceled** when a contact replies that they can no longer make it — clear **Minutes attended** first, since the default `{{payload.minutes}}` has nothing to resolve from a chat reply and would fail the step otherwise.

## Related

- [Register for Event](https://inchat.inseller.my/help/workflows/register-for-event)
- [Workflows: how a flow runs](https://inchat.inseller.my/help/workflows/workflows-overview)

---

Source files: `src/lib/workflows/ai-builder/spec.ts`, `src/lib/workflows/canvas-model.ts`, `src/components/workflows/WorkflowStepConfigDrawer.tsx`, `src/lib/workflows/engine-record-actions.ts`, `src/lib/events/attendance.ts`, `src/lib/workflows/types.ts`, `src/components/workflows/WorkflowTriggerConfigDrawer.tsx`, `src/app/(app)/workflows/[id]/page.tsx`
