# Classes with sections

> Programs offered in several variants, filtered one section at a time.

Source: https://api.tailend.dev/docs/class-sections

Many classes are really one program offered in several variants: a Saturday
morning group for ages 3-5, a Sunday group for ages 9-12, a fall term and a
spring term. The class response describes the program once and lists the
variants as **sections**.

```bash
curl -sS -H "X-API-Key: $TAILEND_API_KEY" --get https://api.tailend.dev/v1/philadelphia/classes \
  --data-urlencode "schedule_day=saturday" \
  --data-urlencode "age=4" \
  --data-urlencode "openings=true"
```

## What a section is

Each item in `sections[]` is one way to take the program: its own days and
times, term dates, age range, tuition and registration state. The field is
always present, and is an empty array when the program has no separate
variants. The program's own `schedule`, `term`, `ages` and `tuition` stay as
the headline values.

```json
{
  "name": "Kids Gymnastics",
  "tuition": { "amount": 185, "from": 160 },
  "registration": { "has_spots_available": true, "sections_with_spots": 1 },
  "sections_total": 2,
  "sections": [
    {
      "id": "…",
      "name": "Saturday 10:00",
      "schedule": { "days": ["saturday"], "start_time": "10:00:00", "end_time": "11:00:00" },
      "ages": { "min": 3, "max": 5 },
      "tuition": { "amount": 185, "currency": "USD" },
      "registration": { "status": "full", "has_spots_available": false }
    },
    {
      "id": "…",
      "name": "Sunday 14:00",
      "schedule": { "days": ["sunday"], "start_time": "14:00:00", "end_time": "15:00:00" },
      "ages": { "min": 9, "max": 12 },
      "tuition": { "amount": 160, "currency": "USD" },
      "registration": { "status": "open", "has_spots_available": true }
    }
  ]
}
```

At the program level:

- `sections_total` is how many sections there are.
- `registration.sections_with_spots` is how many sections are known to have room: seats left, or marked available, and not closed. A section whose availability is unknown is not counted.
- `registration.has_spots_available` is `true` when the program or any section has seats.
- `tuition.from` is the lowest section tuition, for "from $X" displays. With no
  section prices it is the program's own tuition.

## Filtering

A program with sections matches only when **one section** satisfies every
filter you send. The filters are `schedule_day` and `weekday`, `age`,
`start_after` and `start_before`, `from`, `to` and `season`, `free` and
`max_price`, and `openings=true`.

With a Saturday section for ages 3-5 and a Sunday section for ages 9-12:

| Request | Matches? |
| --- | --- |
| `schedule_day=saturday&age=4` | Yes, the Saturday section fits both. |
| `schedule_day=saturday&age=10` | No. The age fits Sunday, the day fits Saturday. |
| `age=7` | No. 7 falls between the two sections. |

`openings=true` keeps programs with open seats in the section that also meets
your other filters. Programs without sections are judged on their own fields.
Filters decide which programs come back and never trim `sections[]`, so you
always see the whole program.

## One card per program

When a provider publishes the same program as several separate listings, a
class list shows them as a single card. The other listings are nested under the
card's `sessions`, each a complete class you can also fetch by its own `id`.
Cards are formed before paging, so a page never splits one and `limit` counts
cards.

Send `expand=sessions` to get every class on its own instead. A `cursor` only
works with the same `expand` and `openings` values it was issued under; change
either and start again from the first page.

## In the changes feed

An edit to a section, or a section being added or removed, appears as an
update to its program, with `sections` in `fields`, in both
`/v1/changes` and `/v1/listings/{id}/versions`. It is never a creation or an
archive of the program.
