# What councils will decide on an issue

> For an advocacy group or membership platform: the agenda items on one issue across a province, when each comes up, and who sits on each council.

After this recipe you can hand your own language model every agenda item on one issue across a province, with the meeting date and the members of each council, each with its link, and ask it to write a briefing for your members.

**For:** an advocacy group, an industry association or a membership platform.

**Uses:** [`GET /v1/agenda-items`](https://data.heytownhall.com/docs/reference/meetings/get-v1-agenda-items) and [`GET /v1/officials`](https://data.heytownhall.com/docs/reference/people-in-office/get-v1-officials). One records request for the agenda items, and one for each town's officials.

## Steps

1. List the agenda items tagged with your issue's topic, across the province, from a date on. [`GET /v1/topics`](https://data.heytownhall.com/docs/reference/topics/get-v1-topics) lists the topic ids.
2. For each town in the results, list its elected officials.
3. Number each record with its link and hand the list to your language model.

An agenda is posted a few days before its meeting, so in production set `FROM` to today's date to see what is coming. The sandbox's meetings end in October 2026, so the example uses `2026-10-01`.

## The code

```python
import os

import requests

BASE = "https://data.heytownhall.com"
HEADERS = {"Authorization": f"Bearer {os.environ['HTD_KEY']}"}
TOPIC = "short_term_rentals"
FROM = "2026-10-01"
QUESTION = "For our members who rent out homes: which councils will discuss short-term rentals, when, and who sits on each council?"

def get(path, params):
    res = requests.get(f"{BASE}{path}", params=params, headers=HEADERS, timeout=30)
    body = res.json()
    if not res.ok:
        raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
    return body["results"]

items = get("/v1/agenda-items", {"topic": TOPIC, "region": "BC", "from": FROM, "limit": 100})
records = [(f"{i['meeting_date']}, {i['jurisdiction_id']}, item {i['number']} {i['title']}. {i['plain_summary'] or i['recommendation']}", i["source_url"]) for i in items]
for town in sorted({i["jurisdiction_id"] for i in items}):
    members = get("/v1/officials", {"jurisdiction": town, "limit": 100})
    names = ", ".join(f"{m['title']} {m['name']}" for m in members)
    records.append((f"Elected officials of {town}: {names}", members[0]["source_url"] if members else ""))

prompt = "\n".join([
    QUESTION,
    "Answer only from these records. Cite each claim by its number and keep its link. If the records do not answer the question, say so.",
    "",
    *(f"[{n}] {text} ({url})" for n, (text, url) in enumerate(records, 1)),
])
print(prompt)  # Send this to your own language model.
```

```js
const BASE = 'https://data.heytownhall.com';
const headers = { Authorization: `Bearer ${process.env.HTD_KEY}` };
const TOPIC = 'short_term_rentals';
const FROM = '2026-10-01';
const QUESTION = 'For our members who rent out homes: which councils will discuss short-term rentals, when, and who sits on each council?';

async function get(path, params) {
  const url = new URL(path, BASE);
  for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
  const res = await fetch(url, { headers });
  const body = await res.json();
  if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
  return body.results;
}

const items = await get('/v1/agenda-items', { topic: TOPIC, region: 'BC', from: FROM, limit: 100 });
const records = items.map((i) => [`${i.meeting_date}, ${i.jurisdiction_id}, item ${i.number} ${i.title}. ${i.plain_summary ?? i.recommendation}`, i.source_url]);
for (const town of [...new Set(items.map((i) => i.jurisdiction_id))].sort()) {
  const members = await get('/v1/officials', { jurisdiction: town, limit: 100 });
  const names = members.map((m) => `${m.title} ${m.name}`).join(', ');
  records.push([`Elected officials of ${town}: ${names}`, members[0]?.source_url ?? '']);
}

const prompt = [
  QUESTION,
  'Answer only from these records. Cite each claim by its number and keep its link. If the records do not answer the question, say so.',
  '',
  ...records.map(([text, url], n) => `[${n + 1}] ${text} (${url})`),
].join('\n');
console.log(prompt); // Send this to your own language model.
```

## What the sandbox prints

```text
For our members who rent out homes: which councils will discuss short-term rentals, when, and who sits on each council?
Answer only from these records. Cite each claim by its number and keep its link. If the records do not answer the question, say so.

[1] 2026-10-10, tolland-township, item 4. Short-term rental licensing bylaw amendments. Staff ask council to consider: short-term rental licensing bylaw amendments. (https://tolland-township.primegov.example/meetings/900023)
[2] 2026-10-07, marlow-county, item 5. Short-term rental licensing bylaw amendments. Staff ask the board to consider: short-term rental licensing bylaw amendments. (https://marlow-county.escribe.example/meetings/900015)
[3] Elected officials of marlow-county: Chair Helena Brightwater, Director Alice Penhallow, Director Owen Castellan, Director Priya Vantongeren, Director Tomas Ferrell (https://marlowcounty.example/council)
[4] Elected officials of tolland-township: Councillor Bea Lindqvist, Councillor Hal Moreton, Councillor Nora Quillfeather, Councillor Sam Okonkwo-Reyes, Mayor Gil Ashdown (https://tolland.example/council)
```

## Going further

- **How each member voted** on the issue before: [`GET /v1/motions`](https://data.heytownhall.com/docs/reference/decisions/get-v1-motions) with the same `topic`, then [`GET /v1/motions/{id}`](https://data.heytownhall.com/docs/reference/decisions/get-v1-motions-id) for each member's recorded vote.
- **The committees each member sits on:** [`GET /v1/council-appointments`](https://data.heytownhall.com/docs/reference/people-in-office/get-v1-council-appointments).
- **Run it every week** with `FROM` set to that day, and send the briefing to your members.
