Skip to content

Registration

Read an event's registration rules

Read how an authorized event awards entries as one ruleset: registration mode, lottery settings (window in the event's timezone, fee, spots to draw, tickets, categories, reserved entries, waitlist), the qualifying race and trail work requirements, registration questions, add-ons sold at registration, volunteer roles and custom automations, plus a plain-English summary and the saved record ids. Read it before proposing a change so the proposal is a patch over what is saved. Requires canEditEvent on the event.

Method

GET

Path

/v1/events/{eventId}/registration-rules

Required scope

capabilities:read

Request

curl -X GET "https://api.endurancehq.app/v1/events/{eventId}/registration-rules" \
  -H "Authorization: Bearer $EHQ_API_KEY"

Path parameters

eventIdstring, required

Response

Success status: 200

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "data": {
      "type": "object",
      "properties": {
        "event": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            },
            "slug": {
              "type": "string"
            },
            "title": {
              "type": "string"
            },
            "timezone": {
              "type": [
                "string",
                "null"
              ]
            },
            "date": {
              "description": "Event date, YYYY-MM-DD.",
              "type": [
                "string",
                "null"
              ]
            },
            "capacity": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                },
                {
                  "type": "null"
                }
              ]
            },
            "raceOptions": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "id",
                  "name"
                ],
                "additionalProperties": false
              },
              "description": "Distances, for raceOptionIds in the ruleset."
            }
          },
          "required": [
            "id",
            "slug",
            "title",
            "timezone",
            "date",
            "capacity",
            "raceOptions"
          ],
          "additionalProperties": false
        },
        "ruleset": {
          "type": "object",
          "properties": {
            "mode": {
              "type": "string",
              "enum": [
                "fcfs",
                "lottery"
              ],
              "description": "How entries are awarded: \"fcfs\" is first come, first served; \"lottery\" fills the field from a weighted drawing of applications."
            },
            "lottery": {
              "description": "Lottery settings. Only the fields present change; null is not allowed while the mode is lottery.",
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "opensAt": {
                      "description": "When applications open, local date-time (2026-12-01T06:00).",
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^\\d{4}-\\d{2}-\\d{2}T(?:[01]\\d|2[0-3]):[0-5]\\d$",
                          "description": "A local date and time in the event's timezone, \"YYYY-MM-DDTHH:mm\" (2026-12-01T06:00 is 6:00 AM on December 1). Never an ISO instant with Z or an offset; EHQ converts it."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "closesAt": {
                      "description": "When applications close, local date-time (2026-12-12T23:59).",
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^\\d{4}-\\d{2}-\\d{2}T(?:[01]\\d|2[0-3]):[0-5]\\d$",
                          "description": "A local date and time in the event's timezone, \"YYYY-MM-DDTHH:mm\" (2026-12-01T06:00 is 6:00 AM on December 1). Never an ISO instant with Z or an offset; EHQ converts it."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "drawingDate": {
                      "description": "The published drawing date (display only; the organizer runs the drawing).",
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                          "description": "A calendar date in the event's timezone, \"YYYY-MM-DD\"."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "announceAt": {
                      "description": "Hold results and notices until this local date-time. Null: results go out when the drawing runs.",
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^\\d{4}-\\d{2}-\\d{2}T(?:[01]\\d|2[0-3]):[0-5]\\d$",
                          "description": "A local date and time in the event's timezone, \"YYYY-MM-DDTHH:mm\" (2026-12-01T06:00 is 6:00 AM on December 1). Never an ISO instant with Z or an offset; EHQ converts it."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "applicationFeeCents": {
                      "description": "Fee to apply, in cents, charged at application. 0 for a free application.",
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 9007199254740991
                    },
                    "capacity": {
                      "description": "Total field size including reserved entries (saved on the event as its capacity): 200.",
                      "anyOf": [
                        {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 9007199254740991
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "spotsToDraw": {
                      "description": "Spots the drawing fills (180 when 20 are director picks). Null: capacity minus reserved entries. Never more than capacity minus reserved entries.",
                      "anyOf": [
                        {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 9007199254740991
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "acceptanceDeadlineHours": {
                      "description": "Hours a drawn runner has to accept and pay (default 72).",
                      "type": "number",
                      "minimum": 1
                    },
                    "tickets": {
                      "description": "How many tickets each application carries.",
                      "type": "object",
                      "properties": {
                        "base": {
                          "description": "Tickets every application starts with (default 1).",
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 100
                        },
                        "perPriorFinish": {
                          "description": "Extra tickets per prior finish of this race (1 means one more ticket for each finish). 0 or null: finishes earn nothing.",
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 100
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "priorFinishMax": {
                          "description": "Cap on tickets from prior finishes. Null: no cap.",
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 9007199254740991
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "countStartsAsFinishes": {
                          "description": "Count a DNF or DQ start the same as a finish for the per-finish ticket.",
                          "type": "boolean"
                        },
                        "volunteerYears": {
                          "description": "Tickets for years of volunteering at this race. Null: volunteering earns no tickets.",
                          "anyOf": [
                            {
                              "type": "object",
                              "properties": {
                                "afterYears": {
                                  "type": "integer",
                                  "minimum": 0,
                                  "maximum": 50,
                                  "description": "Years of volunteering for this race required before any volunteer-year ticket is earned (2 means a volunteer with 2+ years earns tickets)."
                                },
                                "perYear": {
                                  "type": "integer",
                                  "minimum": 0,
                                  "maximum": 100,
                                  "description": "Extra tickets per qualifying volunteer year."
                                },
                                "max": {
                                  "description": "Cap on tickets from volunteer years. Null: no cap.",
                                  "anyOf": [
                                    {
                                      "type": "integer",
                                      "minimum": 0,
                                      "maximum": 9007199254740991
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                },
                                "countYearsBeyondMinimum": {
                                  "description": "True: only the years past the minimum earn tickets. False (default): every year earns once the minimum is met.",
                                  "type": "boolean"
                                }
                              },
                              "required": [
                                "afterYears",
                                "perYear"
                              ],
                              "additionalProperties": false
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "repeatApplications": {
                          "description": "Tickets that build up over unsuccessful applications in earlier years.",
                          "type": "object",
                          "properties": {
                            "model": {
                              "type": "string",
                              "enum": [
                                "none",
                                "linear",
                                "doubling"
                              ],
                              "description": "\"none\": no extra tickets for unsuccessful past applications. \"linear\": adds `increment` tickets per unsuccessful year. \"doubling\": doubles each unsuccessful year."
                            },
                            "increment": {
                              "description": "Linear model only: tickets added per unsuccessful year (default 1).",
                              "type": "number",
                              "minimum": 0
                            },
                            "cap": {
                              "description": "Hard cap on total tickets after every rule. Null: no cap.",
                              "anyOf": [
                                {
                                  "type": "number",
                                  "minimum": 1
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            },
                            "resetOnDecline": {
                              "description": "A runner who is drawn and declines starts over (default true).",
                              "type": "boolean"
                            },
                            "streakBreaksOnSkippedYear": {
                              "description": "Skipping a year resets the streak (default false).",
                              "type": "boolean"
                            }
                          },
                          "required": [
                            "model"
                          ],
                          "additionalProperties": false
                        },
                        "volunteerBonus": {
                          "description": "Bonus tickets from volunteer credits or hours (separate from volunteer-year tickets).",
                          "type": "object",
                          "properties": {
                            "enabled": {
                              "type": "boolean",
                              "description": "Whether volunteer credits or hours earn bonus tickets."
                            },
                            "source": {
                              "description": "What earns the bonus: recorded credits, verified hours, or both.",
                              "type": "string",
                              "enum": [
                                "credits",
                                "hours",
                                "credits_and_hours"
                              ]
                            },
                            "scope": {
                              "description": "Whose volunteering counts: \"series\" is this race's editions (default), \"event\" this edition only, \"organization\" any of this organizer's events, \"all\" every event on the platform.",
                              "type": "string",
                              "enum": [
                                "all",
                                "organization",
                                "series",
                                "event"
                              ]
                            },
                            "creditsPerTicket": {
                              "description": "Credits required per bonus ticket (default 1).",
                              "type": "number",
                              "minimum": 1
                            },
                            "hoursPerTicket": {
                              "description": "Verified hours required per bonus ticket (default 4).",
                              "type": "number",
                              "minimum": 1
                            },
                            "max": {
                              "description": "Most bonus tickets from volunteering (default 3).",
                              "type": "number",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "enabled"
                          ],
                          "additionalProperties": false
                        },
                        "showCountsBeforeDrawing": {
                          "description": "Show applicants their ticket count on the confirmation and status page (default true). Turn off while rules are still changing.",
                          "type": "boolean"
                        }
                      },
                      "additionalProperties": false
                    },
                    "categories": {
                      "description": "Applicant categories, matched in order; an applicant lands in the first whose rule fits. Replaces the saved list when present. An empty list means one category: everyone.",
                      "maxItems": 20,
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 80,
                            "description": "Category name as runners see it: \"Veteran\", \"Never\", \"Women\"."
                          },
                          "key": {
                            "type": "string",
                            "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
                            "description": "Stable lowercase key (letters, digits, - and _). Defaults to a slug of the name; keep it when renaming so saved history stays attached."
                          },
                          "who": {
                            "description": "The plain audience this category holds. Leave out when `rule` says who belongs.",
                            "type": "string",
                            "enum": [
                              "everyone",
                              "veterans",
                              "first_timers",
                              "women",
                              "men"
                            ]
                          },
                          "rule": {
                            "description": "A custom rule instead of `who`.",
                            "type": "object",
                            "properties": {
                              "kind": {
                                "type": "string",
                                "enum": [
                                  "everyone",
                                  "finishes",
                                  "starts",
                                  "applications",
                                  "selections",
                                  "volunteer_years",
                                  "gender",
                                  "state",
                                  "age"
                                ],
                                "description": "What the rule counts: finishes or starts of this race, prior applications, prior selections (drawn in an earlier year), volunteer_years of service, the applicant's gender, home state, or age on race day."
                              },
                              "min": {
                                "description": "Lowest count or age that qualifies (inclusive). Null or absent: no lower bound.",
                                "anyOf": [
                                  {
                                    "type": "integer",
                                    "minimum": 0,
                                    "maximum": 9007199254740991
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "max": {
                                "description": "Highest count or age that qualifies (inclusive). \"max 0\" on finishes means never finished.",
                                "anyOf": [
                                  {
                                    "type": "integer",
                                    "minimum": 0,
                                    "maximum": 9007199254740991
                                  },
                                  {
                                    "type": "null"
                                  }
                                ]
                              },
                              "values": {
                                "description": "For gender: \"female\", \"male\", \"other\", \"prefer_not_to_say\". For state: state codes or names.",
                                "maxItems": 80,
                                "type": "array",
                                "items": {
                                  "type": "string",
                                  "minLength": 1,
                                  "maxLength": 60
                                }
                              }
                            },
                            "required": [
                              "kind"
                            ],
                            "additionalProperties": false
                          },
                          "also": {
                            "description": "Extra conditions every member must also meet (AND), e.g. a \"Never\" category that also requires no prior selection.",
                            "maxItems": 5,
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "kind": {
                                  "type": "string",
                                  "enum": [
                                    "everyone",
                                    "finishes",
                                    "starts",
                                    "applications",
                                    "selections",
                                    "volunteer_years",
                                    "gender",
                                    "state",
                                    "age"
                                  ],
                                  "description": "What the rule counts: finishes or starts of this race, prior applications, prior selections (drawn in an earlier year), volunteer_years of service, the applicant's gender, home state, or age on race day."
                                },
                                "min": {
                                  "description": "Lowest count or age that qualifies (inclusive). Null or absent: no lower bound.",
                                  "anyOf": [
                                    {
                                      "type": "integer",
                                      "minimum": 0,
                                      "maximum": 9007199254740991
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                },
                                "max": {
                                  "description": "Highest count or age that qualifies (inclusive). \"max 0\" on finishes means never finished.",
                                  "anyOf": [
                                    {
                                      "type": "integer",
                                      "minimum": 0,
                                      "maximum": 9007199254740991
                                    },
                                    {
                                      "type": "null"
                                    }
                                  ]
                                },
                                "values": {
                                  "description": "For gender: \"female\", \"male\", \"other\", \"prefer_not_to_say\". For state: state codes or names.",
                                  "maxItems": 80,
                                  "type": "array",
                                  "items": {
                                    "type": "string",
                                    "minLength": 1,
                                    "maxLength": 60
                                  }
                                }
                              },
                              "required": [
                                "kind"
                              ],
                              "additionalProperties": false,
                              "description": "A membership rule for applicants, used when the plain audience words do not fit."
                            }
                          },
                          "spots": {
                            "description": "Spots this category's own drawing fills before the general drawing (a guaranteed minimum). Null or absent: none guaranteed; its applicants are drawn in the general pool.",
                            "anyOf": [
                              {
                                "type": "integer",
                                "minimum": 0,
                                "maximum": 9007199254740991
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "share": {
                            "description": "True: draw the category at no less than its share of applicants, worked out at draw time (\"women get the same share they applied in\"). Ignores `spots`.",
                            "type": "boolean"
                          },
                          "cap": {
                            "description": "The most entries this category may hold after the general drawing. Null: no limit.",
                            "anyOf": [
                              {
                                "type": "integer",
                                "minimum": 0,
                                "maximum": 9007199254740991
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "rolloverUnfilled": {
                            "description": "Unfilled guaranteed spots roll into the general drawing (default true).",
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "name"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "reservedEntries": {
                      "description": "Entries held out of the drawing (director picks, tribal entries, a charity raffle). Replaces the saved list when present.",
                      "maxItems": 20,
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 80,
                            "description": "Who the entries are held for, in the organizer's words (e.g. \"Director picks\", \"Sponsor entries\", \"Charity raffle\")."
                          },
                          "key": {
                            "type": "string",
                            "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
                            "description": "Stable lowercase key (letters, digits, - and _). Defaults to a slug of the name; keep it when renaming so saved history stays attached."
                          },
                          "count": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 10000,
                            "description": "How many entries are held out of the drawing for them. Reserved entries come off capacity before the drawing picks its spots."
                          },
                          "fill": {
                            "type": "string",
                            "enum": [
                              "manual",
                              "invite_code",
                              "separate_draw"
                            ],
                            "description": "How the spots are filled: \"manual\" (the organizer assigns each one), \"invite_code\" (an invitation code tied to this bucket admits them), \"separate_draw\" (a separate raffle or drawing picks them)."
                          },
                          "note": {
                            "description": "Who decides or why, shown to the organizer only.",
                            "anyOf": [
                              {
                                "type": "string",
                                "maxLength": 500
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "name",
                          "count",
                          "fill"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "waitlist": {
                      "description": "Waitlist shape after the drawing.",
                      "type": "object",
                      "properties": {
                        "max": {
                          "description": "How many undrawn applicants are placed on the waitlist. Null (default): everyone not drawn, uncapped.",
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 9007199254740991
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "offerHours": {
                          "description": "Hours a waitlisted runner has to accept an offered spot, when different from the selection window.",
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 1,
                              "maximum": 720
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "offersCloseAt": {
                          "description": "After this local date-time no more waitlist offers go out.",
                          "anyOf": [
                            {
                              "type": "string",
                              "pattern": "^\\d{4}-\\d{2}-\\d{2}T(?:[01]\\d|2[0-3]):[0-5]\\d$",
                              "description": "A local date and time in the event's timezone, \"YYYY-MM-DDTHH:mm\" (2026-12-01T06:00 is 6:00 AM on December 1). Never an ISO instant with Z or an offset; EHQ converts it."
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "backfill": {
                          "description": "Who a released spot goes to first: \"gender\" (a woman's spot goes to the next woman waiting, a man's to the next man), \"pool\" (the next in the same category), \"order\" (the next on the waitlist, whoever it is).",
                          "type": "string",
                          "enum": [
                            "pool",
                            "gender",
                            "order"
                          ]
                        },
                        "publishApplicantList": {
                          "description": "Publish the applicant list (names and status) on the event website.",
                          "type": "boolean"
                        }
                      },
                      "additionalProperties": false
                    },
                    "selectionPayment": {
                      "description": "How a drawn runner pays.",
                      "type": "object",
                      "properties": {
                        "mode": {
                          "type": "string",
                          "enum": [
                            "respond",
                            "card_on_file"
                          ],
                          "description": "\"respond\": drawn runners pay through a deadline link. \"card_on_file\": a card saved at application is charged after the notice window."
                        },
                        "cardChargeNoticeHours": {
                          "description": "card_on_file only: hours between the selection notice and the charge (default 48).",
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 336
                        }
                      },
                      "required": [
                        "mode"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "qualifier": {
              "description": "The qualifying race runner requirement. Null removes it.",
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "name": {
                      "description": "Requirement name as runners see it (default \"Qualifying race\").",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "description": {
                      "description": "Which races count, in prose, shown to applicants.",
                      "type": "string",
                      "maxLength": 8000
                    },
                    "windowStart": {
                      "description": "First date a qualifying race may fall on (inclusive). Null: open.",
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                          "description": "A calendar date in the event's timezone, \"YYYY-MM-DD\"."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "windowEnd": {
                      "description": "Last date a qualifying race may fall on (inclusive). Null: open.",
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                          "description": "A calendar date in the event's timezone, \"YYYY-MM-DD\"."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "verifiedOnly": {
                      "description": "True: only applications whose qualifier was verified (by an organizer or the automatic check) enter the drawing.",
                      "type": "boolean"
                    },
                    "autoVerify": {
                      "description": "True: an automatic \"meets\" finding from the standards check verifies the qualifier without review.",
                      "type": "boolean"
                    },
                    "standards": {
                      "description": "Checkable standards. Replaces the saved list when present.",
                      "maxItems": 12,
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "distance",
                              "race",
                              "list"
                            ],
                            "description": "\"distance\": a course of at least a distance with gain and time bounds. \"race\": one named race. \"list\": a maintained list of races."
                          },
                          "minDistanceKm": {
                            "description": "distance: shortest course that counts, in kilometers (100K = 100, 100 miles = 160.9344).",
                            "type": "number",
                            "minimum": 0
                          },
                          "maxDistanceKm": {
                            "description": "distance: longest course that counts, in kilometers. Null: open.",
                            "anyOf": [
                              {
                                "type": "number",
                                "minimum": 0
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "minGainM": {
                            "description": "distance: least elevation gain, in meters (10,000 ft = 3048; 15,000 ft = 4572).",
                            "anyOf": [
                              {
                                "type": "number",
                                "minimum": 0
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "maxGainM": {
                            "description": "distance: most elevation gain, in meters. Null: open.",
                            "anyOf": [
                              {
                                "type": "number",
                                "minimum": 0
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "maxTimeSeconds": {
                            "description": "Finish time cap in seconds (20 hours = 72000). Null: finishing inside the race's own cutoff is enough.",
                            "anyOf": [
                              {
                                "type": "number",
                                "minimum": 0
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "raceName": {
                            "description": "race: the race's name (\"Copper Kings 100\").",
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 160
                          },
                          "crewableSlug": {
                            "description": "race: Crewable directory slug when known.",
                            "anyOf": [
                              {
                                "type": "string",
                                "minLength": 1,
                                "maxLength": 120
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "ehqEventId": {
                            "description": "race: Endurance HQ event id when the race is on the platform.",
                            "anyOf": [
                              {
                                "type": "string",
                                "minLength": 1,
                                "maxLength": 64
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "listId": {
                            "description": "list: the list id (\"western-states-100\").",
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 80
                          },
                          "label": {
                            "description": "The organizer's own words for this line, shown instead of the generated one.",
                            "anyOf": [
                              {
                                "type": "string",
                                "maxLength": 200
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "kind"
                        ],
                        "additionalProperties": false,
                        "description": "One checkable standard a qualifying race must meet."
                      }
                    },
                    "askedAt": {
                      "description": "When the qualifier is asked: \"during_registration\" (on the application; default) or \"after_registration\".",
                      "type": "string",
                      "enum": [
                        "during_registration",
                        "after_registration"
                      ]
                    },
                    "raceOptionIds": {
                      "description": "Only these distances (race option ids) ask it. Empty or absent: every distance.",
                      "maxItems": 50,
                      "type": "array",
                      "items": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 64
                      }
                    }
                  },
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "trailWork": {
              "description": "The trail work runner requirement. Null removes it.",
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "name": {
                      "description": "Requirement name as runners see it (default \"Trail work\").",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "description": {
                      "description": "What counts and how to log it, in prose, shown to entrants.",
                      "type": "string",
                      "maxLength": 8000
                    },
                    "hours": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 100,
                      "description": "Hours each entrant owes (8)."
                    },
                    "dueDate": {
                      "description": "Local calendar date the hours are due by (end of that day in the event's timezone). Null: no due date.",
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                          "description": "A calendar date in the event's timezone, \"YYYY-MM-DD\"."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "buyoutCents": {
                      "description": "Donation that satisfies the requirement instead of hours, in cents ($250 = 25000). Null: no buyout.",
                      "anyOf": [
                        {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 9007199254740991,
                          "description": "Money in cents: $250 is 25000, free is 0."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "beneficiary": {
                      "description": "Where buyout donations go. Null clears it.",
                      "anyOf": [
                        {
                          "type": "object",
                          "properties": {
                            "organizationId": {
                              "description": "Saved donation organization id when known.",
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 64
                            },
                            "name": {
                              "description": "Trail group that receives buyout donations, by name. Created on the event when it does not exist yet.",
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 160
                            },
                            "donationUrl": {
                              "description": "The group's own donation page, when donations are paid there.",
                              "type": "string",
                              "format": "uri"
                            }
                          },
                          "additionalProperties": false
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "workWindowStart": {
                      "description": "Earliest work date that counts (inclusive). Null: any date.",
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                          "description": "A calendar date in the event's timezone, \"YYYY-MM-DD\"."
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "accepts": {
                      "description": "The kinds of work that count toward the hours (default: all three).",
                      "type": "object",
                      "properties": {
                        "organizationDays": {
                          "type": "boolean",
                          "description": "Work on trail days the organization runs counts."
                        },
                        "outsideTrailWork": {
                          "type": "boolean",
                          "description": "Trail work with another group, confirmed by that group's coordinator, counts."
                        },
                        "outsideVolunteering": {
                          "type": "boolean",
                          "description": "Volunteering at another race, confirmed by its director, counts."
                        }
                      },
                      "required": [
                        "organizationDays",
                        "outsideTrailWork",
                        "outsideVolunteering"
                      ],
                      "additionalProperties": false
                    },
                    "organizationDays": {
                      "description": "Trail days the organization runs. Replaces the saved list when present.",
                      "maxItems": 50,
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "description": "Stable id; defaults to a slug of the date and name.",
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 64
                          },
                          "date": {
                            "type": "string",
                            "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                            "description": "A calendar date in the event's timezone, \"YYYY-MM-DD\"."
                          },
                          "name": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 120,
                            "description": "What the day is called."
                          },
                          "location": {
                            "anyOf": [
                              {
                                "type": "string",
                                "maxLength": 160
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "hours": {
                            "description": "Hours a full day counts for; the runner can log fewer.",
                            "anyOf": [
                              {
                                "type": "number",
                                "minimum": 0.25,
                                "maximum": 24
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "date",
                          "name"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "directorReview": {
                      "description": "True: a coordinator's confirmation still waits for the director before the hours count (default false).",
                      "type": "boolean"
                    },
                    "deadline": {
                      "description": "What happens to entrants who have not done the hours by the due date.",
                      "type": "object",
                      "properties": {
                        "action": {
                          "type": "string",
                          "enum": [
                            "flag",
                            "release_spot"
                          ],
                          "description": "At the due date: \"flag\" only lists overdue entrants for the director; \"release_spot\" drops them from the field so the spot goes to the waitlist."
                        },
                        "graceDays": {
                          "description": "Days after the due date before the action runs (default 0).",
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 60
                        },
                        "requires": {
                          "description": "What counts as done at the deadline: \"logged\" (enough hours logged, verification may still be pending) or \"verified\" (hours verified).",
                          "type": "string",
                          "enum": [
                            "logged",
                            "verified"
                          ]
                        },
                        "notifyRunner": {
                          "description": "Tell the runner when the action runs. False drops them silently; the scheduled reminders still go out beforehand.",
                          "type": "boolean"
                        },
                        "lateAdmitDays": {
                          "description": "Days an entrant admitted after the due date gets, counted from their admission, before the deadline action applies to them. 0 or null: the next run treats them like everyone else.",
                          "anyOf": [
                            {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 365
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "action"
                      ],
                      "additionalProperties": false
                    },
                    "showVerifiedOnApplicantList": {
                      "description": "Show a \"Trail work verified\" check beside accepted entrants on the public list (default false).",
                      "type": "boolean"
                    },
                    "reminders": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean",
                          "description": "Send the shared requirement reminders."
                        },
                        "daysBefore": {
                          "description": "Days before the due date a reminder goes out (default 30, 14, 7, 1).",
                          "maxItems": 12,
                          "type": "array",
                          "items": {
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 365
                          }
                        }
                      },
                      "required": [
                        "enabled"
                      ],
                      "additionalProperties": false
                    },
                    "raceOptionIds": {
                      "description": "Only entrants in these distances owe it. Empty or absent: every distance.",
                      "maxItems": 50,
                      "type": "array",
                      "items": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 64
                      }
                    }
                  },
                  "required": [
                    "hours"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "questions": {
              "description": "Registration questions, matched to saved ones by label. Saved questions not listed are left alone.",
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "The question as the form asks it (\"Would you like a pacer?\"). Matches a saved question by label."
                  },
                  "type": {
                    "description": "\"checkbox\" for a yes/no, \"select\" one of the options, \"multiselect\" several, \"number\", \"textarea\" for long text, \"text\" otherwise (default).",
                    "type": "string",
                    "enum": [
                      "text",
                      "select",
                      "multiselect",
                      "checkbox",
                      "number",
                      "textarea"
                    ]
                  },
                  "options": {
                    "description": "Answer choices for select and multiselect.",
                    "anyOf": [
                      {
                        "maxItems": 50,
                        "type": "array",
                        "items": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 200
                        }
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "required": {
                    "description": "Whether the runner must answer (default false).",
                    "type": "boolean"
                  },
                  "askedAt": {
                    "description": "\"during_registration\" asks at checkout or on the application (default); \"after_registration\" asks in the event app afterwards.",
                    "type": "string",
                    "enum": [
                      "during_registration",
                      "after_registration"
                    ]
                  },
                  "visibility": {
                    "anyOf": [
                      {
                        "type": "object",
                        "properties": {
                          "minAgeOnRaceDay": {
                            "description": "Ask only runners at least this old on race day (65 for a 65+ pacer request).",
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 130
                          },
                          "maxAgeOnRaceDay": {
                            "description": "Ask only runners at most this old on race day.",
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 130
                          },
                          "raceOptionIds": {
                            "description": "Ask only on these distances (race option ids).",
                            "maxItems": 100,
                            "type": "array",
                            "items": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 64
                            }
                          },
                          "answerEquals": {
                            "description": "Ask only when another question has this answer.",
                            "type": "object",
                            "properties": {
                              "questionLabel": {
                                "type": "string",
                                "minLength": 1,
                                "maxLength": 200,
                                "description": "The other question's label."
                              },
                              "value": {
                                "type": "string",
                                "minLength": 1,
                                "maxLength": 500,
                                "description": "The answer that reveals this question (\"true\" for a checked box)."
                              }
                            },
                            "required": [
                              "questionLabel",
                              "value"
                            ],
                            "additionalProperties": false
                          }
                        },
                        "additionalProperties": false,
                        "description": "Who is asked; every rule present must hold. Absent or null: everyone."
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "autoFillSource": {
                    "description": "Prefill a number question from EHQ data (volunteer years of service). Answers are always organizer-only; nothing a runner answers here is published.",
                    "anyOf": [
                      {
                        "type": "string",
                        "enum": [
                          "volunteer_years_of_service"
                        ]
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "remove": {
                    "description": "True removes the saved question with this label.",
                    "type": "boolean"
                  }
                },
                "required": [
                  "label"
                ],
                "additionalProperties": false
              }
            },
            "addOns": {
              "description": "Things for sale at registration, matched to saved add-ons by title.",
              "maxItems": 50,
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "What is for sale at registration (\"Camping spot, church lot\"). Matches a saved add-on by title."
                  },
                  "description": {
                    "anyOf": [
                      {
                        "type": "string",
                        "maxLength": 2000
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "priceCents": {
                    "anyOf": [
                      {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 9007199254740991,
                        "description": "Money in cents: $250 is 25000, free is 0."
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Price per unit in cents. Null when the price isn't decided yet: applying asks the organizer for it as a quick answer and puts the add-on on sale once they reply."
                  },
                  "kind": {
                    "description": "\"service\" for a spot, reservation or seat (default); \"merch\" for a physical item.",
                    "type": "string",
                    "enum": [
                      "service",
                      "merch"
                    ]
                  },
                  "inventory": {
                    "description": "Stock for this add-on. Null: unlimited.",
                    "anyOf": [
                      {
                        "type": "object",
                        "properties": {
                          "total": {
                            "anyOf": [
                              {
                                "type": "integer",
                                "minimum": 0,
                                "maximum": 9007199254740991
                              },
                              {
                                "type": "null"
                              }
                            ],
                            "description": "Units for sale in total (7 camping spots). EHQ counts what is left as runners buy and shows when it is full. Null: not tracked."
                          }
                        },
                        "required": [
                          "total"
                        ],
                        "additionalProperties": false
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "namedSpots": {
                    "description": "Each unit is a specific spot the organizer assigns (camping sites), with live availability on the form.",
                    "type": "boolean"
                  },
                  "salesChannel": {
                    "description": "Where it is offered: \"registration_only\" at registration (default), \"store_and_registration\" in the store as well, \"registered_runners_only\" in the event app shop to runners holding a confirmed entry (camping bought after a lottery acceptance), never on the public form.",
                    "type": "string",
                    "enum": [
                      "registration_only",
                      "store_and_registration",
                      "registered_runners_only"
                    ]
                  },
                  "published": {
                    "description": "Live for sale (default true). False keeps it a draft.",
                    "type": "boolean"
                  },
                  "remove": {
                    "description": "True archives the saved add-on with this title.",
                    "type": "boolean"
                  }
                },
                "required": [
                  "title",
                  "priceCents"
                ],
                "additionalProperties": false
              }
            },
            "volunteerPositions": {
              "description": "Volunteer roles, matched by name.",
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 160,
                    "description": "Role name (\"Aid station captain\", \"Ham radio operator\"). Matches a saved role by name."
                  },
                  "description": {
                    "description": "What the role does, shown to volunteers.",
                    "anyOf": [
                      {
                        "type": "string",
                        "maxLength": 2000
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "category": {
                    "description": "What the role is, independent of its name, so captain-only audiences and station cards find it.",
                    "anyOf": [
                      {
                        "type": "string",
                        "enum": [
                          "pre_race_course_check",
                          "aid_station_captain",
                          "aid_station_team",
                          "course_monitor",
                          "check_in",
                          "video_content",
                          "finish_area",
                          "course_sweep",
                          "setup_teardown",
                          "parking",
                          "medical",
                          "radio",
                          "course_marking",
                          "other"
                        ]
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "active": {
                    "description": "False hides the role from signups without deleting it.",
                    "type": "boolean"
                  },
                  "communicationPolicy": {
                    "description": "Whether people in this role receive event-wide volunteer messages.",
                    "type": "string",
                    "enum": [
                      "inherit",
                      "include",
                      "exclude"
                    ]
                  }
                },
                "required": [
                  "name"
                ],
                "additionalProperties": false
              }
            },
            "automations": {
              "description": "Recurring or anchored messages, matched by name.",
              "maxItems": 50,
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "Automation name (\"Trail work reminder\"). Matches a saved custom automation by name."
                  },
                  "anchor": {
                    "type": "string",
                    "enum": [
                      "event_date",
                      "registration",
                      "calendar_date"
                    ],
                    "description": "What the send time counts from: \"event_date\" (the race date), \"registration\" (each runner's registration), or \"calendar_date\" (a fixed month and day every year)."
                  },
                  "offsetDays": {
                    "description": "Days relative to the anchor; negative is before (event_date and -7 sends a week before the race). Registration sends at or after registration only.",
                    "type": "integer",
                    "minimum": -365,
                    "maximum": 365
                  },
                  "offsetMinutes": {
                    "description": "Minutes relative to the anchor when days are too coarse. Added to offsetDays.",
                    "type": "integer",
                    "minimum": -525600,
                    "maximum": 525600
                  },
                  "calendarDate": {
                    "description": "calendar_date only: the date each year (April 20 for an annual trail work reminder).",
                    "type": "object",
                    "properties": {
                      "month": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 12,
                        "description": "Month, 1 to 12."
                      },
                      "day": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 31,
                        "description": "Day of the month."
                      },
                      "time": {
                        "description": "Local send time \"HH:mm\" (default 09:00).",
                        "type": "string",
                        "pattern": "^(?:[01]\\d|2[0-3]):[0-5]\\d$"
                      }
                    },
                    "required": [
                      "month",
                      "day"
                    ],
                    "additionalProperties": false
                  },
                  "keepOnEachEdition": {
                    "description": "Carry this automation to each new edition of the race so it is set once and lightly edited each year.",
                    "type": "boolean"
                  },
                  "audience": {
                    "description": "Who receives it (default participants).",
                    "type": "string",
                    "enum": [
                      "participants",
                      "volunteers"
                    ]
                  },
                  "segment": {
                    "description": "A saved smart segment key instead of the built-in audience.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "list": {
                    "description": "A saved contact list id instead of the built-in audience.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "channels": {
                    "description": "Delivery channels (default email).",
                    "minItems": 1,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "email",
                        "sms",
                        "push"
                      ]
                    }
                  },
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 300,
                    "description": "Subject line."
                  },
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100000,
                    "description": "Message body."
                  },
                  "reviewMode": {
                    "description": "\"auto\" sends on schedule; \"notify\" tells the organizer first; \"hold\" waits for the organizer to release it (default hold for event-date sends).",
                    "type": "string",
                    "enum": [
                      "auto",
                      "notify",
                      "hold"
                    ]
                  },
                  "reviewNoticeDays": {
                    "description": "Days before the send the review notice goes out (default 3).",
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 30
                  },
                  "active": {
                    "description": "Turned on (default false). Activating schedules real sends.",
                    "type": "boolean"
                  },
                  "remove": {
                    "description": "True turns the saved automation with this name off (sends stop; history is kept).",
                    "type": "boolean"
                  }
                },
                "required": [
                  "name",
                  "anchor",
                  "subject",
                  "body"
                ],
                "additionalProperties": false
              }
            },
            "notes": {
              "description": "Open decisions to flag for the organizer rather than guess (\"Veteran ticket per finish still undecided\").",
              "maxItems": 50,
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1,
                "maxLength": 500
              }
            }
          },
          "additionalProperties": false,
          "description": "A race's registration rules in plain terms. Absent sections are left as they are."
        },
        "summary": {
          "type": "string"
        },
        "sources": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        }
      },
      "required": [
        "event",
        "ruleset",
        "summary",
        "sources"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "data"
  ],
  "additionalProperties": false
}

Other surfaces

CLI: ehq registration rules get --event <event_id>

MCP tool: ehq_registration_rules_get