---
title: "Update a signup"
method: PATCH
path: "/signups/{id}"
tags: ["Signups"]
---

# Update a signup

`PATCH /signups/{id}`

## Request

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

Partial update — only the supplied fields are changed; at least one is required (an empty body is 422). Null clears `description`, `location`, `signupCloseDate` and `eventWideCustomField`; it is a no-op for `timezone` and resets `maxParticipants` to the plan ceiling. Notifies no participants. Emits `signup.updated`.

## Path parameters

- `id` string, uuid, required

## Request body

- UpdateSignupRequest
  - `title` string: Cannot be null: a signup always has a title.
  - `description` string, nullable: Null clears the description.
  - `location` string, nullable: Null clears the location.
  - `eventDate` string: ISO-8601 datetime, same rules as on create: UTC / offset-bearing, or a naive local string that is read in `timezone` (this request's, else the signup's stored one). Refused with 400 on a recurring (Series) signup unless it is the signup's current date: a recurring signup's dates come from its schedule. A multi-day signup built from days alone is not locked. A naive string is read in the signup's stored zone when `timezone` is omitted; it is refused (422) if `timezone: null` is sent beside it.
  - `signupCloseDate` string, nullable: ISO-8601 datetime, same rules as `eventDate`. Null = signups stay open until the event.
  - `timezone` string, nullable: IANA timezone name. Changing it does NOT move the signup's stored instants (`eventDate`, slot times): it changes how they are displayed and how naive datetimes are read. Null is a no-op (a signup always has a timezone), not a way to clear it.
  - `maxParticipants` integer, nullable: Cap on total participants. Above the plan's ceiling is refused with 402 `upgrade_required`. Null resets the cap to the plan's ceiling (e.g. 50 on Free), it does NOT remove the cap; only a plan with no ceiling stores null (uncapped). Omit to leave the cap alone.
  - `requiresPhone` boolean
  - `isPrivate` boolean: May be changed on a published signup; takes effect immediately.
  - `eventWideCustomField` object, nullable: Null removes the one-off field from the signup form.
    - `name` string, required
    - `required` boolean, required
    - `type` 'short' | 'long', required

## Response `200`

Updated signup

- SignupDetail
  - `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.

## Other responses

- `400`: Bad request — body or path could not be parsed
- `401`: Unauthenticated — credentials missing, malformed, invalid, or revoked
- `402`: Upgrade required — feature is gated by a higher billing tier
- `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)
