upsert_spots

Create or update up to 25 spots by your own ref.

POST/v1/guides/{productId}/spots/batch
MCP tool upsert_spotsScope: writeBetaSince 2026-09-30
Markdown

Bulk and idempotent: each item is a create_spot body plus a required ref (your stable key). A spot of this guide with that ref is updated with the fields you send; any other ref creates a spot. One transaction per call, so one bad item rejects the batch and nothing half-lands; the error names the ref. Returns [{ ref, spotId, spotKey, created }] and any warnings, with status 201 even when every item was an update. Re-running the same batch never duplicates spots.

curl -X POST "https://sceniq.earth/api/v1/guides/j5738kd1q9x2m7v4c6b8n0p3r5t1w9y2/spots/batch" \
  -H "Authorization: Bearer $SCENIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "spots": [
    {
      "ref": "yose-tunnel-view",
      "title": "Tunnel View",
      "kicker": "Yosemite Valley",
      "lat": 37.7156,
      "lon": -119.677,
      "mapsUrl": "https://www.google.com/maps/search/?api=1&query=37.7156,-119.677",
      "shortDescription": "El Capitan, Half Dome and Bridalveil Fall in one frame, at the east end of the Wawona Tunnel.",
      "accessMode": "drive_up",
      "customValues": {
        "best_time": "sunset",
        "crowds": "busy"
      }
    },
    {
      "ref": "yose-glacier-point",
      "title": "Glacier Point",
      "kicker": "Yosemite, above the valley",
      "lat": 37.7307,
      "lon": -119.5741,
      "shortDescription": "Half Dome, Vernal Fall and Nevada Fall from nearly 1,000 meters above the valley floor.",
      "accessMode": "drive_up",
      "customValues": {
        "best_time": "sunset",
        "crowds": "packed"
      }
    },
    {
      "ref": "yose-taft-point",
      "title": "Taft Point",
      "kicker": "Yosemite, Glacier Point Road",
      "lat": 37.7125,
      "lon": -119.6049,
      "shortDescription": "Fissures in the granite and a sheer drop to the valley, with El Capitan across the gap.",
      "accessMode": "hike",
      "customValues": {
        "best_time": "sunset",
        "crowds": "busy"
      }
    }
  ]
}'

Path parameters

Body parameters

  • spotsarray of objectsrequired

    Up to 25 items: every create_spot field plus ref (required).

    Show 35 fields
    • titlestringrequired

      Place name as locals know it, e.g. Oeschinensee or Fushimi Inari-taisha. Cannot be empty.

    • kickerstringoptional

      Short eyebrow above the title, e.g. Patagonia, Bernese Oberland or Glacier lake. Empty string clears.

    • latnumberoptional

      Latitude in decimal degrees (-90 to 90). From the creator's material or a cited source.

    • lonnumberoptional

      Longitude in decimal degrees (-180 to 180).

    • mapsUrlstringoptional

      The creator's own map link: https on Google Maps, Apple Maps, Swisstopo, OpenStreetMap or SchweizMobil, or a geo: URI. Empty string clears.

    • shortDescriptionstringoptional

      One or two sentences for the card. Empty string clears.

    • longDescriptionstringoptional

      The full write-up in the creator's voice. Empty string clears.

    • colorstringoptional

      Optional pin color hex, e.g. #0071e3. Empty string clears.

    • cardOrientationstringoptional

      portrait or landscape (photo framing in the viewer); a spot without one reads as landscape.

      portraitlandscape
    • customValuesmap of valuesoptional

      Object keyed by property key (list_properties): text for shortLabel and longSection, a number for number, epoch ms for date, one option id for singleSelect, an array of option ids for checkbox (ids, never labels). Every required property needs a value; keys without a live property are kept unchecked. Sent for an existing spot it replaces the stored object; null for a key clears that value.

    • arrivalArrival or nulloptional

      How to get there, full replacement, null clears: { byCar?: { directions, parking, walkToSpotMin, walkRoundTrip? (the minutes are there and back), none? (no car access), note? }, byTrainBus?: { route, walkFromStopMin, walkRoundTrip?, none? (no public transport, which reads differently from an empty field), note? }, byBoat?, byAir?: { airports? (IATA or ICAO codes), route?, minutes?, operatorUrl?, luggageKg?, none?, note? }, onFoot?: { route?, minutes?, roundTrip?, none?, note? }, byBike?: { same }, bestTime?, operating?, approaches?: [{ label (Argentine side), country? (AR), legs?: [{ mode: car|bus|train|tram|metro|cable_car|funicular|boat|ferry|plane|helicopter|shuttle|jeep|walk|bike|taxi|other, from?, to?, minutes?, roundTrip?, price?, payment?, note? }], hours?, entry?, note?, lat?, lon? }] (at most 6, for spots with several ways in or chains of legs), rules?: [{ text, kind?: reservation|permit|guide|registration|no_independent_travel|vehicle|other, months? [1-12] (shown only then), appliesTo?: car|all, bookingOpens?, sourceUrl? }] }.

      Show 10 fieldsof Arrival
      • byCarobjectoptional

        Driving and parking.

        Show 6 fields
        • directionsstringoptional

          The drive in words, at most 2,000 characters.

        • parkingstringoptional

          Where to park, at most 1,000 characters.

        • walkToSpotMinnumberoptional

          Minutes on foot from the car to the spot.

        • walkRoundTripbooleanoptional

          true when walkToSpotMin is there and back.

        • nonebooleanoptional

          true when the spot has no car access.

        • notestringoptional

          One more line about driving, at most 500 characters.

      • byTrainBusobjectoptional

        Public transport.

        Show 5 fields
        • routestringoptional

          The connection in words, at most 2,000 characters.

        • walkFromStopMinnumberoptional

          Minutes on foot from the stop to the spot.

        • walkRoundTripbooleanoptional

          true when walkFromStopMin is there and back.

        • nonebooleanoptional

          true when there is no public transport.

        • notestringoptional

          One more line about public transport, at most 500 characters.

      • byBoatstringoptional

        The boat connection in words, at most 2,000 characters.

      • byAirobjectoptional

        Flights or helicopter.

        Show 7 fields
        • airportsarray of stringsoptional

          Up to 8 IATA or ICAO codes (LUA, VNLK).

        • routestringoptional

          The flight in words, at most 2,000 characters.

        • minutesnumberoptional

          Flight time in minutes.

        • operatorUrlstringoptional

          The operator's page (http or https).

        • luggageKgnumberoptional

          The luggage limit in kilograms (1 to 200).

        • nonebooleanoptional

          true when there is no air access.

        • notestringoptional

          One more line about flying, at most 500 characters.

      • onFootArrivalModeBlockoptional

        Walking in.

        Show 5 fieldsof ArrivalModeBlock
        • routestringoptional

          The way in words, at most 2,000 characters.

        • minutesnumberoptional

          Minutes it takes.

        • roundTripbooleanoptional

          true when minutes is there and back.

        • nonebooleanoptional

          true when this mode is not possible.

        • notestringoptional

          One more line, at most 500 characters.

      • byBikeArrivalModeBlockoptional

        Cycling in.

        Show 5 fieldsof ArrivalModeBlock
        • routestringoptional

          The way in words, at most 2,000 characters.

        • minutesnumberoptional

          Minutes it takes.

        • roundTripbooleanoptional

          true when minutes is there and back.

        • nonebooleanoptional

          true when this mode is not possible.

        • notestringoptional

          One more line, at most 500 characters.

      • bestTimestringoptional

        The best time to go in words, at most 1,000 characters.

      • operatingstringoptional

        When the access runs (seasons, hours of a road or a ferry), at most 2,000 characters.

      • approachesarray of objectsoptional

        Up to 6 ways in, for spots with several approaches or chains of legs (a flight, then a jeep, then a walk).

        Show 8 fields
        • labelstringrequired

          The approach's name, at most 80 characters (Argentine side).

        • countrystringoptional

          ISO 3166-1 alpha-2 code of the country it starts in (AR).

        • legsarray of objectsoptional

          Up to 12 legs in order.

          Show 8 fields
          • modestringrequired

            car, bus, train, tram, metro, cable_car, funicular, boat, ferry, plane, helicopter, shuttle, jeep, walk, bike, taxi or other.

            carbustraintrammetrocable_carfunicularboatferryplanehelicoptershuttlejeepwalkbiketaxiother
          • fromstringoptional

            Where the leg starts, at most 120 characters.

          • tostringoptional

            Where the leg ends, at most 120 characters.

          • minutesnumberoptional

            Minutes the leg takes.

          • roundTripbooleanoptional

            true when minutes is there and back.

          • pricestringoptional

            The leg's price in words, at most 80 characters.

          • paymentstringoptional

            How the leg is paid, at most 80 characters.

          • notestringoptional

            One line for the leg, at most 300 characters.

        • hoursstringoptional

          When the approach runs, at most 300 characters.

        • entrystringoptional

          What entry takes on this side, at most 300 characters.

        • notestringoptional

          One more line, at most 500 characters.

        • latnumberoptional

          Latitude of the approach's start.

        • lonnumberoptional

          Longitude of the approach's start.

      • rulesarray of AccessRuleoptional

        Up to 20 access rules (reservations, permits), each shown in its months.

        Show 6 fieldsof AccessRule
        • textstringrequired

          The rule in the creator's words, at most 300 characters.

        • kindstringoptional

          reservation, permit, guide, registration, no_independent_travel, vehicle or other.

          reservationpermitguideregistrationno_independent_travelvehicleother
        • monthsarray of numbersoptional

          The months the rule applies (1 to 12); the apps show it only then. Absent means all year.

        • appliesTostringoptional

          car for a rule that only binds drivers (a timed entry for vehicles), all for everyone.

          carall
        • bookingOpensstringoptional

          When booking opens, in words, at most 100 characters (90 days ahead at 07:00).

        • sourceUrlstringoptional

          The page that states the rule (http or https).

    • mapLinksarray of MapLinkoptional

      Extra map links [{ label, url }] (https or an allowlisted map link), drawn as map pills. [] clears. For official, ticket or status pages use links instead.

      Show 2 fieldsof MapLink
      • labelstringrequired

        The pill text, at most 40 characters.

      • urlstringrequired

        An https page or an allowlisted map link.

    • imagesEnabledbooleanoptional

      false hides the photo section from buyers; photos stay stored.

    • customPropsEnabledbooleanoptional

      false hides custom properties from buyers; values stay stored.

    • tripFactsTripFacts or nulloptional

      The planning stats bar, null clears: { elevationM? (meters; 0 is a stated sea level, absent is unknown), elevationRefersTo?: viewpoint|summit|ground|water|trailhead, elevationSource?, busyness? (1 quiet to 5 packed, shown as Crowdedness), bestSeason? }.

      Show 5 fieldsof TripFacts
      • elevationMnumberoptional

        Elevation in meters (-500 to 9,000). 0 is a stated sea level; absent is unknown.

      • elevationRefersTostringoptional

        What the elevation measures: viewpoint, summit, ground, water or trailhead.

        viewpointsummitgroundwatertrailhead
      • elevationSourcestringoptional

        Where the number comes from, at most 300 characters.

      • busynessnumberoptional

        Crowds from 1 (quiet) to 5 (packed), shown as Crowdedness.

      • bestSeasonstringoptional

        The best season in words, at most 200 characters.

    • accessModestring or nulloptional

      How visitors reach the spot, shown as Accessibility: drive_up, short_walk, hike, multi_day, boat, cable_car, train, flight, tour_only, aerial_only. null clears. Without it the cell shows the easiest required route's grade, or Drive-up only when arrival.byCar describes a drive.

      drive_upshort_walkhikemulti_dayboatcable_cartrainflighttour_onlyaerial_only
    • pinLabelstringoptional

      What the main pin marks for a spot that is an area or a line, shown as Pinned: <label> (Ti Top viewpoint). Empty string clears.

    • pinsarray of SpotPinoptional

      Extra points, full replacement ([] clears), at most 12: [{ kind: viewpoint|entrance|gate|trailhead|parking|stop|pier|summit|other, label, lat, lon, months? [1-12] (only reachable then), note? }]. Each gets its own map links and a place on the web map.

      Show 6 fieldsof SpotPin
      • kindstringrequired

        viewpoint, entrance, gate, trailhead, parking, stop, pier, summit or other.

        viewpointentrancegatetrailheadparkingstoppiersummitother
      • labelstringrequired

        What buyers read next to the pin, at most 80 characters (South rim viewpoint).

      • latnumberrequired

        Latitude in decimal degrees (-90 to 90), from the creator's material or a cited source.

      • lonnumberrequired

        Longitude in decimal degrees (-180 to 180).

      • monthsarray of numbersoptional

        The months the point is reachable (1 to 12). Absent means all year.

      • notestringoptional

        One line of context, at most 300 characters.

    • linksarray of GuideLinkoptional

      Links with a purpose, full replacement ([] clears), at most 12: [{ kind: official|website|tickets|booking|status|timetable|tide|listing|social|authority|app|other, label?, url? (http or https; http is accepted with a warning), value? (a channel that is not a URL, e.g. WeChat mini program: Li River boats), note? }]. Each needs a url or a value.

      Show 5 fieldsof GuideLink
      • kindstringrequired

        What the link is for. official and website are the place's own pages; tickets and booking sell entry or rooms; status, timetable and tide carry live conditions; listing, social, authority and app cover the rest.

        officialwebsiteticketsbookingstatustimetabletidelistingsocialauthorityappother
      • labelstringoptional

        The link text buyers see, at most 60 characters. Without it the apps name the link by its kind.

      • urlstringoptional

        An http or https page. http is accepted with a warning.

      • valuestringoptional

        A channel that is not a URL, at most 200 characters, for example a WeChat mini program and its search term.

      • notestringoptional

        One line of context shown under the link, at most 300 characters.

    • statusStatusNotice or nulloptional

      Closure or works notice, null clears: { state: open|partly_closed|closed|reopening, note?, since?, until? (the day the state is expected to end; buyers stop seeing the notice after it), sourceUrl?, checkedOn? }. reopening with until means reopens on that day.

      Show 6 fieldsof StatusNotice
      • statestringrequired

        open, partly_closed, closed or reopening. reopening with until means it reopens on that day.

        openpartly_closedclosedreopening
      • notestringoptional

        What is closed or changed, in the creator's words, at most 500 characters.

      • sincestringoptional

        The day the state began (YYYY-MM-DD).

      • untilstringoptional

        The day the state is expected to end (YYYY-MM-DD). Must not be before since.

      • sourceUrlstringoptional

        The page that announced it (http or https).

      • checkedOnstringoptional

        The day the value was last checked against its source (YYYY-MM-DD).

    • feesarray of Feeoptional

      Fees and tickets, full replacement ([] clears), at most 30: [{ label, amount? (major units, e.g. 40 or 12.5; needs currency), currency? (EUR), free?, seeOfficial? (a price left to the official page on purpose), per?: person|vehicle|night|group|entry|day|hour, audience?: all|adult|child|foreign|domestic|resident|student|senior, paidWhere?: online|on_site|in_tour, payment?: cash_only|card_only|cash_or_card, note?, validFrom?, validUntil?, sourceUrl?, checkedOn? }]. Buyers see the amount with an as-of date; readiness flags rows past validUntil or unchecked for a year.

      Show 14 fieldsof Fee
      • labelstringrequired

        What the fee is for, at most 80 characters (Adult entry, Car park, Boat transfer).

      • amountnumberoptional

        The price in major units (40, 12.5). Needs currency. Absent when the fee is free or left to the official page.

      • currencystringoptional

        ISO 4217 code (USD, EUR, ARS). Required with amount.

      • freebooleanoptional

        true for no charge. Cannot be combined with an amount.

      • seeOfficialbooleanoptional

        true for a price the guide leaves to the official page on purpose (it changes too often to copy).

      • perstringoptional

        What one amount pays for: person, vehicle, night, group, entry, day or hour.

        personvehiclenightgroupentrydayhour
      • audiencestringoptional

        Who pays this rate: all, adult, child, foreign, domestic, resident, student or senior.

        alladultchildforeigndomesticresidentstudentsenior
      • paidWherestringoptional

        Where it is paid: online, on_site or in_tour (part of a tour price).

        onlineon_sitein_tour
      • paymentstringoptional

        How it can be paid: cash_only, card_only or cash_or_card.

        cash_onlycard_onlycash_or_card
      • notestringoptional

        One line of context, at most 300 characters (free on the first Sunday of the month).

      • validFromstringoptional

        The first day the price applies (YYYY-MM-DD).

      • validUntilstringoptional

        The last day the price applies (YYYY-MM-DD). Readiness flags the row after it.

      • sourceUrlstringoptional

        The page the price comes from (http or https).

      • checkedOnstringoptional

        The day the value was last checked against its source (YYYY-MM-DD).

    • scheduleSchedule or nulloptional

      Structured opening hours, null clears; the free-text hours stay the fallback: { hours?: [{ from? (MM-DD), to? (MM-DD, may wrap over the new year), weekdays? [1-7], closed?, open? (HH:MM or sunrise|sunset), close? (HH:MM or sunrise|sunset), openOffsetMin?, closeOffsetMin? (minutes around a sun anchor, -60 = an hour before), lastEntry? (HH:MM), leaveBy? (HH:MM), note? }], specialDays?: [{ date? or rule? (first Sunday of the month), closed?, open?, close?, note? }], slots?: { first, last, everyMin, cap?, note? }, validFrom?, validUntil?, sourceUrl?, checkedOn?, note? }. The last matching band wins, so list the year-round band first and exceptions after it. The apps show today's hours in the spot's time zone.

      Show 8 fieldsof Schedule
      • hoursarray of HoursBandoptional

        Up to 24 bands; the last matching band wins.

        Show 11 fieldsof HoursBand
        • fromstringoptional

          First day of the band as MM-DD. Absent means the start of the year.

        • tostringoptional

          Last day of the band as MM-DD. May wrap over the new year (from 11-01 to 03-31).

        • weekdaysarray of numbersoptional

          The weekdays it covers, 1 (Monday) to 7 (Sunday). Absent means every day.

        • closedbooleanoptional

          true when the place is closed in this band. Otherwise open and close are required.

        • openstringoptional

          Opening time as HH:MM, or sunrise or sunset.

        • closestringoptional

          Closing time as HH:MM, or sunrise or sunset.

        • openOffsetMinnumberoptional

          Minutes around a sun anchor for open (-240 to 240; -60 is an hour before sunrise).

        • closeOffsetMinnumberoptional

          Minutes around a sun anchor for close (-240 to 240).

        • lastEntrystringoptional

          Last entry as HH:MM.

        • leaveBystringoptional

          The time visitors must be out as HH:MM.

        • notestringoptional

          One line for the band, at most 200 characters.

      • specialDaysarray of objectsoptional

        Up to 40 days that differ from the bands: a date or a rule, closed or with their own times.

        Show 6 fields
        • datestringoptional

          The day (YYYY-MM-DD). Give a date or a rule.

        • rulestringoptional

          A recurring day in words, at most 120 characters (first Sunday of the month).

        • closedbooleanoptional

          true when the place is closed that day.

        • openstringoptional

          Opening time that day as HH:MM, or sunrise or sunset.

        • closestringoptional

          Closing time that day as HH:MM, or sunrise or sunset.

        • notestringoptional

          One line for the day, at most 200 characters.

      • slotsobjectoptional

        Timed entry: entries every everyMin minutes from first to last.

        Show 5 fields
        • firststringrequired

          The first entry time as HH:MM.

        • laststringrequired

          The last entry time as HH:MM.

        • everyMinnumberrequired

          Minutes between entries.

        • capnumberoptional

          People per slot, when the source states it (1 to 100,000).

        • notestringoptional

          One line about booking a slot, at most 200 characters.

      • validFromstringoptional

        The first day these hours apply (YYYY-MM-DD).

      • validUntilstringoptional

        The last day these hours apply (YYYY-MM-DD).

      • sourceUrlstringoptional

        The page the hours come from (http or https).

      • checkedOnstringoptional

        The day the value was last checked against its source (YYYY-MM-DD).

      • notestringoptional

        A note for the whole schedule, at most 500 characters.

    • featuresarray of SpotFeatureoptional

      Parts of the site with their own rules (a monastery's closing day, a ticketed skyway), full replacement, at most 12: [{ name, note?, hours?, entry?, season?, fees? (as fees), status? (as status), url? }].

      Show 8 fieldsof SpotFeature
      • namestringrequired

        The part's name, at most 80 characters.

      • notestringoptional

        What to know about it, at most 500 characters.

      • hoursstringoptional

        Its hours in words, at most 200 characters.

      • entrystringoptional

        Its entry rule or price in words, at most 200 characters.

      • seasonstringoptional

        When it is open in words, at most 200 characters.

      • feesarray of Feeoptional

        Its own fees, as the spot's fees.

        Show 14 fieldsof Fee
        • labelstringrequired

          What the fee is for, at most 80 characters (Adult entry, Car park, Boat transfer).

        • amountnumberoptional

          The price in major units (40, 12.5). Needs currency. Absent when the fee is free or left to the official page.

        • currencystringoptional

          ISO 4217 code (USD, EUR, ARS). Required with amount.

        • freebooleanoptional

          true for no charge. Cannot be combined with an amount.

        • seeOfficialbooleanoptional

          true for a price the guide leaves to the official page on purpose (it changes too often to copy).

        • perstringoptional

          What one amount pays for: person, vehicle, night, group, entry, day or hour.

          personvehiclenightgroupentrydayhour
        • audiencestringoptional

          Who pays this rate: all, adult, child, foreign, domestic, resident, student or senior.

          alladultchildforeigndomesticresidentstudentsenior
        • paidWherestringoptional

          Where it is paid: online, on_site or in_tour (part of a tour price).

          onlineon_sitein_tour
        • paymentstringoptional

          How it can be paid: cash_only, card_only or cash_or_card.

          cash_onlycard_onlycash_or_card
        • notestringoptional

          One line of context, at most 300 characters (free on the first Sunday of the month).

        • validFromstringoptional

          The first day the price applies (YYYY-MM-DD).

        • validUntilstringoptional

          The last day the price applies (YYYY-MM-DD). Readiness flags the row after it.

        • sourceUrlstringoptional

          The page the price comes from (http or https).

        • checkedOnstringoptional

          The day the value was last checked against its source (YYYY-MM-DD).

      • statusStatusNoticeoptional

        Its own closure notice.

        Show 6 fieldsof StatusNotice
        • statestringrequired

          open, partly_closed, closed or reopening. reopening with until means it reopens on that day.

          openpartly_closedclosedreopening
        • notestringoptional

          What is closed or changed, in the creator's words, at most 500 characters.

        • sincestringoptional

          The day the state began (YYYY-MM-DD).

        • untilstringoptional

          The day the state is expected to end (YYYY-MM-DD). Must not be before since.

        • sourceUrlstringoptional

          The page that announced it (http or https).

        • checkedOnstringoptional

          The day the value was last checked against its source (YYYY-MM-DD).

      • urlstringoptional

        Its own page (http or https).

    • restrictionsarray of Restrictionoptional

      Rules on site, full replacement, at most 20: [{ kind: no_photo|wide_only|no_drone|no_entry|no_stopping|no_parking|other, label, note?, lat?, lon?, radiusM?, line? ([[lon, lat], ...] for a stretch of road), sourceUrl? }].

      Show 8 fieldsof Restriction
      • kindstringrequired

        no_photo, wide_only (shown as Wide shots only), no_drone, no_entry, no_stopping, no_parking or other.

        no_photowide_onlyno_droneno_entryno_stoppingno_parkingother
      • labelstringrequired

        The rule as buyers read it, at most 80 characters.

      • notestringoptional

        More context, at most 300 characters.

      • latnumberoptional

        Latitude of the point the rule applies around.

      • lonnumberoptional

        Longitude of the point the rule applies around.

      • radiusMnumberoptional

        The radius around lat and lon in meters (1 to 100,000).

      • linearray of array of numbersoptional

        A stretch of road or trail instead of a point: 2 to 200 [lon, lat] positions.

      • sourceUrlstringoptional

        The page that states the rule (http or https).

    • eventsarray of SpotEventoptional

      Recurring or one-off events worth timing a visit for, full replacement, at most 12: [{ name, when (in words: every night at 19:45 and 20:45), dates? [YYYY-MM-DD] (the apps show the next one), months?, url?, free?, note? }].

      Show 7 fieldsof SpotEvent
      • namestringrequired

        The event's name, at most 80 characters.

      • whenstringrequired

        When, in words, at most 200 characters (every night at 19:45 and 20:45).

      • datesarray of stringsoptional

        Exact dates when known (YYYY-MM-DD, up to 60). Readiness lists events whose dates have all passed.

      • monthsarray of numbersoptional

        The months it happens (1 to 12).

      • urlstringoptional

        The event's page (http or https).

      • freebooleanoptional

        true when it costs nothing to watch.

      • notestringoptional

        One line of context, at most 300 characters.

    • foodFoodNote or nulloptional

      Why the spot lists no food places, null clears: { status: included (meals come with the lodge or boat) | none_nearby | bring_your_own, note? }.

      Show 2 fieldsof FoodNote
      • statusstringrequired

        included (meals come with the lodge or boat), none_nearby, or bring_your_own.

        includednone_nearbybring_your_own
      • notestringoptional

        One line of context, at most 300 characters.

    • areaIdstringoptional

      The area (park, island, region) the spot sits in, from set_area; its rules, fees and links show on the spot. Empty string clears.

    • timeZonestringoptional

      IANA zone (America/Phoenix). Derived from the pin on every write; set it only when the lookup is wrong near a border. Empty string hands it back to the pin.

    • customNotesmap of stringsoptional

      One short note per custom property value, keyed by property key, shown next to the value (walls 40 EUR, morning from viewpoint 7). Full replacement ({} clears).

    • sourcesarray of SourceRefoptional

      Research evidence for reviewers, never shown to buyers; full replacement, at most 40: [{ url? or title?, kind?: page|api|archive|document|other, capturedOn?, note?, supports? (which facts it backs, e.g. fees, arrival.operating, lat/lon), conflict? (true when it disagrees with another source on those facts) }].

      Show 7 fieldsof SourceRef
      • urlstringoptional

        The page, API or archive copy the facts come from (http or https).

      • titlestringoptional

        A name for a source without a URL (a park leaflet, a phone call with the operator), at most 200 characters.

      • kindstringoptional

        page, api, archive, document or other.

        pageapiarchivedocumentother
      • capturedOnstringoptional

        The day the source was read (YYYY-MM-DD).

      • notestringoptional

        What the source says or why it was chosen, at most 500 characters.

      • supportsarray of stringsoptional

        Which facts it backs, up to 12 entries of at most 60 characters (fees, arrival.operating, lat/lon).

      • conflictbooleanoptional

        true when this source disagrees with another on the facts it backs. Say which one the value follows in note or reviewNotes.

    • reviewNotesstringoptional

      Notes for the creator's review (why a value was chosen, what could not be verified). Never shown to buyers; get_guide_readiness lists them. Empty string clears.

    • factsCheckedOnstringoptional

      The day the facts were last checked against their sources (YYYY-MM-DD). Empty string clears.

    • reviewBystringoptional

      The day the facts need a new check (YYYY-MM-DD), e.g. when a season, fare or timetable runs out. get_guide_readiness and get_publish_status warn once it has passed. Empty string clears.

    • siteChangeSiteChange or nulloptional

      The day something at the spot changed (a bridge replaced, boats banned), null clears: { on (YYYY-MM-DD), note? }. Readiness flags photos captured before it.

      Show 2 fieldsof SiteChange
      • onstringrequired

        The day of the change (YYYY-MM-DD). Required.

      • notestringoptional

        What changed, at most 300 characters.

    • refstringrequired

      Required: your own stable key for the spot, 1 to 80 characters (letters, digits, dot, underscore, colon or hyphen, a letter or digit first), once per batch. A spot of this guide with that ref is updated with the fields sent; any other ref creates a spot.

Returns

201 Created with this object.

  • productIdstring · guide idrequired

    The guide's id.

  • creatednumberrequired

    Spots created.

  • updatednumberrequired

    Spots updated.

  • resultsarray of objectsrequired

    One result per item, in order.

    Show 4 fields
    • refstringrequired

      Your ref.

    • spotIdstring · spot idrequired

      The spot's id.

    • spotKeystringrequired

      The spot's key.

    • createdbooleanrequired

      true when this call created the spot.

  • warningsarray of stringsoptional

    Notes about things the write accepted but you should look at (a plain http link). Present only when there are any.

Response 201
{
  "productId": "j5738kd1q9x2m7v4c6b8n0p3r5t1w9y2",
  "created": 3,
  "updated": 0,
  "results": [
    {
      "ref": "yose-tunnel-view",
      "spotId": "k17bc3xz8h6f4d2s0q9w7e5r3t1y2v4j",
      "spotKey": "01M2Z1G0Y0R2K5P8S1V4X7Z0C3",
      "created": true
    },
    {
      "ref": "yose-glacier-point",
      "spotId": "k1736b344br1c7m8qk8ks4sfndxyyftj",
      "spotKey": "01M3H2AWH0F9PB64V7QB8QS0MC",
      "created": true
    },
    {
      "ref": "yose-taft-point",
      "spotId": "k17hdgc1c8j423kgq30t9x5hyv1tv8yb",
      "spotKey": "01M3H2AXG8GB8YNF4FCEFARFN5",
      "created": true
    }
  ]
}

Errors

Every error is { error: { code, message } }. Act on the code.

  • 400invalid_argumentA required field is missing, a field is unknown, or a value does not match its type (the Convex validator's path and value are in the message).
  • 400invalid_requestA writer refused a value: a map link on a host that is not allowed, a title that is too long, a date that does not exist, too many items in a list. The message is the writer's own sentence.
  • 401unauthenticatedThe request carries no key. Send it as Authorization: Bearer sk_sceniq_... (or X-Api-Key).
  • 401invalid_api_keyThe key is unknown, revoked or expired.
  • 403api_scope_requiredA read-only key called a write operation, or a key without the publish option called publish_changes.
  • 403forbiddenThe key belongs to a creator account that is no longer active.
  • 404not_foundThe id does not exist or belongs to another creator. The two answers are identical on purpose, so ids never reveal what exists.
  • 413payload_too_largeA JSON body over 2 MB, an image over 6 MB, a batch upload over 19 MB, or an MCP message over 2 MB.
  • 429rate_limitedOver a limit: requests per key per minute, per creator per hour, uploads per key per minute, or check_links per guide per hour.
  • 500internal_errorAn unexpected failure. The message is hidden on purpose.