> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ontime.hosai.app/llms.txt
> Use this file to discover all available pages before exploring further.

# The punch lifecycle

> What happens between a finger on a sensor and a day record — acknowledgement timing, deduplication, and parking.

The reliability of terminal attendance comes from three rules working together. This page is those rules.

## The sequence

```mermaid theme={null}
sequenceDiagram
  participant D as Terminal
  participant C as Connector
  participant O as OnTime
  D->>C: TimeLog (raw TCP, XML)
  C->>O: POST /api/ingest/biometric/timelog (HTTPS)
  alt OnTime accepts (2xx)
    O-->>C: 200
    C-->>D: ack
    Note over D: Record cleared from flash
  else OnTime unreachable or rejects
    O--xC: error / timeout
    Note over C: No ack sent
    Note over D: Record retained,<br/>re-sent in ~60s
  end
```

## Rule 1 — ack only after acceptance

<Warning>
  **The connector acknowledges a punch only after OnTime has returned success.**

  The device's flash is the retry queue. No acknowledgement means the device keeps the record and re-sends it about every 60 seconds, holding the rest of its queue behind it.
</Warning>

The consequence is the guarantee: **an outage cannot lose a punch.** OnTime being down, a deploy restarting it, a network cut — in every case the device simply keeps re-sending until it gets through.

Keepalive messages are the exception: they are acknowledged **immediately** and forwarded without waiting, because device liveness must not depend on backend latency.

## Rule 2 — deduplicate on the triple

Because the device re-sends, OnTime will see the same punch more than once. It deduplicates on three fields together:

```text theme={null}
(deviceId, transId, logTime)
```

<Note>
  **Why all three.** The transaction ID alone is not safe — it resets when a device is wiped, so a wiped terminal would start re-issuing IDs that already exist. Including the log time closes that.
</Note>

Together with rule 1 this produces **at-least-once delivery, exactly-once storage**. A punch cannot be missed, and cannot be double-counted.

## Rule 3 — unmapped punches park

If the terminal's user ID does not resolve to an employee — no mapping, no matching code, or an ambiguous suffix match — the punch is **parked**: recorded, marked unmapped, and held.

<Warning>
  **Nothing is ever dropped.** Creating the mapping later replays every parked punch for that terminal user and re-reconciles each affected day. See [Map terminal users](/terminals/map-terminal-users).
</Warning>

## What a resolved punch becomes

Once resolved, the punch becomes an ordinary attendance event:

| Field         | Value                                                                        |
| ------------- | ---------------------------------------------------------------------------- |
| **Source**    | `Biometric`                                                                  |
| **Direction** | In or out, from the terminal's duty-on / duty-off flag                       |
| **Timestamp** | The device's wall-clock time, stamped with the connector's configured offset |
| **Status**    | Approved — there is no geofence check on a terminal punch                    |

The day is then reconciled through the same day engine every other punch source uses. A terminal punch and a mobile punch produce the same kind of day record.

## Photos

If the terminal captured an image with the punch, it is stored and attached to the event as the punch photo — so it appears wherever mobile selfies do, including as evidence on a regularization.

<Note>
  An oversized or corrupt image is skipped and **the punch still lands**. Photo capture never blocks attendance.
</Note>

## Terminal administration events

Enrolling or deleting a user at the device is recorded too. That gives you an enrolment audit trail, and it is the signal that a new terminal user needs mapping.

## The full picture

```mermaid theme={null}
flowchart TB
  P[Punch at terminal] --> C{Connector<br/>forwards}
  C --> S{Serial known?}
  S -->|no| U[Recorded as unclaimed device<br/>200 returned so the queue drains]
  S -->|yes| DD{Already seen?<br/>deviceId + transId + logTime}
  DD -->|yes| OK1[Return OK — no duplicate stored]
  DD -->|no| R{User ID resolves<br/>to an employee?}
  R -->|no| PARK[Parked — held until mapped]
  R -->|yes| E[Attendance event created]
  E --> DAY[Day reconciled]
  PARK -.->|mapping created| E
```

## What this means operationally

<Steps>
  <Step title="A missing punch is almost never lost">
    Check, in order: is the terminal user mapped, is the device online, is the connector running. Each has a distinct signature — see [Troubleshooting](/terminals/troubleshooting).
  </Step>

  <Step title="A backed-up device queue points at the connector, not the device">
    The device holding records means it is not getting acknowledgements, which means the connector is not reaching OnTime — a wrong secret, or a network problem.
  </Step>

  <Step title="Duplicate punches are not a thing">
    If a person appears twice in a day report from one terminal, they punched twice.
  </Step>
</Steps>
