# Event Stage Participation Evidence (`h_murdock/event-stage-participation-evidence`) Actor

Merge supplied participant intervals inside explicit opening, breakout and closing stages. Retain reconnect/parallel-room evidence, coverage limits, missing identities and source ambiguity. JSON or mapped CSV; no certificates or attendance decisions.

- **URL**: https://apify.com/h_murdock/event-stage-participation-evidence.md
- **Developed by:** [Gilad Ronen](https://apify.com/h_murdock) (community)
- **Categories:** Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.25 / completed report

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Event Stage Participation Evidence

Combine authorized attendance exports for an event with an opening session, parallel breakout rooms and a closing session. This Actor produces a participant-by-stage evidence report from your explicit room manifest and observed join/leave intervals. It merges reconnects and overlapping room observations so the same minute contributes once to a stage and once to each participant's whole-event interval union.

Use this when different organizations or meeting platforms operate parts of the same workshop and you already have permission to process their exports. Prepare a common opaque participant ID before uploading. The Actor does not fetch Zoom or Teams accounts, parse changing native export layouts, infer identity from display names, or require attendee contact details.

### Inputs

Provide an `eventId` and four tables as JSON arrays or mapped CSV:

- `rosterRows`: `rowId`, `participantId`. Preserve identifiers as strings, including leading zeros. Missing or blank identities remain unresolved.
- `stageRows`: `stageId`, `startTime`, `endTime`, `sessionIds`. A stage is a bounded time window with one or more acceptable room/session IDs. JSON uses a room array; CSV uses `a|b` in the `sessionIds` field. Use JSON when a session ID contains a pipe.
- `coverageRows`: `sessionId`, `coverage`, which is `complete`, `partial` or `not-provided`. This is your declaration about the submitted export, not a verified claim about a platform's reporting system.
- `intervalRows`: `rowId`, `sessionId`, `participantId`, `joinTime`, `leaveTime`.

Each timestamp must include a valid calendar date, seconds, and `Z` or an explicit offset such as `+03:00`. Up to three fractional second digits are accepted. End times must follow start times. Explicit offsets handle daylight-saving boundaries as actual instants; zone names and ambiguous local times are rejected. Intervals are half-open: touching endpoints do not constitute a simultaneous-room observation.

For CSV, remove the corresponding array and supply a nonempty `rosterCsv`, `stagesCsv`, `coverageCsv` or `intervalsCsv`. Default headers are the field names above. Optional complete column mappings allow different headers. BOMs and quoted fields are supported; duplicate headers, unknown columns, reused mappings and irregular rows are rejected. Header-only CSV deliberately supplies an empty table.

### Reading the report

Each roster-stage row retains indexed source references and reports `observedUnionMilliseconds`, exact decimal `observedUnionSeconds`, and an exact `observedMinutesFraction` expressed as milliseconds divided by 60000. For example, 900000/60000 is fifteen observed minutes. `mergedIntervals` contains UTC epoch-millisecond endpoints after clipping and union. `participants` contains whole-event unions, which also avoid double counting when stage windows overlap.

Observed intervals in any acceptable breakout room support that stage. Overlapping observations in different rooms produce `simultaneous_room_observation` evidence; this is not a misconduct or engagement conclusion. Duplicate row IDs, duplicate roster identities, duplicated stage IDs, overlapping stage windows and repeated coverage declarations remain ambiguous with all source rows retained. Arithmetic observed durations remain available; `resolvedUnionMilliseconds` and the corresponding whole-event field are null where ambiguity or missing evidence prevents a resolved metric.

A stage with no observations receives `no_observation_in_submitted_complete_exports` only when every allowed session has one complete coverage declaration. Partial, missing or not-provided exports produce `missing_evidence_unknown`. Neither result establishes actual absence. Orphan participants, missing identities, sessions outside the manifest and intervals outside declared windows stay in the unassigned evidence table. They are never assigned by a time or name guess.

### Downloads, pricing and limits

One completed run delivers one complete dataset item, full JSON (`OUTPUT`), participant-stage CSV, findings CSV and a standalone escaped HTML report. Download the HTML attachment and open it locally. CSV cells are neutralized against spreadsheet formulas.

Price: $0.25 per completed report, with platform usage included and no startup or dataset-item fee. Invalid input, insufficient remaining report budget and output-capacity failures do not charge a report event. A fresh run of the same input is a new report. Resuming the same run repairs missing exports without another item or charge; modified saved results or changed input are rejected. Partial export files can exist after an interruption, so wait for successful completion before using them.

Maximum input is 4 MB JSON and 2000 combined table rows, with 2000 stage-session assignments and 10000 roster-stage pairs. Expanded analysis is bounded to 40000 evidence references and 20000 findings; highly duplicated manifests can require smaller batches. Full JSON must remain below 8 MB and every export below 9 MB, checked before billing.

Inputs and reports are stored in your Apify run storage. Use authorized opaque IDs, not names, email addresses or other contact details. This Actor reports supplied-data evidence only. It does not issue certificates, apply thresholds, decide attendance, evaluate learning, determine payroll, verify regulated qualifications or claim a live platform state.

# Actor input Schema

## `eventId` (type: `string`):

Explicit opaque event identifier. IDs remain exact case/whitespace-sensitive strings including leading zeros; max160characters. No platform lookup.

## `rosterRows` (type: `array`):

rowId,participantId. Opaque caller-normalized identity; blank/null identity remains unresolved, never grouped. No names/email or inference. Supply array OR nonempty corresponding CSV exclusively. Max4MB JSON/2000combinedtable rows,2000sessionassignments,10000roster-stagepairs. Indexed references preserve duplicates.

## `rosterCsv` (type: `string`):

Remove rosterRows when using nonemptyCSV. BOM/quotedfields accepted; exactly unique mappedheaders, no unknowncolumns, irregularrows rejected. Header-only explicitly empty. rowId,participantId. Opaque caller-normalized identity; blank/null identity remains unresolved, never grouped. No names/email or inference.

## `rosterColumns` (type: `object`):

Complete distinct field-to-header mapping. Canonical mapping works with arrays; custom mapping requires CSV. No inferred native Zoom/Teams layouts.

## `stageRows` (type: `array`):

stageId,startTime,endTime,sessionIds. Explicit valid offset/Z instants, seconds required, milliseconds optional. sessionIds is an array of allowed exact room IDs; CSV uses pipe-separated room IDs (no pipe escaping, use JSON for IDs containing |). Nonempty distinct allowed sessions. Overlapping stage windows remain ambiguous. Supply array OR nonempty corresponding CSV exclusively. Max4MB JSON/2000combinedtable rows,2000sessionassignments,10000roster-stagepairs. Indexed references preserve duplicates.

## `stagesCsv` (type: `string`):

Remove stageRows when using nonemptyCSV. BOM/quotedfields accepted; exactly unique mappedheaders, no unknowncolumns, irregularrows rejected. Header-only explicitly empty. stageId,startTime,endTime,sessionIds. Explicit valid offset/Z instants, seconds required, milliseconds optional. sessionIds is an array of allowed exact room IDs; CSV uses pipe-separated room IDs (no pipe escaping, use JSON for IDs containing |). Nonempty distinct allowed sessions. Overlapping stage windows remain ambiguous.

## `stageColumns` (type: `object`):

Complete distinct field-to-header mapping. Canonical mapping works with arrays; custom mapping requires CSV. No inferred native Zoom/Teams layouts.

## `coverageRows` (type: `array`):

sessionId,coverage: complete|partial|not-provided. Complete is your supplied-export declaration, not proof of absence or live platform completeness. All allowed sessions require unique complete declarations for no-observation status. Supply array OR nonempty corresponding CSV exclusively. Max4MB JSON/2000combinedtable rows,2000sessionassignments,10000roster-stagepairs. Indexed references preserve duplicates.

## `coverageCsv` (type: `string`):

Remove coverageRows when using nonemptyCSV. BOM/quotedfields accepted; exactly unique mappedheaders, no unknowncolumns, irregularrows rejected. Header-only explicitly empty. sessionId,coverage: complete|partial|not-provided. Complete is your supplied-export declaration, not proof of absence or live platform completeness. All allowed sessions require unique complete declarations for no-observation status.

## `coverageColumns` (type: `object`):

Complete distinct field-to-header mapping. Canonical mapping works with arrays; custom mapping requires CSV. No inferred native Zoom/Teams layouts.

## `intervalRows` (type: `array`):

rowId,sessionId,participantId,joinTime,leaveTime. Opaque pre-normalized identities only; explicit offset/Z timestamps, seconds required, <=3fractionaldigits. End strictly after start. Duplicate rowIds preserved with ambiguity; no display-name matching. Supply array OR nonempty corresponding CSV exclusively. Max4MB JSON/2000combinedtable rows,2000sessionassignments,10000roster-stagepairs. Indexed references preserve duplicates.

## `intervalsCsv` (type: `string`):

Remove intervalRows when using nonemptyCSV. BOM/quotedfields accepted; exactly unique mappedheaders, no unknowncolumns, irregularrows rejected. Header-only explicitly empty. rowId,sessionId,participantId,joinTime,leaveTime. Opaque pre-normalized identities only; explicit offset/Z timestamps, seconds required, <=3fractionaldigits. End strictly after start. Duplicate rowIds preserved with ambiguity; no display-name matching.

## `intervalColumns` (type: `object`):

Complete distinct field-to-header mapping. Canonical mapping works with arrays; custom mapping requires CSV. No inferred native Zoom/Teams layouts.

## Actor input object example

```json
{
  "eventId": "workshop-001",
  "rosterRows": [
    {
      "rowId": "roster-1",
      "participantId": "001"
    },
    {
      "rowId": "roster-2",
      "participantId": "002"
    },
    {
      "rowId": "roster-3",
      "participantId": "003"
    }
  ],
  "rosterColumns": {
    "rowId": "rowId",
    "participantId": "participantId"
  },
  "stageRows": [
    {
      "stageId": "opening",
      "startTime": "2026-10-01T09:00:00Z",
      "endTime": "2026-10-01T10:00:00Z",
      "sessionIds": [
        "teams-opening"
      ]
    },
    {
      "stageId": "breakout",
      "startTime": "2026-10-01T10:00:00Z",
      "endTime": "2026-10-01T12:00:00Z",
      "sessionIds": [
        "zoom-A",
        "zoom-B"
      ]
    },
    {
      "stageId": "closing",
      "startTime": "2026-10-01T12:00:00Z",
      "endTime": "2026-10-01T13:00:00Z",
      "sessionIds": [
        "teams-closing"
      ]
    }
  ],
  "stageColumns": {
    "stageId": "stageId",
    "startTime": "startTime",
    "endTime": "endTime",
    "sessionIds": "sessionIds"
  },
  "coverageRows": [
    {
      "sessionId": "teams-opening",
      "coverage": "complete"
    },
    {
      "sessionId": "zoom-A",
      "coverage": "complete"
    },
    {
      "sessionId": "zoom-B",
      "coverage": "partial"
    },
    {
      "sessionId": "teams-closing",
      "coverage": "not-provided"
    }
  ],
  "coverageColumns": {
    "sessionId": "sessionId",
    "coverage": "coverage"
  },
  "intervalRows": [
    {
      "rowId": "o1",
      "sessionId": "teams-opening",
      "participantId": "001",
      "joinTime": "2026-10-01T08:55:00Z",
      "leaveTime": "2026-10-01T09:30:00Z"
    },
    {
      "rowId": "o2",
      "sessionId": "teams-opening",
      "participantId": "001",
      "joinTime": "2026-10-01T09:20:00Z",
      "leaveTime": "2026-10-01T09:50:00Z"
    },
    {
      "rowId": "b1",
      "sessionId": "zoom-A",
      "participantId": "001",
      "joinTime": "2026-10-01T10:00:00Z",
      "leaveTime": "2026-10-01T11:00:00Z"
    },
    {
      "rowId": "b2",
      "sessionId": "zoom-B",
      "participantId": "001",
      "joinTime": "2026-10-01T10:30:00Z",
      "leaveTime": "2026-10-01T11:30:00Z"
    },
    {
      "rowId": "c1",
      "sessionId": "teams-closing",
      "participantId": "001",
      "joinTime": "2026-10-01T12:55:00Z",
      "leaveTime": "2026-10-01T13:05:00Z"
    },
    {
      "rowId": "b3",
      "sessionId": "zoom-A",
      "participantId": "002",
      "joinTime": "2026-10-01T10:00:00Z",
      "leaveTime": "2026-10-01T10:30:00Z"
    },
    {
      "rowId": "b4",
      "sessionId": "zoom-B",
      "participantId": "002",
      "joinTime": "2026-10-01T10:10:00Z",
      "leaveTime": "2026-10-01T10:40:00Z"
    },
    {
      "rowId": "orphan",
      "sessionId": "zoom-A",
      "participantId": "unknown",
      "joinTime": "2026-10-01T10:00:00Z",
      "leaveTime": "2026-10-01T10:30:00Z"
    },
    {
      "rowId": "missing",
      "sessionId": "zoom-A",
      "participantId": "",
      "joinTime": "2026-10-01T10:00:00Z",
      "leaveTime": "2026-10-01T10:30:00Z"
    },
    {
      "rowId": "unknown-session",
      "sessionId": "other-room",
      "participantId": "001",
      "joinTime": "2026-10-01T10:00:00Z",
      "leaveTime": "2026-10-01T10:30:00Z"
    },
    {
      "rowId": "outside",
      "sessionId": "teams-opening",
      "participantId": "001",
      "joinTime": "2026-10-01T14:00:00Z",
      "leaveTime": "2026-10-01T14:30:00Z"
    }
  ],
  "intervalColumns": {
    "rowId": "rowId",
    "sessionId": "sessionId",
    "participantId": "participantId",
    "joinTime": "joinTime",
    "leaveTime": "leaveTime"
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

One complete report, participant-stage rows, whole-event unions, findings and all supplied source rows.

## `json` (type: `string`):

No description

## `evidence` (type: `string`):

No description

## `findings` (type: `string`):

No description

## `html` (type: `string`):

Download and open the escaped standalone report locally.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "eventId": "workshop-001",
    "rosterRows": [
        {
            "rowId": "roster-1",
            "participantId": "001"
        },
        {
            "rowId": "roster-2",
            "participantId": "002"
        },
        {
            "rowId": "roster-3",
            "participantId": "003"
        }
    ],
    "rosterCsv": "",
    "rosterColumns": {
        "rowId": "rowId",
        "participantId": "participantId"
    },
    "stageRows": [
        {
            "stageId": "opening",
            "startTime": "2026-10-01T09:00:00Z",
            "endTime": "2026-10-01T10:00:00Z",
            "sessionIds": [
                "teams-opening"
            ]
        },
        {
            "stageId": "breakout",
            "startTime": "2026-10-01T10:00:00Z",
            "endTime": "2026-10-01T12:00:00Z",
            "sessionIds": [
                "zoom-A",
                "zoom-B"
            ]
        },
        {
            "stageId": "closing",
            "startTime": "2026-10-01T12:00:00Z",
            "endTime": "2026-10-01T13:00:00Z",
            "sessionIds": [
                "teams-closing"
            ]
        }
    ],
    "stagesCsv": "",
    "stageColumns": {
        "stageId": "stageId",
        "startTime": "startTime",
        "endTime": "endTime",
        "sessionIds": "sessionIds"
    },
    "coverageRows": [
        {
            "sessionId": "teams-opening",
            "coverage": "complete"
        },
        {
            "sessionId": "zoom-A",
            "coverage": "complete"
        },
        {
            "sessionId": "zoom-B",
            "coverage": "partial"
        },
        {
            "sessionId": "teams-closing",
            "coverage": "not-provided"
        }
    ],
    "coverageCsv": "",
    "coverageColumns": {
        "sessionId": "sessionId",
        "coverage": "coverage"
    },
    "intervalRows": [
        {
            "rowId": "o1",
            "sessionId": "teams-opening",
            "participantId": "001",
            "joinTime": "2026-10-01T08:55:00Z",
            "leaveTime": "2026-10-01T09:30:00Z"
        },
        {
            "rowId": "o2",
            "sessionId": "teams-opening",
            "participantId": "001",
            "joinTime": "2026-10-01T09:20:00Z",
            "leaveTime": "2026-10-01T09:50:00Z"
        },
        {
            "rowId": "b1",
            "sessionId": "zoom-A",
            "participantId": "001",
            "joinTime": "2026-10-01T10:00:00Z",
            "leaveTime": "2026-10-01T11:00:00Z"
        },
        {
            "rowId": "b2",
            "sessionId": "zoom-B",
            "participantId": "001",
            "joinTime": "2026-10-01T10:30:00Z",
            "leaveTime": "2026-10-01T11:30:00Z"
        },
        {
            "rowId": "c1",
            "sessionId": "teams-closing",
            "participantId": "001",
            "joinTime": "2026-10-01T12:55:00Z",
            "leaveTime": "2026-10-01T13:05:00Z"
        },
        {
            "rowId": "b3",
            "sessionId": "zoom-A",
            "participantId": "002",
            "joinTime": "2026-10-01T10:00:00Z",
            "leaveTime": "2026-10-01T10:30:00Z"
        },
        {
            "rowId": "b4",
            "sessionId": "zoom-B",
            "participantId": "002",
            "joinTime": "2026-10-01T10:10:00Z",
            "leaveTime": "2026-10-01T10:40:00Z"
        },
        {
            "rowId": "orphan",
            "sessionId": "zoom-A",
            "participantId": "unknown",
            "joinTime": "2026-10-01T10:00:00Z",
            "leaveTime": "2026-10-01T10:30:00Z"
        },
        {
            "rowId": "missing",
            "sessionId": "zoom-A",
            "participantId": "",
            "joinTime": "2026-10-01T10:00:00Z",
            "leaveTime": "2026-10-01T10:30:00Z"
        },
        {
            "rowId": "unknown-session",
            "sessionId": "other-room",
            "participantId": "001",
            "joinTime": "2026-10-01T10:00:00Z",
            "leaveTime": "2026-10-01T10:30:00Z"
        },
        {
            "rowId": "outside",
            "sessionId": "teams-opening",
            "participantId": "001",
            "joinTime": "2026-10-01T14:00:00Z",
            "leaveTime": "2026-10-01T14:30:00Z"
        }
    ],
    "intervalsCsv": "",
    "intervalColumns": {
        "rowId": "rowId",
        "sessionId": "sessionId",
        "participantId": "participantId",
        "joinTime": "joinTime",
        "leaveTime": "leaveTime"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("h_murdock/event-stage-participation-evidence").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "eventId": "workshop-001",
    "rosterRows": [
        {
            "rowId": "roster-1",
            "participantId": "001",
        },
        {
            "rowId": "roster-2",
            "participantId": "002",
        },
        {
            "rowId": "roster-3",
            "participantId": "003",
        },
    ],
    "rosterCsv": "",
    "rosterColumns": {
        "rowId": "rowId",
        "participantId": "participantId",
    },
    "stageRows": [
        {
            "stageId": "opening",
            "startTime": "2026-10-01T09:00:00Z",
            "endTime": "2026-10-01T10:00:00Z",
            "sessionIds": ["teams-opening"],
        },
        {
            "stageId": "breakout",
            "startTime": "2026-10-01T10:00:00Z",
            "endTime": "2026-10-01T12:00:00Z",
            "sessionIds": [
                "zoom-A",
                "zoom-B",
            ],
        },
        {
            "stageId": "closing",
            "startTime": "2026-10-01T12:00:00Z",
            "endTime": "2026-10-01T13:00:00Z",
            "sessionIds": ["teams-closing"],
        },
    ],
    "stagesCsv": "",
    "stageColumns": {
        "stageId": "stageId",
        "startTime": "startTime",
        "endTime": "endTime",
        "sessionIds": "sessionIds",
    },
    "coverageRows": [
        {
            "sessionId": "teams-opening",
            "coverage": "complete",
        },
        {
            "sessionId": "zoom-A",
            "coverage": "complete",
        },
        {
            "sessionId": "zoom-B",
            "coverage": "partial",
        },
        {
            "sessionId": "teams-closing",
            "coverage": "not-provided",
        },
    ],
    "coverageCsv": "",
    "coverageColumns": {
        "sessionId": "sessionId",
        "coverage": "coverage",
    },
    "intervalRows": [
        {
            "rowId": "o1",
            "sessionId": "teams-opening",
            "participantId": "001",
            "joinTime": "2026-10-01T08:55:00Z",
            "leaveTime": "2026-10-01T09:30:00Z",
        },
        {
            "rowId": "o2",
            "sessionId": "teams-opening",
            "participantId": "001",
            "joinTime": "2026-10-01T09:20:00Z",
            "leaveTime": "2026-10-01T09:50:00Z",
        },
        {
            "rowId": "b1",
            "sessionId": "zoom-A",
            "participantId": "001",
            "joinTime": "2026-10-01T10:00:00Z",
            "leaveTime": "2026-10-01T11:00:00Z",
        },
        {
            "rowId": "b2",
            "sessionId": "zoom-B",
            "participantId": "001",
            "joinTime": "2026-10-01T10:30:00Z",
            "leaveTime": "2026-10-01T11:30:00Z",
        },
        {
            "rowId": "c1",
            "sessionId": "teams-closing",
            "participantId": "001",
            "joinTime": "2026-10-01T12:55:00Z",
            "leaveTime": "2026-10-01T13:05:00Z",
        },
        {
            "rowId": "b3",
            "sessionId": "zoom-A",
            "participantId": "002",
            "joinTime": "2026-10-01T10:00:00Z",
            "leaveTime": "2026-10-01T10:30:00Z",
        },
        {
            "rowId": "b4",
            "sessionId": "zoom-B",
            "participantId": "002",
            "joinTime": "2026-10-01T10:10:00Z",
            "leaveTime": "2026-10-01T10:40:00Z",
        },
        {
            "rowId": "orphan",
            "sessionId": "zoom-A",
            "participantId": "unknown",
            "joinTime": "2026-10-01T10:00:00Z",
            "leaveTime": "2026-10-01T10:30:00Z",
        },
        {
            "rowId": "missing",
            "sessionId": "zoom-A",
            "participantId": "",
            "joinTime": "2026-10-01T10:00:00Z",
            "leaveTime": "2026-10-01T10:30:00Z",
        },
        {
            "rowId": "unknown-session",
            "sessionId": "other-room",
            "participantId": "001",
            "joinTime": "2026-10-01T10:00:00Z",
            "leaveTime": "2026-10-01T10:30:00Z",
        },
        {
            "rowId": "outside",
            "sessionId": "teams-opening",
            "participantId": "001",
            "joinTime": "2026-10-01T14:00:00Z",
            "leaveTime": "2026-10-01T14:30:00Z",
        },
    ],
    "intervalsCsv": "",
    "intervalColumns": {
        "rowId": "rowId",
        "sessionId": "sessionId",
        "participantId": "participantId",
        "joinTime": "joinTime",
        "leaveTime": "leaveTime",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("h_murdock/event-stage-participation-evidence").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "eventId": "workshop-001",
  "rosterRows": [
    {
      "rowId": "roster-1",
      "participantId": "001"
    },
    {
      "rowId": "roster-2",
      "participantId": "002"
    },
    {
      "rowId": "roster-3",
      "participantId": "003"
    }
  ],
  "rosterCsv": "",
  "rosterColumns": {
    "rowId": "rowId",
    "participantId": "participantId"
  },
  "stageRows": [
    {
      "stageId": "opening",
      "startTime": "2026-10-01T09:00:00Z",
      "endTime": "2026-10-01T10:00:00Z",
      "sessionIds": [
        "teams-opening"
      ]
    },
    {
      "stageId": "breakout",
      "startTime": "2026-10-01T10:00:00Z",
      "endTime": "2026-10-01T12:00:00Z",
      "sessionIds": [
        "zoom-A",
        "zoom-B"
      ]
    },
    {
      "stageId": "closing",
      "startTime": "2026-10-01T12:00:00Z",
      "endTime": "2026-10-01T13:00:00Z",
      "sessionIds": [
        "teams-closing"
      ]
    }
  ],
  "stagesCsv": "",
  "stageColumns": {
    "stageId": "stageId",
    "startTime": "startTime",
    "endTime": "endTime",
    "sessionIds": "sessionIds"
  },
  "coverageRows": [
    {
      "sessionId": "teams-opening",
      "coverage": "complete"
    },
    {
      "sessionId": "zoom-A",
      "coverage": "complete"
    },
    {
      "sessionId": "zoom-B",
      "coverage": "partial"
    },
    {
      "sessionId": "teams-closing",
      "coverage": "not-provided"
    }
  ],
  "coverageCsv": "",
  "coverageColumns": {
    "sessionId": "sessionId",
    "coverage": "coverage"
  },
  "intervalRows": [
    {
      "rowId": "o1",
      "sessionId": "teams-opening",
      "participantId": "001",
      "joinTime": "2026-10-01T08:55:00Z",
      "leaveTime": "2026-10-01T09:30:00Z"
    },
    {
      "rowId": "o2",
      "sessionId": "teams-opening",
      "participantId": "001",
      "joinTime": "2026-10-01T09:20:00Z",
      "leaveTime": "2026-10-01T09:50:00Z"
    },
    {
      "rowId": "b1",
      "sessionId": "zoom-A",
      "participantId": "001",
      "joinTime": "2026-10-01T10:00:00Z",
      "leaveTime": "2026-10-01T11:00:00Z"
    },
    {
      "rowId": "b2",
      "sessionId": "zoom-B",
      "participantId": "001",
      "joinTime": "2026-10-01T10:30:00Z",
      "leaveTime": "2026-10-01T11:30:00Z"
    },
    {
      "rowId": "c1",
      "sessionId": "teams-closing",
      "participantId": "001",
      "joinTime": "2026-10-01T12:55:00Z",
      "leaveTime": "2026-10-01T13:05:00Z"
    },
    {
      "rowId": "b3",
      "sessionId": "zoom-A",
      "participantId": "002",
      "joinTime": "2026-10-01T10:00:00Z",
      "leaveTime": "2026-10-01T10:30:00Z"
    },
    {
      "rowId": "b4",
      "sessionId": "zoom-B",
      "participantId": "002",
      "joinTime": "2026-10-01T10:10:00Z",
      "leaveTime": "2026-10-01T10:40:00Z"
    },
    {
      "rowId": "orphan",
      "sessionId": "zoom-A",
      "participantId": "unknown",
      "joinTime": "2026-10-01T10:00:00Z",
      "leaveTime": "2026-10-01T10:30:00Z"
    },
    {
      "rowId": "missing",
      "sessionId": "zoom-A",
      "participantId": "",
      "joinTime": "2026-10-01T10:00:00Z",
      "leaveTime": "2026-10-01T10:30:00Z"
    },
    {
      "rowId": "unknown-session",
      "sessionId": "other-room",
      "participantId": "001",
      "joinTime": "2026-10-01T10:00:00Z",
      "leaveTime": "2026-10-01T10:30:00Z"
    },
    {
      "rowId": "outside",
      "sessionId": "teams-opening",
      "participantId": "001",
      "joinTime": "2026-10-01T14:00:00Z",
      "leaveTime": "2026-10-01T14:30:00Z"
    }
  ],
  "intervalsCsv": "",
  "intervalColumns": {
    "rowId": "rowId",
    "sessionId": "sessionId",
    "participantId": "participantId",
    "joinTime": "joinTime",
    "leaveTime": "leaveTime"
  }
}' |
apify call h_murdock/event-stage-participation-evidence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,h_murdock/event-stage-participation-evidence"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/JxKsfl2bEkeCLnsc4/builds/JRu0Fv1pEIgyQA9xp/openapi.json
