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

# WaiverFile plugin

> Electronic waivers and booking with WaiverFile - read signed waivers and their PDFs, search and page through waiver data, manage events and event categories, list waiver forms, and subscribe URLs to the triggers WaiverFile posts to.

Use **WaiverFile** through Corsair: one client, typed API calls.

Create the key in the WaiverFile admin under **Settings > API > API keys**; the same page shows the site ID. Every call needs that key **and the site ID it belongs to**, so store the pair in the key slot as `<apiKey>\n<siteID>` — the plugin splits it and reports a configuration error before any request if it is malformed.

* **Credentials travel in the query string, not a header.** Every route takes `apiKey` and `siteID` as query parameters; there is no bearer token. The plugin adds both to every call, so a bare key is rejected up front rather than failing with an obscure 404.
* **`InsertEvent` is the exception.** It carries the credentials in the **body** as `APIKey` and `SiteID`, which the plugin fills in from the same packed key. The query still holds them, but the body's copy is what the route reads.
* **A bad key is usually a 400.** Some malformed keys get a plain 401, but WaiverFile normally answers `{"Message":"Invalid"}` (or `InvalidService…` from `Ping`) with HTTP 400 for a wrong key or a site the key does not belong to — the same status as a genuine validation failure. The plugin reads the message to tell them apart, so a credential problem is reported as an auth error and a real 400 stays a validation error.
* **Errors have no code.** The body is `{"Message": "..."}` with a capital M. There is nothing else to branch on, which is why the handlers rely on the status plus that one message.
* **A missing route is also a 404.** Because everything is addressed by query parameter, a 404 means the *path* is wrong rather than the object missing — a misspelled endpoint reads exactly like a deleted waiver.
* **`pageIndex` conventions differ.** The waiver lists (`waivers.list`, `waivers.listLite`) are **zero-based** and cap `pageSize` at 500. `waivers.data` is **one-based** and allows up to 1000. Getting this wrong silently returns the wrong page, so each schema documents and enforces its own.
* **`consolidateParticipants` is reserved.** The API accepts it and ignores it; rows still come back one per participant.
* **Two deletes are not `DELETE`.** `events.delete` is a `GET /DeleteEvent` and `eventCategories.delete` is a `POST /DeleteEventCategory` — so neither is safe to replay on a timeout, despite one looking like a read.
* **Three serialisations, one shape.** Events, forms, categories and the site come back as serialised .NET objects (lowerCamelCase columns under `me`); the waiver reads come back as XML converted to JSON, with every value a string (`"true"`, `"0"`) and a single item sent as an object instead of an array; `Ping`, the counts and the webhook routes answer bare values. The plugin flattens all of them into the PascalCase names the reference uses (`WaiverID`, `WaiverEventID`, `Name`) and always returns lists as arrays. Participants on the waiver reads keep WaiverFile's spelling `Particpants`; `events.waivers` uses standard JSON, so there they are `Participants` and booleans are real booleans.
* **`events.inviteManagers` always sends both optional parameters.** The route 404s unless `managerEmailMessage` and `skipSendingEmailIfAccountExists` are present, so the plugin sends an empty message and `false` when they are omitted. With `false`, every address is emailed.
* **There is no single-event read.** The v1 API has no `GetEvent` route; read an event through `events.byDateRange`, `events.byCategory` or the answer of the write you just made.
* **`events.update` replaces the attached forms.** Omitting `waiverFormIDs`, or sending an empty list, attaches every active form; pass the event's current form IDs to keep them. Send the empty GUID as `eventCategoryID` for no category.
* **The webhook triggers are path segments.** Subscribing is `POST /subscribe/<trigger>?targetUrl=…` and unsubscribing is `DELETE /deletesubscribe/<trigger>` with the same URL, one route per trigger. The twelve triggers are `newwaiver`, `newevent`, `newcheckin`, `newqrscan`, `newbookingcheckin`, `bookingcheckinundone`, `bookingcheckedin`, `newmembercheckin`, `membercheckinundone`, `editwaiver`, `editevent` and `editcheckin`. There is no signing secret, so the receiving host verifies by its own means.
* **A waiver PDF is a file, not JSON.** `waivers.pdf` downloads the bytes directly and returns them base64-encoded in `content`, with the content type and the real byte length.
* **A waiver holds personal data.** Date of birth, email and phone are on the record, so the plugin never writes them to the event log. It does log identifiers (waiver, event and category IDs), counts, byte counts and flags, the site, event and category names, and a webhook target's origin and path (never its query string). Error messages have the API key removed, because WaiverFile echoes the request URL in some error bodies.

**What you get:**

* 31 typed API operations

## Setup

**1. Install**

```bash npm theme={null}
npm install corsair @corsair-dev/waiverfile better-sqlite3
npm install --save-dev @types/better-sqlite3
```

```bash yarn theme={null}
yarn add corsair @corsair-dev/waiverfile better-sqlite3
yarn add --dev @types/better-sqlite3
```

```bash pnpm theme={null}
pnpm install corsair @corsair-dev/waiverfile better-sqlite3
pnpm install --save-dev @types/better-sqlite3
```

```bash bun theme={null}
bun add corsair @corsair-dev/waiverfile better-sqlite3
bun add --dev @types/better-sqlite3
```

**2. Add the plugin**

```ts corsair.ts theme={null}
import Database from 'better-sqlite3';
import { createCorsair } from 'corsair';
import { waiverfile } from '@corsair-dev/waiverfile';

export const corsair = createCorsair({
	plugins: [
		waiverfile(),
	],
	database: new Database('corsair.db'),
	kek: process.env.CORSAIR_KEK!,
	multiTenancy: true,
	hub: {
		projectApiKey: process.env.CORSAIR_API_KEY!,
		signingSecret: process.env.CORSAIR_SIGNING_SECRET!,
	},
});
```

With `multiTenancy: true`, scope calls with `corsair.withTenant(id)`. See [Quick start](https://docs.corsair.dev/quick-start.md) for KEK + Hub keys, and [Multi-tenancy](https://docs.corsair.dev/concepts/multi-tenancy.md) for account isolation.

**3. Choose authentication**

> [tab] API Key (Recommended)
>
> No setup required yet. When you make your first request as a tenant, Corsair prompts for the API key.
>
> ```ts theme={null}
> waiverfile()
> ```
>
> More: [API Key](https://docs.corsair.dev/concepts/api-key)

**4. Connect a tenant**

Mint a connect link and send the tenant to it. Hub hosts the page and delivers the result to your app — see [Connect / OAuth](https://docs.corsair.dev/management/connect).

```ts theme={null}
const { connectUrl } = await corsair.manage.connect.createLink({
	plugin: 'waiverfile',
	tenantId: 'acme',
});
// redirect the user's browser to connectUrl
```

## Example API calls

**Read a signed waiver**

```ts theme={null}
const tenant = corsair.withTenant('acme');
await tenant.waiverfile.api.waivers.get({ waiverID: '4e5c5176-1238-45a3-8a87-35435baecaa2' });
```

**Create an event**

```ts theme={null}
const tenant = corsair.withTenant('acme');
await tenant.waiverfile.api.events.create({
	eventName: 'Saturday open jump',
	dateStart: '2024-06-02T09:00:00Z',
	dateEnd: '2024-06-02T17:00:00Z',
	isAllDay: false,
});
```

See the full list on the [API](https://docs.corsair.dev/plugins/waiverfile/api.md) page.

## What's next

> [card] [API reference](https://docs.corsair.dev/plugins/waiverfile/api.md)
>
> Every `waiverfile.api.*` operation with input and output types.

> [card] [Connect / OAuth](https://docs.corsair.dev/management/connect)
>
> createLink, Hub delivery, and tenant connect flows.

> [card] [Use with agents](https://docs.corsair.dev/mcp-adapters/mcp-adapters)
>
> Expose this plugin's operations as MCP tools.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.