---
title: "A window closed and one holder never turned up for it."
method: POST
path: "pass.holder_missed"
tags: ["pass.* events"]
---

# A window closed and one holder never turned up for it.

`POST pass.holder_missed` (webhook)

### When this fires

An access window closed and a specific holder was recorded as **Missed**: they paid, the window ran, and they never joined.

`pass.window_closed` already reports an aggregate `subscribers_affected`, which is the wrong half for outreach. Who did not turn up is the useful question, the answer is already computed and stored per holder, and this is that answer.

> **Missed is a claim about the customer, so it is made carefully.** A holder is only marked Missed when they were never admitted. If they bought so late that the window was effectively over before their payment cleared, the pass is settled as ended rather than missed and **this event does not fire**; Subscriby will not tell you a paying customer no-showed when the timing was ours. One exception runs the other way: when a window never opened at all because of a worker outage, its ungranted holders *are* recorded as Missed and this event fires for each of them, so treat a burst of these for a single window as an outage signal rather than as no-shows.

### Caveats

- `join_requested` is the field worth branching on. A holder who never queued probably never saw the message; that is a reachability problem. A holder who queued and still did not appear made a choice, and chasing them the same way reads as nagging.
- **On a series this fires per date.** Missing one date of a season affects nothing else: the rest of the slate runs as booked, so treat a single occurrence as a data point rather than a churn signal. `pass_series.completed` carries the final tally, which is the figure that actually means something.
- It fires alongside `pass.window_closed` for the same window. Both are correct (one is per holder, one is the aggregate) so an endpoint subscribed to both receives one of each.

### Related events

- `pass.holder_queued`: the engagement this is the absence of.
- `pass.window_closed`: the aggregate for the same window.
- `pass_series.leg_completed`: the same close, seen as progress through a season.

## Headers

- `SB-Signature` string, required
- `SB-Event-Id` string, required
- `SB-Event-Name` string, required
- `Content-Type` string, required
- `User-Agent` string, required

## Payload

- object: The envelope every event is delivered in.
  - `id` string, required: A ULID unique to the event, prefixed `evt_`. Every retry carries the same id, so it is the key to deduplicate on.
  - `type` 'pass.holder_missed', required: The event name, always `pass.holder_missed` here.
  - `created_at` string, date-time, required: When the event was emitted, server time, ISO 8601.
  - `api_version` string, required: The contract version of the payload shape, `2026-05-01` today. A breaking change to a shape ships under a new version.
  - `project_id` string, uuid, nullable, required: The project the event belongs to; null for team-scoped and billing events.
  - `data` object, required: The event-specific payload. Keys that do not apply to a given emission are omitted rather than sent as null, so check for presence.
    - `subscription_id` string, uuid, required: The purchase that went unused.
    - `subscriber_id` string, uuid, required: The project user who did not turn up.
    - `plan_id` string, uuid, required: The plan the **window** belongs to.
    - `window_id` string, uuid, required: The window they missed.
    - `starts_at` string, date-time, required: Window start, **always UTC**.
    - `ends_at` string, date-time, required: Window end, always UTC.
    - `join_requested` boolean, required: Whether they ever tapped their invite link. `false` means they never engaged at all; `true` means they queued and still did not appear.

## Acknowledgement `2XX`

Your endpoint acknowledged the delivery. Any 2xx status within 30 seconds marks it delivered; the response body is ignored.

## Other responses

- `default`: Any other status, a connection failure, or no answer within 30 seconds counts as a failed attempt. The delivery is retried 8 times, after 10 seconds, 30 seconds, 2 minutes, 10 minutes, 1 hour, 6 hours, 1 day, 3 days; the last failure dead-letters it, and it can be retried from the dashboard or `POST /v1/webhook-deliveries/{delivery}/retry`. After 20 consecutive failures the endpoint is paused until it is resumed.

---

[API](https://skmtc.dev/memberpass/apis/subscriby-rest-api.md) · [All operations](https://skmtc.dev/memberpass/apis/subscriby-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/memberpass/apis/subscriby-rest-api/revisions/7b538e472e9a?raw)
