---
title: "Cancel a signup and tell its participants"
method: POST
path: "/signups/{id}/cancel"
tags: ["Signups"]
---

# Cancel a signup and tell its participants

`POST /signups/{id}/cancel`

## Request

- Base URL: `https://thesignup.app/api/v1`
- URL: `https://thesignup.app/api/v1/signups/{id}/cancel`
- Auth: HTTP bearer

Marks the signup `closed` (leaving `signupCloseDate` as it was), and EMAILS (and, where consented, TEXTS) every attending registration that the signup is cancelled, with the optional `message` included. Immediate; no undo and no dry-run. Honors each person's notification preferences; texts only go to people with an account, a phone and SMS consent. The response is the signup plus `notified`: `recipients` is the audience (attending registrations, including people who opted out of email); `emails` and `sms` are the notices actually sent. Emits the `signup.canceled` webhook. To close WITHOUT telling anyone use `DELETE /signups/{id}`. Safe to retry: a repeat of a cancel that already told people returns the first call's `notified` and sends nothing (no second webhook or activity record). It tells people again only if the earlier close told nobody (a `DELETE`, a status change to closed, or a cancel that had no one attending), if the signup was closed another way since, or if it was reopened in between. A retry with the same `Idempotency-Key` replays the first response verbatim. The body is optional; one that is not a JSON object is refused with 400. The account that owns the credential must have a verified email address (returns 403 otherwise), as for publish.

## Path parameters

- `id` string, uuid, required

## Request body

- CancelSignupRequest
  - `message` string: Participant-facing: the organizer's note, included in the cancellation EMAIL sent to every attending registration, and in the cancellation TEXT for those with an account who have texts on, a phone, and SMS consent. Omit it to send the standard cancellation notice with no note. Never private; do not put anything here you would not tell the participants.

## Response `200`

The cancelled signup and who was told

- CancelSignupResponse
  - `id` string, uuid, required
  - `organizationId` string, required
  - `slug` string, required
  - `title` string, required
  - `status` 'draft' | 'published' | 'closed', required
  - `eventDate` string, date-time, required: ISO-8601 UTC.
  - `signupCloseDate` string, date-time, nullable, required: ISO-8601 UTC. Null = signups never close until the event.
  - `location` string, nullable, required
  - `isPrivate` boolean, required
  - `createdAt` string, date-time, required
  - `updatedAt` string, date-time, required
  - `description` string, nullable, required
  - `maxParticipants` integer, nullable, required: Cap on total participants. Null only on a plan with no participant ceiling (uncapped); elsewhere the cap is stored as the plan ceiling.
  - `commitmentRequired` boolean, required: When true, a participant must claim a slot or an item to sign up — attending without committing to anything is not offered.
  - `requiresPhone` boolean, required
  - `timezone` string, required: IANA timezone the organizer attached to this signup. Display surfaces should prefer this over the viewer's local zone when rendering eventDate / slot times.
  - `eventWideCustomField` object, nullable, required: Optional one-off field shown on every signup form.
    - `name` string, required
    - `required` boolean, required
    - `type` 'short' | 'long', required
  - `days` Day[], required: Every day of the signup in date order, cancelled ones included. Same shape as `GET /signups/{id}/days`.
    - `id` string, uuid, required
    - `eventId` string, uuid, required
    - `date` string, required: The calendar date, `YYYY-MM-DD`, in the signup's timezone.
    - `seriesId` string, uuid, nullable, required: The Slot Series that generated this day, or null for a day added by hand or an ordinary signup's day.
    - `cancelledAt` string, date-time, nullable, required: When the day was called off, or null. A cancelled day keeps its slots, items and registrations. A repeating schedule's date is cancelled through its Shifts instead and keeps null here; read `noLiveTime`.
    - `noLiveTime` boolean, required: True when the day had times and none of them is live any more — participants are not offered it, exactly as a cancelled day.
    - `question` object, nullable, required: The Day Question asked once per day of whoever signs up for anything that day: the day's own, else the repeating schedule's default. Null when none.
      - `label` string, required
      - `placeholder` string: Example answer, shown as a hint in the answer box. Absent when none was given.
      - `required` boolean, required
    - `questionSource` 'own' | 'series' | 'null', nullable, required: Whose `question` it is: the day's own, or the schedule's default it inherits. Null when there is no question.
    - `isPast` boolean, required: Whether the date is before today in the signup's timezone.
  - `notified` CancelNotified, required
    - `recipients` integer, required: The audience: distinct registrations attending when the signup was cancelled, including people who opted out of the email. Not a send count; `emails` and `sms` are.
    - `emails` integer, required: Cancellation emails the mail transport accepted.
    - `sms` integer, required: Cancellation texts Twilio accepted.

## Other responses

- `400`: Bad request — body or path could not be parsed
- `401`: Unauthenticated — credentials missing, malformed, invalid, or revoked
- `403`: Forbidden — credentials valid but lack the required scope
- `404`: Not found
- `422`: Validation failed — request body / query / path is structurally wrong
- `429`: Rate limited or daily ceiling exceeded
- `500`: Internal server error

---

[API](https://skmtc.dev/thesignup/apis/thesignup-api.md) · [All operations](https://skmtc.dev/thesignup/apis/thesignup-api/llms.txt) · [OpenAPI document](https://skmtc.dev/thesignup/apis/thesignup-api/revisions/c6da7cf92172?raw)
