Docs
BlogFor builders

Spend your agent's reasoning on the family, not on event pages

A how-to for agent builders: instead of paying a high-reasoning model to parse every event page and then wondering if it read it right, ask for typed fields, trim the payload, check the listing's own last-confirmed field, and save the reasoning for fitting the options to the family.

A developer at a laptop with tidy sorted cards, crumpled flyers pushed aside, while a family plans an outing through the window.

Your agent gets a question: what is a good thing to do Saturday morning with a three-year-old? It finds three promising links, fetches each page, and hands every one to your most capable model with the same instruction. Pull out the date, the time, the price, the ages, and whether registration is open.

It works, mostly. It also costs you serious reasoning on every page, every time, and at the end you still do not know whether the model read it right. The page said "Saturdays" and the model said the third. The price on the page belonged to the older group.

Tomorrow, someone else asks the same question and you pay for the same pages again.

1. Where does the reasoning actually go?

Three places, and none of them is about the family.

  • Reading: turning prose, images of flyers and widget markup into fields.
  • Checking: wondering whether the reading was right.
  • Repeating: doing it again next time, and again as the date approaches.

What you actually wanted from your best model is judgment: this option is at nap time, that one is fine for the baby, this one is free and close. Reading a page is not that.

2. What can you skip?

Ask for the fields directly. These are all on the event record, and a value the organizer did not state comes back null, not guessed:

The family needsRead this
Whenstarts_local (local wall-clock time), has_time, has_date, timezone
How muchprice.is_free, price.min, price.max
For whomages.min, ages.max
Whereplace.name, location.street_address, location.neighborhood
How to sign upregistration_url, registration_required, is_sold_out
Can I lean on itverified_at

Classes and camps have their own equivalents (schedule, tuition, sessions) in the same spirit. For a repeating event, occurrence.next_starts_local is the one to read, since starts_local is where the series started.

3. How do you keep the payload small?

Ask for less. The fields parameter keeps only the paths you name (id and type always stay):

GET/v1/philadelphia/eventsRequest
  • from2026-10-03
  • to2026-10-04
  • age3
  • limit5
  • fieldsname,starts_local,has_time,place.name,price.is_free,registration_url,verified_at
curl -G -H "X-API-Key: $TJ_KEY" https://api.tailend.dev/v1/philadelphia/events \
  --data-urlencode "from=2026-10-03" --data-urlencode "to=2026-10-04" \
  --data-urlencode "age=3" --data-urlencode "limit=5" \
  --data-urlencode "fields=name,starts_local,has_time,place.name,price.is_free,registration_url,verified_at"
ResponseSample
{
  "data": [
    {
      "id": "2f3c9d4e-3d2b-4b7d-9a3e-1b2c3d4e5f60",
      "type": "event",
      "name": "Harvest Storytime",
      "starts_local": "2026-10-03T10:30:00",
      "has_time": true,
      "place": { "name": "Maple Street Library" },
      "price": { "is_free": true },
      "registration_url": null,
      "verified_at": "2026-10-02T14:05:00Z"
    }
  ]
}

That is a handful of lines per candidate, not a page. The event and place are invented for illustration. If you need the full place, add expand=place.

4. How do you know it was read correctly without reading it again?

Do not re-read the page with a bigger model. Use what the listing says about itself.

Every record carries verified_at, the last time it was confirmed against the organizer's own page (or null if it never was). If you want to show your own trust signal, ask for include=source, which adds the organizer's page URL and the outcome of the last check: clear, changed, flagged, unverifiable, unreachable or gone.

Some notes:

  • include=source is a paid add-on. Without it, the request returns 403 include_not_permitted. Add include_mode=lenient during development and the same request succeeds without the block, with the omission recorded in notices[].
  • Branch on the check outcome, not only on the timestamp. A listing can be confirmed recently and still come back unverifiable.
  • Send the parent to the organizer's own page (source.url) for the final look at the thing they are about to spend money on.

5. What if you ask again tomorrow?

Every response carries an ETag. Send it back as If-None-Match and an unchanged answer is a 304 with no body:

GET/v1/events/2f3c9d4e-3d2b-4b7d-9a3e-1b2c3d4e5f60Request
  • If-None-MatchW/"TD64uXep9BDu_tA9n6OF_Zwl3IA"
curl -i -H "X-API-Key: $TJ_KEY" -H 'If-None-Match: W/"TD64uXep9BDu_tA9n6OF_Zwl3IA"' \
  https://api.tailend.dev/v1/events/2f3c9d4e-3d2b-4b7d-9a3e-1b2c3d4e5f60

A 304 still counts as a request against your limits, but it does not cost you a reasoning pass.

6. So where does the reasoning go instead?

To the part only your agent can do: fitting the options to this family. Two kids with different ages. A nap at one. A stroller and a bus. A budget. A parent who said last week that they were tired of the museum. That is the conversation your users came for, and it is where the expensive model earns its keep.

Why this matters

Cost is the visible reason. The less visible one is confidence. A model that reads a page and returns a plausible answer is very good at sounding right. Typed fields with an honest "last confirmed" beside them let your agent be sure about the boring facts and spend its doubt on the interesting ones.

How Tailend helps

Tailend reads events, classes, camps and places from the organizers' own pages and calendars into consistent records, so your agent starts from fields instead of prose. Each record says when it was last confirmed, and a value that is not stated is null rather than a guess. We built it after hitting this exact problem running TinyJawns, a family guide in Philadelphia. We start there and are working outward from there.

All posts