{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://opentechevents.org/schema/v0.3/event.schema.json",
  "title": "OTE Event",
  "description": "A single tech community event. See https://opentechevents.org for the normative prose.",
  "allOf": [
    {
      "$ref": "#/$defs/event"
    },
    {
      "type": "object",
      "description": "A standalone event document must carry its own specVersion and license. Inside a feed, both are inherited from the feed.",
      "required": [
        "specVersion",
        "license"
      ]
    }
  ],
  "$defs": {
    "event": {
      "type": "object",
      "required": [
        "id",
        "name",
        "startDate",
        "timezone"
      ],
      "properties": {
        "specVersion": {
          "description": "Version of OTE Spec this document adheres to.",
          "const": "0.3.0",
          "examples": [
            "0.3.0"
          ]
        },
        "id": {
          "description": "Stable, globally unique identifier. A URI under a domain the publisher controls. Minted once, never rewritten — this is what lets consumers update an event instead of duplicating it.",
          "type": "string",
          "format": "uri",
          "pattern": "^[a-zA-Z][a-zA-Z0-9+.-]*:",
          "examples": [
            "https://pyalmeria.example/eventos/2026-06-async",
            "https://calendar.example/ics/rust-madrid#a1b2c3d4-uid"
          ]
        },
        "url": {
          "description": "Canonical URL where the event is described today. May change over time; id may not.",
          "type": "string",
          "format": "uri",
          "pattern": "^https?://",
          "examples": [
            "https://pyalmeria.example/eventos/2026-06-async"
          ]
        },
        "name": {
          "description": "Display name of the event.",
          "type": "string",
          "minLength": 1,
          "examples": [
            "PyAlmería — Introducción a async/await"
          ]
        },
        "description": {
          "description": "Short description. Plain text or Markdown.",
          "type": "string",
          "examples": [
            "Charla introductoria a la programación asíncrona en Python, con ejemplos en vivo."
          ]
        },
        "image": {
          "description": "Promotional images of the event: poster, cover, card. A list, in preference order — the first is the primary one, and often the only one a destination can use. Several entries describe the SAME image in different crops or resolutions (Google asks for 1:1, 4:3 and 16:9), not a photo gallery. Absolute https URLs to the image file itself, never to a page showing it.",
          "type": "array",
          "items": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://"
          },
          "minItems": 1,
          "examples": [
            [
              "https://rustmadrid.example/img/2026-06-16x9.png"
            ],
            [
              "https://rustmadrid.example/img/2026-06-1x1.png",
              "https://rustmadrid.example/img/2026-06-4x3.png",
              "https://rustmadrid.example/img/2026-06-16x9.png"
            ]
          ]
        },
        "organizers": {
          "description": "Who runs the event — not where the data came from (that is source). A list: co-organised events are the norm, not the exception. Inside a feed, an event that omits it inherits the feed's organizers; declaring it REPLACES the inherited list, it does not add to it.",
          "$ref": "#/$defs/organizers",
          "examples": [
            [
              {
                "name": "PyAlmería",
                "url": "https://pyalmeria.example"
              }
            ],
            [
              {
                "name": "GDG Madrid",
                "url": "https://gdgmadrid.example"
              },
              {
                "type": "person",
                "name": "Ada Lovelace",
                "url": "https://ada.example"
              }
            ]
          ]
        },
        "startDate": {
          "description": "Wall-clock start: a date (2026-10-15) for all-day events, or a local date-time (2026-10-15T09:00:00). Never carries a UTC offset — timezone does that.",
          "$ref": "#/$defs/wallClock",
          "examples": [
            "2026-06-11T18:30:00",
            "2026-10-15"
          ]
        },
        "endDate": {
          "description": "Wall-clock end, in the SAME form as startDate (both dates, or both date-times). If absent, the event is assumed to end on the day it starts.",
          "$ref": "#/$defs/wallClock",
          "examples": [
            "2026-06-11T20:00:00",
            "2026-10-16"
          ]
        },
        "timezone": {
          "description": "IANA timezone (e.g. Europe/Madrid). Turns a wall-clock startDate into an unambiguous instant. For all-day events it contextualises the date — it does not shift it.",
          "type": "string",
          "pattern": "^[A-Za-z_]+(?:/[A-Za-z0-9_+-]+)+$|^UTC$",
          "examples": [
            "Europe/Madrid",
            "America/Bogota",
            "UTC"
          ]
        },
        "attendanceMode": {
          "description": "What the organiser says this event is. NO DEFAULT: absent means unknown, never in-person.",
          "enum": [
            "in-person",
            "online",
            "hybrid"
          ],
          "examples": [
            "in-person",
            "online",
            "hybrid"
          ]
        },
        "location": {
          "$ref": "#/$defs/location",
          "examples": [
            {
              "venue": "El Cable, Almería"
            },
            {
              "onlineUrl": "https://meet.example/pyalmeria"
            },
            {
              "venue": "Campus Madrid, Calle de Moreno Nieto 2, Madrid",
              "address": {
                "street": "Calle de Moreno Nieto 2",
                "locality": "Madrid",
                "postalCode": "28005",
                "country": "ES"
              },
              "onlineUrl": "https://meet.example/rust-madrid"
            }
          ]
        },
        "tags": {
          "description": "Free-form topic tags. Maps to iCal CATEGORIES and schema.org keywords. A controlled vocabulary may layer on top later; the field itself stays free. No default: absent means unknown.",
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "examples": [
            [
              "rust",
              "wasm"
            ],
            [
              "python",
              "async"
            ]
          ]
        },
        "languages": {
          "description": "BCP 47 tags, e.g. [\"es\",\"en\"]. No default: absent means unknown.",
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$"
          },
          "minItems": 1,
          "examples": [
            [
              "es"
            ],
            [
              "es",
              "en"
            ]
          ]
        },
        "offers": {
          "description": "What it costs to attend, and where to register. A list: tiered pricing (early bird, student, patron) is one entry each, and a free event is a single entry with price 0. Absent means UNKNOWN, never free — saying free is what price 0 is for.",
          "$ref": "#/$defs/offers",
          "examples": [
            [
              {
                "price": 0,
                "url": "https://rustmadrid.example/meetups/2026-06#registro"
              }
            ],
            [
              {
                "name": "Early bird",
                "price": 35,
                "currency": "EUR",
                "url": "https://devfest-levante.example/2026/entradas",
                "availability": "sold-out",
                "closesAt": "2026-07-31T23:59:59+02:00"
              },
              {
                "name": "General",
                "price": 45,
                "currency": "EUR",
                "url": "https://devfest-levante.example/2026/entradas",
                "availability": "in-stock"
              }
            ]
          ]
        },
        "cfp": {
          "description": "The event's open call for proposals — talks, workshops, papers. The one field of the spec with no equivalent in ANY of the three destination formats: it exists because 'which conferences are accepting proposals right now' is a question only the publisher can answer, and today it is answered by scraping.",
          "$ref": "#/$defs/cfp",
          "examples": [
            {
              "url": "https://devfest-levante.example/2026/cfp",
              "closesAt": "2026-07-15T23:59:59+02:00"
            },
            {
              "url": "https://devfest-levante.example/2026/cfp",
              "opensAt": "2026-05-01T00:00:00+02:00",
              "closesAt": "2026-07-15T23:59:59+02:00",
              "coversTravel": true,
              "coversAccommodation": true
            }
          ]
        },
        "status": {
          "description": "What happened to the event, not to the data. An event that is cancelled, postponed or moved online MUST stay published: removing it leaves a dead event in subscribers' calendars. tentative means announced but not confirmed (iCal STATUS:TENTATIVE) — it exists so an importer never has to upgrade an unconfirmed event to scheduled, which is the one value with a default.",
          "enum": [
            "scheduled",
            "tentative",
            "cancelled",
            "postponed",
            "rescheduled",
            "moved-online"
          ],
          "default": "scheduled",
          "examples": [
            "scheduled",
            "cancelled",
            "moved-online"
          ]
        },
        "partOf": {
          "description": "The series or multi-part event this document is one occurrence of. A REFERENCE, never a recurrence rule: OTE does not generate dates: whoever publishes expands the recurrence into one document per occurrence, each with its own id, dates and status. A consumer that ignores this field still sees complete, correct events.",
          "$ref": "#/$defs/partOf",
          "examples": [
            {
              "id": "https://rustmadrid.example/meetups",
              "name": "Rust Madrid — meetup mensual",
              "url": "https://rustmadrid.example/meetups"
            },
            {
              "type": "multipart",
              "id": "https://pyalmeria.example/study-jams/2026-testing",
              "name": "Study Jam de testing en Python (3 sesiones)"
            }
          ]
        },
        "license": {
          "description": "License of THIS DATA, not of the event. SPDX identifier (CC0-1.0, CC-BY-4.0…, full list at https://spdx.org/licenses/) or a URL.",
          "$ref": "#/$defs/license",
          "examples": [
            "CC-BY-4.0",
            "CC0-1.0"
          ]
        },
        "source": {
          "description": "Provenance. Required when the event was imported or aggregated from elsewhere; omitted when the organiser describes their own event — they are the source.",
          "$ref": "#/$defs/source",
          "examples": [
            {
              "name": "Rust Madrid",
              "url": "https://calendar.example/ics/rust-madrid",
              "license": "CC-BY-4.0",
              "retrievedAt": "2026-06-01T05:00:00Z"
            }
          ]
        },
        "updatedAt": {
          "description": "Instant the event's DATA last changed — equivalent to iCal LAST-MODIFIED, not DTSTAMP (which marks generation and changes on every export). Lets a consumer sync incrementally: fetch only what changed since its last read. Absent means unknown, not 'never changed'.",
          "$ref": "#/$defs/instant",
          "examples": [
            "2026-06-10T18:00:00Z"
          ]
        }
      },
      "allOf": [
        {
          "description": "startDate and endDate must be of the same form: two all-day dates, or two local date-times.",
          "oneOf": [
            {
              "properties": {
                "startDate": {
                  "$ref": "#/$defs/date"
                },
                "endDate": {
                  "$ref": "#/$defs/date"
                }
              },
              "type": "object"
            },
            {
              "properties": {
                "startDate": {
                  "$ref": "#/$defs/dateTime"
                },
                "endDate": {
                  "$ref": "#/$defs/dateTime"
                }
              },
              "type": "object"
            }
          ]
        }
      ]
    },
    "date": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
    },
    "dateTime": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(:\\d{2})?$"
    },
    "wallClock": {
      "type": "string",
      "anyOf": [
        {
          "$ref": "#/$defs/date"
        },
        {
          "$ref": "#/$defs/dateTime"
        }
      ]
    },
    "instant": {
      "description": "An absolute point in time, WITH offset or Z. Used for metadata (when data was fetched) and for deadlines (when a sale or a call closes) — never for when an event happens, which is wall clock plus timezone.",
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$"
    },
    "license": {
      "type": "string",
      "minLength": 2,
      "pattern": "^([A-Za-z0-9.+-]+|https?://.+)$"
    },
    "organizers": {
      "description": "Who runs the event or the feed. Kept deliberately small: a name, and where to find them.",
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/organizer"
      }
    },
    "organizer": {
      "type": "object",
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "description": "Display name of the organiser.",
          "type": "string",
          "minLength": 1,
          "examples": [
            "PyAlmería",
            "Ada Lovelace"
          ]
        },
        "url": {
          "description": "Where this organiser lives on the web — their own site, or their profile on the platform they publish from.",
          "type": "string",
          "format": "uri",
          "pattern": "^https?://",
          "examples": [
            "https://pyalmeria.example",
            "https://www.meetup.com/pyalmeria/"
          ]
        },
        "type": {
          "description": "Organisation or person. Defaults to organisation: a translator has to pick a schema.org @type, and Organization is the tolerant choice.",
          "enum": [
            "organization",
            "person"
          ],
          "default": "organization",
          "examples": [
            "organization",
            "person"
          ]
        }
      }
    },
    "partOf": {
      "description": "A reference to the whole this occurrence belongs to. Identity only — no dates: the occurrence already carries them.",
      "type": "object",
      "required": [
        "id"
      ],
      "properties": {
        "id": {
          "description": "Stable identifier of the series or multi-part event. Same rules as the event's id: a URI under a domain the publisher controls, minted once. It does NOT have to resolve to an OTE document — it is what lets a consumer group occurrences.",
          "type": "string",
          "format": "uri",
          "pattern": "^[a-zA-Z][a-zA-Z0-9+.-]*:",
          "examples": [
            "https://rustmadrid.example/meetups",
            "https://pyalmeria.example/study-jams/2026-testing"
          ]
        },
        "name": {
          "description": "Display name of the series or multi-part event, so a consumer can group occurrences without resolving the id.",
          "type": "string",
          "minLength": 1,
          "examples": [
            "Rust Madrid — meetup mensual",
            "Study Jam de testing en Python (3 sesiones)"
          ]
        },
        "url": {
          "description": "Page describing the series or the multi-part event as a whole.",
          "type": "string",
          "format": "uri",
          "pattern": "^https?://",
          "examples": [
            "https://rustmadrid.example/meetups"
          ]
        },
        "type": {
          "description": "series: independent occurrences that share an identity (a monthly meetup). multipart: parts of ONE event held on non-consecutive dates (a three-session study jam on non-consecutive Saturdays, one registration). Defaults to series, the tolerant choice. It changes the translation: a series becomes schema.org EventSeries, a multi-part event becomes an Event whose parts are its subEvent.",
          "enum": [
            "series",
            "multipart"
          ],
          "default": "series",
          "examples": [
            "series",
            "multipart"
          ]
        }
      }
    },
    "location": {
      "description": "What is KNOWN about where the event happens. Not the same question as attendanceMode, which states the organiser's intent.",
      "type": "object",
      "properties": {
        "venue": {
          "description": "Human-readable physical location. Its presence means the event has a physical venue. It is the string every destination can print as-is — iCal LOCATION, the text of an RSS item, schema.org Place.name.",
          "type": "string",
          "minLength": 1,
          "examples": [
            "El Cable, Almería"
          ]
        },
        "address": {
          "description": "Postal address of the physical venue, in parts. COMPLEMENTS venue, never replaces it: venue is the one string every format can print, address is what a translator needs to emit a schema.org PostalAddress — whose subfields Google validates one by one for the Event rich result. Every part is optional; leave out what you do not know. An absent key means unknown; \"\" or null publish 'unknown' as if it were data, which is the one thing worse than saying nothing.",
          "$ref": "#/$defs/address",
          "examples": [
            {
              "street": "Calle de Moreno Nieto 2",
              "locality": "Madrid",
              "postalCode": "28005",
              "country": "ES"
            },
            {
              "locality": "Almería",
              "country": "ES"
            }
          ]
        },
        "onlineUrl": {
          "description": "URL to attend online. Its presence means the event has online access.",
          "type": "string",
          "format": "uri",
          "pattern": "^https?://",
          "examples": [
            "https://meet.example/pyalmeria"
          ]
        },
        "geo": {
          "description": "Coordinates of the physical venue (WGS-84 decimal degrees). Independent of venue, which is free text — a point, not a name. Maps to iCal GEO and schema.org Place.geo (GeoCoordinates).",
          "type": "object",
          "required": [
            "lat",
            "lon"
          ],
          "properties": {
            "lat": {
              "description": "Latitude in decimal degrees.",
              "type": "number",
              "minimum": -90,
              "maximum": 90,
              "examples": [
                40.4168
              ]
            },
            "lon": {
              "description": "Longitude in decimal degrees.",
              "type": "number",
              "minimum": -180,
              "maximum": 180,
              "examples": [
                -3.7038
              ]
            }
          }
        }
      },
      "anyOf": [
        {
          "required": [
            "venue"
          ]
        },
        {
          "required": [
            "onlineUrl"
          ]
        }
      ]
    },
    "address": {
      "description": "A postal address in parts, mapped 1:1 onto schema.org PostalAddress. Five fields, all optional, none of them free-form enough to be a second venue: this is the machine-readable half of a location, not a prettier one.",
      "type": "object",
      "minProperties": 1,
      "properties": {
        "street": {
          "description": "Street and number, as written locally. May carry a floor or a unit; it is one line of text, not a sub-object.",
          "type": "string",
          "minLength": 1,
          "examples": [
            "Calle de Moreno Nieto 2",
            "100 West Snickerpark Dr"
          ]
        },
        "locality": {
          "description": "City, town or village.",
          "type": "string",
          "minLength": 1,
          "examples": [
            "Madrid",
            "Almería"
          ]
        },
        "region": {
          "description": "Province, state or autonomous community — whatever level sits between locality and country in that country. Free text or an ISO 3166-2 code; both travel to schema.org addressRegion unchanged.",
          "type": "string",
          "minLength": 1,
          "examples": [
            "Comunidad de Madrid",
            "PA"
          ]
        },
        "postalCode": {
          "description": "Postal code, as the local post office writes it. A string, never a number: leading zeros are part of it.",
          "type": "string",
          "minLength": 1,
          "examples": [
            "28005",
            "19019"
          ]
        },
        "country": {
          "description": "ISO 3166-1 alpha-2 code, uppercase (ES, US, MX). A code and not a country name, because the name has one spelling per language: \"España\", \"Spain\" and \"Espagne\" are the same country, and a consumer grouping events by country would see three. Turning a name into a code is a table lookup, not an invention — which is why the spec asks for it here and nowhere else.",
          "type": "string",
          "pattern": "^[A-Z]{2}$",
          "examples": [
            "ES",
            "US"
          ]
        }
      }
    },
    "offers": {
      "description": "Ways of attending, with their price. A list because an event may sell several kinds of ticket, and because the answer to \"is it free?\" has to survive an event that is free for students and paid for everyone else.",
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/offer"
      }
    },
    "offer": {
      "description": "One way of attending: a price, a place to get it, or both. Maps 1:1 onto a schema.org Offer, which is what Google reads to show a price. It models the ticket, not the ticketing: no capacity, no seats left, no per-ticket registration state.",
      "type": "object",
      "properties": {
        "name": {
          "description": "What this ticket is called (\"General admission\", \"Estudiantes\"). Worth writing when there is more than one offer, noise when there is only one.",
          "type": "string",
          "minLength": 1,
          "examples": [
            "General admission",
            "Estudiantes"
          ]
        },
        "price": {
          "description": "Amount per attendee, in `currency`. 0 means free — and it is the ONLY way to say free: an absent offers list means the price is unknown. A number, never text: no currency symbol, no thousands separator, no range and no \"desde\", because the whole reason to publish a price as data is that someone can filter and compare on it. A price that cannot be written as one number is several offers.",
          "type": "number",
          "minimum": 0,
          "examples": [
            0,
            45,
            12.5
          ]
        },
        "currency": {
          "description": "ISO 4217 alpha-3 code, uppercase (EUR, USD, MXN). Required whenever `price` is above 0, and pointless at 0: free is free in every currency, and emitting one there is how Luma ends up publishing a currency for a meetup that costs nothing.",
          "type": "string",
          "pattern": "^[A-Z]{3}$",
          "examples": [
            "EUR",
            "USD"
          ]
        },
        "url": {
          "description": "Where to buy the ticket or register for this particular offer. Distinct from the event's own url: that page describes the event, this one is where money or a seat changes hands. Omit it when registration happens on the event page itself.",
          "type": "string",
          "format": "uri",
          "pattern": "^https?://",
          "examples": [
            "https://devfest-levante.example/2026/entradas"
          ]
        },
        "availability": {
          "description": "Whether this offer can still be taken. NO DEFAULT: absent means unknown, never available — a stale feed that keeps claiming in-stock is worse than one that says nothing. Only the two states an attendee can act on are modelled.",
          "enum": [
            "in-stock",
            "sold-out"
          ],
          "examples": [
            "in-stock",
            "sold-out"
          ]
        },
        "opensAt": {
          "description": "When this offer goes on sale. An INSTANT, with offset or Z — unlike the event's own dates, which are wall clock: a sale opening is a moment a button starts working, not an hour on a poster.",
          "$ref": "#/$defs/instant",
          "examples": [
            "2026-05-01T10:00:00+02:00"
          ]
        },
        "closesAt": {
          "description": "When this offer stops being available. An INSTANT, with offset or Z, for the same reason as opensAt — and because \"23:59\" without an offset is the classic deadline bug.",
          "$ref": "#/$defs/instant",
          "examples": [
            "2026-07-31T23:59:59+02:00"
          ]
        }
      },
      "anyOf": [
        {
          "description": "An offer must carry a price or a link — ideally both. One with neither says nothing that omitting the whole list does not already say, the same rule location follows with venue and onlineUrl.",
          "required": [
            "price"
          ]
        },
        {
          "required": [
            "url"
          ]
        }
      ],
      "allOf": [
        {
          "description": "A non-zero amount without a currency is not a price: 45 is a different thing in EUR, USD and MXN, and a consumer that has to guess will guess its own.",
          "if": {
            "type": "object",
            "required": [
              "price"
            ],
            "properties": {
              "price": {
                "type": "number",
                "exclusiveMinimum": 0
              }
            }
          },
          "then": {
            "type": "object",
            "required": [
              "currency"
            ]
          }
        }
      ]
    },
    "cfp": {
      "description": "An open call for proposals. One per event: unlike organizers, no real producer publishes more than one — the CFP directories that exist (confs.tech, developers.events, Sessionize) all model exactly one link and one deadline. Deliberately small: it says where to submit and until when, not what a submission looks like.",
      "type": "object",
      "required": [
        "url"
      ],
      "properties": {
        "url": {
          "description": "Where proposals are submitted — the form, or the page describing the call. Required: a CFP nobody can find is not a call, and this is the one piece of it that survives export to every format, inside the text.",
          "type": "string",
          "format": "uri",
          "pattern": "^https?://",
          "examples": [
            "https://devfest-levante.example/2026/cfp"
          ]
        },
        "opensAt": {
          "description": "When the call starts accepting proposals. An INSTANT, with offset or Z. Absent means it is already open — a call that has not opened yet is announced, not published.",
          "$ref": "#/$defs/instant",
          "examples": [
            "2026-05-01T00:00:00+02:00"
          ]
        },
        "closesAt": {
          "description": "Deadline for proposals. An INSTANT, with offset or Z, never a bare \"23:59\": which midnight it is is the whole question, and \"anywhere on Earth\" is a real answer (-12:00) that a wall-clock field could not express. Absent means unknown, not open forever — and it is what a consumer needs to answer \"which CFPs are open right now\".",
          "$ref": "#/$defs/instant",
          "examples": [
            "2026-07-15T23:59:59+02:00",
            "2026-07-15T23:59:59-12:00"
          ]
        },
        "coversTravel": {
          "description": "Whether the event covers a selected speaker's travel. NO DEFAULT: absent means unknown, never false. It is here and \"call for sponsors\" is not because this is what a speaker filters on before deciding whether they can afford to submit.",
          "type": "boolean",
          "examples": [
            true
          ]
        },
        "coversAccommodation": {
          "description": "Whether the event covers a selected speaker's accommodation. Same rule as coversTravel: absent means unknown.",
          "type": "boolean",
          "examples": [
            true
          ]
        }
      }
    },
    "source": {
      "type": "object",
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "description": "Name of the origin (e.g. \"Rust Madrid\", \"Meetup\").",
          "type": "string",
          "minLength": 1,
          "examples": [
            "Rust Madrid",
            "Meetup"
          ]
        },
        "url": {
          "description": "Link to the original record, so the data can be verified and corrected upstream.",
          "type": "string",
          "format": "uri",
          "pattern": "^https?://",
          "examples": [
            "https://calendar.example/ics/rust-madrid"
          ]
        },
        "license": {
          "description": "License under which the ORIGIN publishes the data. Constrains what may be republished: declaring a license does not grant rights the origin never gave.",
          "$ref": "#/$defs/license",
          "examples": [
            "CC-BY-4.0"
          ]
        },
        "retrievedAt": {
          "description": "When the data was fetched.",
          "$ref": "#/$defs/instant",
          "examples": [
            "2026-06-01T05:00:00Z"
          ]
        }
      }
    }
  }
}
