---
title: "VolunteerReminder API v1.0.0"
description: "REST API for VolunteerReminder — automated volunteer shift reminders by
email, SMS, and calendar invite.

Authentication: pass an API key as `Authorization: Bearer vrk_…` on every
endpoint except the public `GET /pricing` and `POST /signups`. Keys are issued
from the account security screen and carry scopes (`read`, `schedules:write`,
`contacts:write`, `reminders:write`, `keys:manage`).

Safety: agent-created accounts start in reminder **test mode** — reminders
reroute to the owner's own address until a human flips live sending on from the
dashboard. Nothing in this API can enable live sends, mutate billing, change the
login email, or delete an account.

Recommended agent path: `POST /signups` → `POST /imports/preview` →
`POST /imports` → `GET /schedules` (or MCP `get_upcoming_shifts`). Import never
changes reminder mode, the confirm loop, or the substitute finder.

Errors are always `{ \"error\": { \"code\", \"message\" } }`; branch on `code`.

Edits that remove shifts ask first: `PATCH /schedules/{id}` answers 409
`confirm_remove_required` (nothing saved) with the `removed` dates and a
`confirm_token`; resend the same edit with `confirm_remove: true` and that token
once the user agrees. Other edits are unchanged."
canonical_url: https://volunteerreminder.com/docs
md_url: https://volunteerreminder.com/docs.md
last_updated: 2026-08-23
---
# VolunteerReminder API v1.0.0

REST API for VolunteerReminder — automated volunteer shift reminders by
email, SMS, and calendar invite.

Authentication: pass an API key as `Authorization: Bearer vrk_…` on every
endpoint except the public `GET /pricing` and `POST /signups`. Keys are issued
from the account security screen and carry scopes (`read`, `schedules:write`,
`contacts:write`, `reminders:write`, `keys:manage`).

Safety: agent-created accounts start in reminder **test mode** — reminders
reroute to the owner's own address until a human flips live sending on from the
dashboard. Nothing in this API can enable live sends, mutate billing, change the
login email, or delete an account.

Recommended agent path: `POST /signups` → `POST /imports/preview` →
`POST /imports` → `GET /schedules` (or MCP `get_upcoming_shifts`). Import never
changes reminder mode, the confirm loop, or the substitute finder.

Errors are always `{ "error": { "code", "message" } }`; branch on `code`.

Edits that remove shifts ask first: `PATCH /schedules/{id}` answers 409
`confirm_remove_required` (nothing saved) with the `removed` dates and a
`confirm_token`; resend the same edit with `confirm_remove: true` and that token
once the user agrees. Other edits are unchanged.

Base URL: `https://volunteerreminder.com`

OpenAPI document: `https://volunteerreminder.com/openapi.json`

## Authentication

Send your API key as `Authorization: Bearer vrk_…` on every endpoint except the ones marked **public** below. A 401 means the key is missing/invalid; a 403 (`insufficient_scope`) means it lacks the scope an endpoint needs.

## Endpoints

### GET /api/v1/account

Get the current account

- Auth: API key required
- Tags: Account

Plan, entitlements, current reminder mode, and usage vs limits for the account the API key belongs to. Requires the `read` scope.

### POST /api/v1/billing/checkout-url

Get a Stripe Checkout URL for the human to complete

- Auth: API key required
- Tags: Billing

Create a Stripe-hosted Checkout session for a named plan/interval and return its URL for the ACCOUNT OWNER to open and pay — the agent never takes payment. Reuses the dashboard's session-creation logic, including the already-subscribed guard and idempotency key. Requires the `read` scope (this endpoint returns a URL and mutates no agent-managed data; the irreversible act, payment, stays with the human — agent-channel §3.4).

### GET /api/v1/contacts

List volunteers

- Auth: API key required
- Tags: Contacts

All non-deleted contacts on this account's roster, ordered by name. Requires `read`.

### POST /api/v1/contacts

Add a volunteer

- Auth: API key required
- Tags: Contacts

Add one contact to the roster. Blocked with `validation_error` when the account is already at its plan's volunteer limit (domain/volunteerCap.ts, the same rule the dashboard enforces). Requires `contacts:write`.

### POST /api/v1/contacts/bulk

Add volunteers in bulk

- Auth: API key required
- Tags: Contacts

Add up to 200 contacts in one call. ALL-OR-NOTHING: if the batch would push the account's active-volunteer count past its plan's limit, or any contact references a `group_id` that doesn't belong to this account, NOTHING is created and a `validation_error` names the limit, the account's current count, or the bad group id. Requires `contacts:write`.

### PATCH /api/v1/contacts/{id}

Update a volunteer

- Auth: API key required
- Tags: Contacts

Partial update — only the fields present in the body are changed. Requires `contacts:write`.

### DELETE /api/v1/contacts/{id}

Remove a volunteer

- Auth: API key required
- Tags: Contacts

Soft-deletes the contact (frees a volunteer-cap slot; removes it from any schedule rosters it was on). Requires `contacts:write`.

### POST /api/v1/imports

Import a roster + schedule

- Auth: API key required
- Tags: Imports

Parse a pasted CSV/table and create contacts, weekly/custom schedules, and roster assignments. Requires preview_token from POST /imports/preview for the same text. Test-mode accounts only; account 3 is refused even in test. Does not change reminder mode, the confirm loop, or the substitute finder. Requires `schedules:write` and `contacts:write`. UTF-16 text is refused with 400 `unsupported_encoding`, as on preview.

### POST /api/v1/imports/preview

Preview a roster + schedule import

- Auth: API key required
- Tags: Imports

PURE parse of a pasted CSV/table. Creates nothing. Returns the contacts, schedules, upcoming shifts, and a preview_token that a subsequent POST /imports must send back. Test-mode accounts only; account 3 is refused even in test. Does not change reminder mode. Requires `read`. Text that is UTF-16 (Excel "Unicode Text") read as UTF-8 or Latin-1 is refused with 400 `unsupported_encoding`; send CSV UTF-8 text.

### GET /api/v1/keys

List API keys

- Auth: API key required
- Tags: Keys

List this account's API keys (including revoked ones, so their status is visible). The secret is NEVER returned. Requires the `keys:manage` scope.

### POST /api/v1/keys

Mint a new API key

- Auth: API key required
- Tags: Keys

Create a new API key for this account. The requested scopes must be a subset of the calling key's scopes (a key can never widen authority). Requires the `keys:manage` scope. The secret is returned exactly once.

### DELETE /api/v1/keys/{id}

Revoke an API key

- Auth: API key required
- Tags: Keys

Soft-revoke one of this account's API keys. Idempotent per key; a revoked key can never authenticate again. Requires the `keys:manage` scope. Returns 404 for an unknown id, an already-revoked key, or another account's key.

### GET /api/v1/open-days

Get unfilled slots (who do we still need?)

- Auth: API key required
- Tags: Reminders

Wraps domain/openDays.ts (the same logic behind the public open-dates embed): calendar days with no attendable shift yet, plus recurring monthly slots not yet claimed by a schedule. Requires the `read` scope.

### GET /api/v1/pricing

Get the public plan catalog

- Auth: public — no API key
- Tags: Pricing

Public, unauthenticated plan/pricing/entitlement catalog. The payload an agent comparison-shopping on a user's behalf reads; no API key required.

### GET /api/v1/reminder-settings

Get the reminder lead times and channel availability

- Auth: API key required
- Tags: Reminders

The account-wide default lead time, any per-job overrides, and which channels (email/SMS) are available on the current plan. Requires the `read` scope.

### PUT /api/v1/reminder-settings

Update the reminder lead times

- Auth: API key required
- Tags: Reminders

Set the account-wide default lead time and/or a per-job override. Requires the `reminders:write` scope. Fails validation (no writes performed) if neither `global_default` nor `job_override` is provided, if a lead time leaves all three fields at zero, if `job_override.job_id` is not a job on this account, or if `channels.require_sms` is true on a plan without SMS.

### DELETE /api/v1/reminder-settings/global-default

Clear the account-wide default reminder

- Auth: API key required
- Tags: Reminders

Soft-delete the account-wide default reminder lead time. Returns the updated reminder configuration. 404 if no active default is set. Requires the `reminders:write` scope.

### DELETE /api/v1/reminder-settings/job-overrides/{id}

Remove a per-job reminder override

- Auth: API key required
- Tags: Reminders

Soft-delete the reminder lead-time override for one job (addressed by JOB id). Returns the updated reminder configuration. 404 if the job has no active override on this account. Requires the `reminders:write` scope.

### POST /api/v1/reminders/test

Send a test reminder to the account owner

- Auth: API key required
- Tags: Reminders

Send ONE sample reminder to the account OWNER's own email so they can preview what a reminder looks like. It NEVER reaches volunteers — it always targets the owner — so it respects the test-mode safety model trivially. Requires the `reminders:write` scope.

### GET /api/v1/schedules

List schedules

- Auth: API key required
- Tags: Schedules

All active (non-archived) schedules for the account. Requires the `read` scope.

### POST /api/v1/schedules

Create a schedule

- Auth: API key required
- Tags: Schedules

Creates a schedule and materializes its upcoming occurrences. Validation mirrors the dashboard's add-schedule form. Requires the `schedules:write` scope.

### POST /api/v1/schedules/preview

Preview upcoming occurrence dates for a recurrence config

- Auth: API key required
- Tags: Schedules

PURE and side-effect-free: no schedule is read or written. Wraps domain/recurrence.ts + domain/timezone.ts to show the next occurrence dates a recurrence configuration would produce, so an agent can confirm with the human before calling POST /schedules. Requires the `read` scope.

### GET /api/v1/schedules/{id}

Get a schedule

- Auth: API key required
- Tags: Schedules

Requires the `read` scope.

### PATCH /api/v1/schedules/{id}

Update a schedule

- Auth: API key required
- Tags: Schedules

Partial update — only send the fields you want to change. Unset fields keep their current value. Recomputes and re-materializes future occurrences when recurrence fields change. An edit that would remove upcoming shifts saves nothing and answers 409 `confirm_remove_required` with the `removed` days and a `confirm_token`; once the user agrees, resend the same edit with `confirm_remove: true` and that `confirm_token`. Does NOT change the roster: use `POST /schedules/{id}/roster` for that. Requires the `schedules:write` scope.

### DELETE /api/v1/schedules/{id}

Archive a schedule

- Auth: API key required
- Tags: Schedules

Soft-deletes (archives) the schedule; past reminder history is preserved. Requires the `schedules:write` scope.

### GET /api/v1/schedules/{id}/occurrences

List upcoming shifts for a schedule

- Auth: API key required
- Tags: Schedules

Occurrences in [from, to] plus the schedule's roster and each date's attendance/exception status. Requires the `read` scope.

### POST /api/v1/schedules/{id}/roster

Replace a schedule's roster

- Auth: API key required
- Tags: Contacts

Assign contacts to a schedule. REPLACES the schedule's whole roster (matches the dashboard's save-the-form semantics) — send the complete desired set of contact ids, not a delta. Every id must belong to this account or the whole request is rejected. Requires `contacts:write`.

### GET /api/v1/send-log

Get recent reminder send outcomes

- Auth: API key required
- Tags: Reminders

Recent reminder outcomes merged from the sent log and the dead-letter failure log, newest first. Requires the `read` scope.

### POST /api/v1/signups

Create an account (pending email verification)

- Auth: public — no API key
- Tags: Signups

Public, unauthenticated. Creates an inert trial account for the supplied email and sends the owner a verification link — no API key is returned. The human must click the link to activate; agent-created accounts start with reminders in the safe test posture and can never reach real volunteers until a human turns live sending on from the dashboard. Idempotent on a repeat email (same response, no second account) and heavily rate-limited.

## Legal

[Privacy Policy](/privacy) · [Terms of Service](/terms)

## Sitemap

See the full [sitemap](https://volunteerreminder.com/sitemap.md) for every public page.
