{"data":[{"name":"discover_events","version":1,"kind":"read","auth":"anonymous","scopes":[],"summary":"Search public events: organizers' own published events on Turnout first, then the imported catalogue.","description":"Searches two sources and returns them in one list, each row tagged with `origin`. \"turnout\" rows are events an organizer published on Turnout, read live from Turnout's own database — the primary source. \"discover\" rows are the imported third-party catalogue (San Francisco), subject to its own licence. Own events are listed first. Draft, cancelled, private and fixture events are never returned by either source, so a row an agent sees is a row its public page shows. Fields Turnout does not record (end time, address, city, price, category for own events) are null with a notice, never guessed.","whenToUse":"Use when you need to know what is happening: to find an organizer's published event to offer a user, to find comparable events, to check whether a date is busy, or to answer a question about a place, category or timeframe.","notFor":["Attendance, conversion or demand numbers — neither public source carries them. Use the credentialed analytics tools.","One known slug — use get_event."],"keywords":["search","catalogue","what's on","things to do","events near","listing","find events","own events","published"],"costClass":"internal","idempotency":"optional","sideEffects":[],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"query":{"type":"string","minLength":2,"maxLength":120},"city":{"type":"string","minLength":2,"maxLength":60},"neighbourhood":{"type":"string","minLength":2,"maxLength":60},"category":{"type":"string","minLength":2,"maxLength":60},"dateFrom":{"type":"string","minLength":4,"maxLength":40,"description":"ISO 8601 date or timestamp, e.g. 2026-12-01 or 2026-12-01T19:00:00Z"},"dateTo":{"type":"string","minLength":4,"maxLength":40,"description":"ISO 8601 date or timestamp, e.g. 2026-12-01 or 2026-12-01T19:00:00Z"},"free":{"type":"boolean"},"mode":{"type":"string","enum":["in_person","online"]},"sort":{"type":"string","enum":["soonest","latest"]},"source":{"type":"string","enum":["both","turnout","catalogue"]},"page":{"type":"integer","minimum":1,"maximum":500},"pageSize":{"type":"integer","minimum":1,"maximum":100,"description":"Rows per page (1-100)."}},"additionalProperties":false},"examples":[{"input":{"query":"pottery","dateFrom":"2026-12-01","pageSize":10},"note":"Find pottery events in December."},{"input":{"city":"Oakland","category":"Music","sort":"soonest"}},{"input":{"source":"turnout"},"note":"Only events organizers published on Turnout."}],"path":"/api/agent/v1/tools/discover_events"},{"name":"get_event","version":1,"kind":"read","auth":"anonymous","scopes":[],"summary":"One event by slug: this organization's own event with a credential, else a published public event.","description":"Resolution order: (1) the caller's OWN event at any status, when a credential for its organization is presented — returned with registration counts, capacity, fill rate and attendance rate; (2) a published event hosted on Turnout, anonymous; (3) the imported third-party catalogue. Anything else is a 404 — the same answer for 'no such event' and 'not yours', so a private event cannot be discovered by guessing its slug.","whenToUse":"Use when you already have a slug and need that event's details, either from the caller's own account or from the public sources.","notFor":["Searching — use discover_events.","Analytics over time — use event_analytics."],"keywords":["event details","by slug","one event","event by id","single"],"costClass":"internal","idempotency":"optional","sideEffects":[],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":200,"description":"Event slug, from discover_events or the dashboard."}},"additionalProperties":false,"required":["slug"]},"examples":[{"input":{"slug":"sunday-pottery-workshop"}}],"path":"/api/agent/v1/tools/get_event"},{"name":"get_organizer","version":1,"kind":"read","auth":"anonymous","scopes":[],"summary":"Public organizer profile from the catalogue: name and upcoming events.","description":"Reads an organizer profile exactly as the public page publishes it: the name, the count of upcoming events and those events.","whenToUse":"Use to enrich an event with who runs it, or to see everything one organizer has listed.","notFor":["Events by topic — use discover_events."],"keywords":["organizer","who runs","host","promoter","profile"],"costClass":"internal","idempotency":"optional","sideEffects":[],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":200}},"additionalProperties":false,"required":["slug"]},"examples":[{"input":{"slug":"bay-area-potters"}}],"path":"/api/agent/v1/tools/get_organizer"},{"name":"check_availability","version":1,"kind":"read","auth":"credential","scopes":["events:read"],"summary":"Reports whether the organization already has an event in a slot, and where the next free one is.","description":"Checks one proposed start time against the organization's own calendar and, when the slot is taken, returns the next free alternatives computed with the same rule. Turnout stores a start time per event (no end time), so conflicts are computed with an assumed duration that the response states.","whenToUse":"Use before proposing or creating a date, or when a caller asks whether a slot is free.","notFor":["Checking whether a third-party listing exists in the catalogue — that is discover_events."],"keywords":["availability","conflict","double booking","free slot","calendar clash","when can we"],"costClass":"internal","idempotency":"optional","sideEffects":[],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"startsAt":{"type":"string","minLength":4,"maxLength":40,"description":"Proposed start, ISO 8601 with timezone."},"durationMinutes":{"type":"integer","minimum":15,"maximum":1440},"venue":{"type":"string","minLength":1,"maxLength":200},"excludeEventId":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false,"required":["startsAt"]},"examples":[{"input":{"startsAt":"2026-12-05T19:00:00Z","durationMinutes":180,"venue":"The Workshop SF"}}],"path":"/api/agent/v1/tools/check_availability"},{"name":"suggest_event","version":1,"kind":"read","auth":"anonymous","scopes":[],"summary":"Evidence for planning an event from a brief: comparable events, category, timing and gaps.","description":"Turns a one-line brief into keywords, finds comparable future events in the public catalogue, and reports the market shape (categories, weekdays, how many comparables exist). Deterministic: no generative model, no invented price or demand forecast.","whenToUse":"Use when someone describes an event idea and needs evidence about what already exists and where there is room.","notFor":["Creating anything — planning is read-only here (see create_event_draft)."],"keywords":["idea","plan","suggest","brainstorm","what should we run","theme","gap in the market"],"costClass":"internal","idempotency":"optional","sideEffects":[],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"brief":{"type":"string","minLength":3,"maxLength":300,"description":"One line describing the event idea."},"city":{"type":"string","minLength":2,"maxLength":60},"withinDays":{"type":"integer","minimum":7,"maximum":365},"limit":{"type":"integer","minimum":1,"maximum":25}},"additionalProperties":false,"required":["brief"]},"examples":[{"input":{"brief":"an evening of live jazz for beginners","city":"San Francisco","withinDays":45}}],"path":"/api/agent/v1/tools/suggest_event"},{"name":"get_organization_analytics","version":1,"kind":"read","auth":"credential","scopes":["analytics:read"],"summary":"The organization's own registrations, funnel, attribution, attendance and agent usage for a window.","description":"Reads the first-party event store for the credential's organization and returns the published metrics: registrations and four separate funnels, attempt outcomes, conversion by channel, attribution coverage, repeat attendance, agent usage and cost per verified attendee. Every metric carries its own numerator and denominator.","whenToUse":"Use for a question about the organization's events over a period: this week, this month, this quarter, a trend over time, or how the events are performing in general.","notFor":["One event's funnel — use event_analytics.","Public catalogue data — use discover_events."],"keywords":["analytics","metrics","funnel","conversion","performance","registrations","how are we doing","report","this quarter","this month","period","trend","year to date"],"costClass":"internal","idempotency":"optional","sideEffects":[],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"windowDays":{"type":"integer","minimum":1,"maximum":365}},"additionalProperties":false},"examples":[{"input":{"windowDays":90}}],"path":"/api/agent/v1/tools/get_organization_analytics"},{"name":"event_analytics","version":1,"kind":"read","auth":"credential","scopes":["analytics:read"],"summary":"One of the organization's events: registrations, attendance, funnel, channels, agent usage.","description":"Scopes the analytics read to a single event of the credential's organization: participant counts by status, capacity and fill rate, the web-form / agent / attendance funnel steps, RSVP attempt outcomes, conversions by channel and agent usage recorded for that event.","whenToUse":"Use for a specific event's performance, before or after it runs.","notFor":["Organization-wide totals — use get_organization_analytics."],"keywords":["event metrics","how did the event go","attendance","registrations","fill rate"],"costClass":"internal","idempotency":"optional","sideEffects":[],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":200},"windowDays":{"type":"integer","minimum":1,"maximum":365}},"additionalProperties":false,"required":["slug"]},"examples":[{"input":{"slug":"draft-winter-lantern-parade","windowDays":120}}],"path":"/api/agent/v1/tools/event_analytics"},{"name":"preview_campaign","version":1,"kind":"read","auth":"credential","scopes":["analytics:read"],"summary":"What a campaign would send to, who would be excluded and why, and the one measured baseline.","description":"Dry run: computes the audience for a segment, the suppressions (no email address, no marketing consent, out of segment), and the only conversion Turnout can measure for that event (registration → attended / no_show, with its sample size). Creates nothing.","whenToUse":"Use before drafting or scheduling a campaign, to see the reachable audience and the honest baseline.","notFor":["Creating the campaign — use create_campaign_draft."],"keywords":["dry run","audience size","how many will get","suppression","before sending","preview"],"costClass":"internal","idempotency":"optional","sideEffects":[],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":200},"channel":{"type":"string","enum":["email"]},"segment":{"type":"string","enum":["registered","attended","no_show","all"]},"scheduledFor":{"type":"string","minLength":4,"maxLength":40,"description":"ISO 8601 date or timestamp, e.g. 2026-12-01 or 2026-12-01T19:00:00Z"}},"additionalProperties":false,"required":["slug"]},"examples":[{"input":{"slug":"draft-winter-lantern-parade","segment":"registered","scheduledFor":"2026-12-18T16:00:00Z"}}],"path":"/api/agent/v1/tools/preview_campaign"},{"name":"get_campaign_opportunity","version":1,"kind":"read","auth":"credential","scopes":["analytics:read"],"summary":"Ranked next actions (remind, promote, fix attribution), each with its own evidence.","description":"Looks at upcoming events, capacity, recorded registrations and attribution coverage, and returns ranked opportunities. Each one states the counts it rests on and the tool to call next. No expected revenue, uplift or ROI is quoted: Turnout records no advertising spend yet.","whenToUse":"Use when asked what to do next, or which event needs attention.","notFor":["Raw metrics — use get_organization_analytics."],"keywords":["opportunity","prioritise","what next","needs attention","recommendation","at risk"],"costClass":"internal","idempotency":"optional","sideEffects":[],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"horizonDays":{"type":"integer","minimum":1,"maximum":365},"windowDays":{"type":"integer","minimum":1,"maximum":365}},"additionalProperties":false},"examples":[{"input":{"horizonDays":30}}],"path":"/api/agent/v1/tools/get_campaign_opportunity"},{"name":"get_budget_status","version":1,"kind":"read","auth":"credential","scopes":["analytics:read"],"summary":"This credential's budget: calls and cost used today, ceilings, and last-24h usage per tool.","description":"Answers 'can I keep calling?' from the usage ledger: today's call count and settled cost against both daily ceilings, plus per-tool call counts, failures and median latency for the last 24 hours.","whenToUse":"Use when a call was refused for budget, or to plan a batch without hitting a ceiling.","notFor":["Rate limits per minute, which are stated in every error response."],"keywords":["budget","quota","rate limit","remaining","how many calls","cost so far"],"costClass":"internal","idempotency":"optional","sideEffects":[],"confirmRequired":false,"inputSchema":{"type":"object","properties":{}},"examples":[{"input":{}}],"path":"/api/agent/v1/tools/get_budget_status"},{"name":"create_rsvp","version":1,"kind":"write","auth":"credential","scopes":["rsvp:write"],"summary":"Register one person for a published event, on the agent path of the funnel.","description":"Writes a participant row for a published event through the same code path as the web form (`src/lib/rsvp.ts`), on the agent path: `rsvp_attempted` (outcome completed | waitlisted | duplicate | rejected | error) and, only for a written `registered` row, `rsvp_completed`. An existing registration for the same email is reported as `duplicate` with the existing token — never as a second registration and never as an error. At capacity, the result is `waitlisted`, which is a place in a queue, not a registration.","whenToUse":"Use when a caller explicitly asks to register someone (or themselves) for an event and has given a name and an email address.","notFor":["Bulk or imported lists — that is the CSV import in the dashboard, not an agent call.","Registering for a draft or cancelled event: the tool refuses, because the public form would too."],"keywords":["register","rsvp","sign up","join","book a place","reserve a seat","attend"],"costClass":"internal","idempotency":"required","sideEffects":["Writes one participant row (status `registered` or `waitlisted`) when the email is not already registered.","Emits `rsvp_attempted` for every execution and `rsvp_completed` only for a written `registered` row.","Repeating the call cannot register the same person twice: the UNIQUE index on (event_id, dedupe_key) refuses the second row, and the loser of that race is answered as a `duplicate`. The guarantee is the constraint, not the ledger."],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":200,"description":"Slug of the published event."},"name":{"type":"string","minLength":1,"maxLength":200},"email":{"type":"string","format":"email","maxLength":200,"description":"Lowercased before it is stored; it is the identity used for deduplication."},"consentMarketing":{"type":"boolean"}},"additionalProperties":false,"required":["slug","name","email"]},"examples":[{"input":{"slug":"winter-lantern-parade","name":"Dana Ruiz","email":"dana@example.com","consentMarketing":true}}],"path":"/api/agent/v1/tools/create_rsvp"},{"name":"create_event_draft","version":1,"kind":"write","auth":"credential","scopes":["events:write"],"summary":"Create an event as a draft — not visible publicly, accepting no registrations.","description":"Inserts an event owned by the credential's organization with status `draft`. Publishing is a separate, confirmed call, so a draft can be reviewed first. A slug is derived from the title (with a random suffix) unless one is given.","whenToUse":"Use when someone asks to set up, schedule or draft an event.","notFor":["Publishing (use publish_event, which needs confirm) — a draft is deliberately not live."],"keywords":["create event","add event","schedule","draft","set up","new listing"],"costClass":"internal","idempotency":"required","sideEffects":["Inserts one event row with status `draft`. The slug is derived from the request (title, start, venue, capacity) unless one is given, so the same request always means the same row.","Repeating the call returns the existing draft instead of creating a second one: the UNIQUE index on `events.slug` is the guarantee, not the ledger. Pass an explicit `slug` to create a genuinely separate event with the same title."],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":200},"startsAt":{"type":"string","minLength":4,"maxLength":40,"description":"Start time, ISO 8601 with timezone."},"durationMinutes":{"type":"integer","minimum":15,"maximum":1440},"timezone":{"type":"string","minLength":3,"maxLength":64},"venue":{"type":"string","maxLength":200},"capacity":{"type":"integer","minimum":0,"maximum":1000000},"description":{"type":"string","maxLength":5000},"slug":{"type":"string","minLength":3,"maxLength":60}},"additionalProperties":false,"required":["title","startsAt"]},"examples":[{"input":{"title":"Winter Lantern Parade","startsAt":"2026-12-20T18:00:00-08:00","venue":"Golden Gate Park","capacity":120}}],"path":"/api/agent/v1/tools/create_event_draft"},{"name":"update_event","version":1,"kind":"write","auth":"credential","scopes":["events:write"],"summary":"Change one of the organization's own events (draft or published).","description":"Updates the given fields of an event owned by the credential's organization. A completed or cancelled event is refused: its attendance record and funnel describe what happened, and editing it would make the numbers stop matching the events.","whenToUse":"Use when a detail of an existing event has changed: time, venue, capacity, title or description.","notFor":["Creating an event (create_event_draft).","Publishing or cancelling (publish_event / cancel_event, both confirmed)."],"keywords":["edit event","update","change time","reschedule","fix venue","capacity"],"costClass":"internal","idempotency":"required","sideEffects":["Updates the given fields on one event row. If the event is published, the change is public immediately.","Convergent and safe to repeat: the fields are set to the values in the request, so a second identical call leaves the row in the same state."],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":200},"title":{"type":"string","minLength":3,"maxLength":200},"startsAt":{"type":"string","minLength":4,"maxLength":40,"description":"ISO 8601 date or timestamp, e.g. 2026-12-01 or 2026-12-01T19:00:00Z"},"timezone":{"type":"string","minLength":3,"maxLength":64},"venue":{"type":"string","maxLength":200},"capacity":{"type":"integer","minimum":0,"maximum":1000000},"description":{"type":"string","maxLength":5000}},"additionalProperties":false,"required":["slug"]},"examples":[{"input":{"slug":"winter-lantern-parade","capacity":150,"venue":"County Fair Building"}}],"path":"/api/agent/v1/tools/update_event"},{"name":"publish_event","version":1,"kind":"write","auth":"credential","scopes":["events:write"],"summary":"Publish a draft event: make it publicly visible and able to accept registrations.","description":"Sets an event to `published`. This is the point where the event becomes public, so it requires `confirm: true` in addition to the idempotency key. Already-published is not an error: the response says nothing changed.","whenToUse":"Use only when the caller has explicitly decided the event should go live.","notFor":["Drafting an event (create_event_draft) — this is the irreversible step."],"keywords":["publish","go live","make public","launch event","open registrations"],"costClass":"internal","idempotency":"required","sideEffects":["Sets the event status to `published`: it becomes visible on the public page and accepts registrations.","Emits no analytics event: the R1 contract has no `event_published` name.","Safe to repeat: an already-published event is returned unchanged (`published: false`), so a retry cannot publish twice or re-open a cancelled event."],"confirmRequired":true,"inputSchema":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":200}},"additionalProperties":false,"required":["slug"]},"examples":[{"input":{"slug":"winter-lantern-parade"}}],"path":"/api/agent/v1/tools/publish_event"},{"name":"cancel_event","version":1,"kind":"write","auth":"credential","scopes":["events:write"],"summary":"Cancel an event so it stops accepting registrations. Does not notify anyone.","description":"Sets an event to `cancelled` and reports how many registrations it had, by status. It deliberately does NOT cancel the registrations or message anybody: the people who signed up still expect to hear from the organizer, and telling them is a separate, deliberate action.","whenToUse":"Use when an event will not happen and registrations must stop.","notFor":["Deleting history: cancellation keeps the record, and there is no delete tool."],"keywords":["cancel","call off","postpone","stop registrations","not happening"],"costClass":"internal","idempotency":"required","sideEffects":["Sets the event status to `cancelled`; the public page stops accepting registrations.","Leaves every participant row untouched (nobody is unregistered and nobody is notified).","Safe to repeat: an already-cancelled event is returned unchanged (`cancelled: false`)."],"confirmRequired":true,"inputSchema":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":200},"reason":{"type":"string","maxLength":500}},"additionalProperties":false,"required":["slug"]},"examples":[{"input":{"slug":"winter-lantern-parade","reason":"Venue withdrew the permit"}}],"path":"/api/agent/v1/tools/cancel_event"},{"name":"create_campaign_draft","version":1,"kind":"write","auth":"credential","scopes":["campaigns:write"],"summary":"Write a campaign draft for an event: a proposal with a body, not a send.","description":"Stores a draft campaign (name, segment, subject, body, optional planned time) for one of the organization's events. It has no audience yet and writes no reminders. The audience is resolved, with suppressions applied, when schedule_campaign_reminders runs.","whenToUse":"Use when someone asks for a reminder, announcement or email to be prepared for an event's registrations.","notFor":["Sending anything — no tool sends.","Estimating results — use preview_campaign for the honest baseline."],"keywords":["campaign","draft an email","newsletter","announcement","reminder text","compose"],"costClass":"internal","idempotency":"required","sideEffects":["Inserts one campaign draft row. No participant is touched and nothing is queued.","The proposal itself is the business key (event, name, segment, subject, body, schedule): an identical draft already in the database is returned instead of writing a second one, so a retry cannot duplicate a draft even if the ledger claim was lost."],"confirmRequired":false,"inputSchema":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":200,"description":"Event the campaign belongs to."},"name":{"type":"string","minLength":3,"maxLength":120,"description":"Internal name for the draft."},"segment":{"type":"string","enum":["registered","attended","no_show","all"]},"subject":{"type":"string","maxLength":200},"body":{"type":"string","minLength":1,"maxLength":20000,"description":"Plain text. No template language is processed, so placeholders are sent literally."},"scheduledFor":{"type":"string","minLength":4,"maxLength":40,"description":"ISO 8601 date or timestamp, e.g. 2026-12-01 or 2026-12-01T19:00:00Z"}},"additionalProperties":false,"required":["slug","name","subject","body"]},"examples":[{"input":{"slug":"winter-lantern-parade","name":"Parade reminder","subject":"See you on Saturday","body":"Doors open at 6pm…","segment":"registered"}}],"path":"/api/agent/v1/tools/create_campaign_draft"},{"name":"schedule_campaign_reminders","version":1,"kind":"write","auth":"credential","scopes":["campaigns:write"],"summary":"Queue reminders for a draft's mailable audience. Queues — it does not send.","description":"Resolves the draft's audience from the event's registrations, applies the suppressions (no email address, no marketing consent, out of segment) and writes one reminder row per mailable recipient with status `queued`. The draft becomes `scheduled`. Requires `confirm: true` because it writes rows about real people.","whenToUse":"Use only after a draft has been reviewed and the caller has confirmed it should be prepared for delivery.","notFor":["Sending: delivery is disabled in this build and no tool sends a message."],"keywords":["schedule","queue reminders","prepare send","approve campaign","enrol recipients"],"costClass":"internal","idempotency":"required","sideEffects":["Writes one `reminders` row per mailable recipient, with status `queued` (never `sent`).","Moves the draft from `draft` to `scheduled`. The status is written after the rows, so a crash in between leaves a `draft` that a repeat call completes rather than a `scheduled` draft with no reminders.","Repeating the call writes nothing: each reminder carries the business key (draft, recipient, channel) under a UNIQUE index, and an already-scheduled draft is answered with the counts it already has. Queuing is not sending, and nothing is ever sent."],"confirmRequired":true,"inputSchema":{"type":"object","properties":{"draftId":{"type":"string","format":"uuid","description":"The draft's id, from create_campaign_draft."}},"additionalProperties":false,"required":["draftId"]},"examples":[{"input":{"draftId":"6f1e8c1e-2b0a-4a9d-9f7e-3d5c1a2b4c6d"}}],"path":"/api/agent/v1/tools/schedule_campaign_reminders"}],"meta":{"apiVersion":"v1","generatedAt":"2026-11-08T20:53:10.967Z","attribution":"Event data published on Turnout by its organizer. Turnout is the source of truth for these rows; the organizer's own page is the citation.","license":"Turnout's own event data. Redistributed by the organizer's publication choice; not third-party licensed content.","notices":["An agent should list tools (or search them) before calling: the published set is the contract, and a tool that is not listed does not exist.","Every response states its own limits: `not_available` where a number cannot be established, and a reason for every suppression."],"count":17,"totalPublished":17,"discovery":{"query":null,"mode":"listing","note":"No query: every published tool, in registry order."},"filters":{"kind":null,"auth":null,"limit":50},"authentication":{"anonymousTools":["discover_events","get_event","get_organizer","suggest_event"],"credentialTools":["check_availability","get_organization_analytics","event_analytics","preview_campaign","get_campaign_opportunity","get_budget_status","create_rsvp","create_event_draft","update_event","publish_event","cancel_event","create_campaign_draft","schedule_campaign_reminders"],"schemes":[{"type":"bearer","header":"Authorization","valuePrefix":"tnt_","note":"Issued in the Turnout dashboard; stored server-side only as a hash, so it is shown once."},{"type":"apiKey","header":"x-turnout-agent-key","note":"Same credential, for clients that cannot set an Authorization header."}],"scopes":[{"scope":"events:read","description":"Read public event data (the Discover catalogue) and the organization's own events."},{"scope":"organizers:read","description":"Read public organizer profiles."},{"scope":"analytics:read","description":"Read the organization's own first-party metrics, including the credential's own call budget."},{"scope":"rsvp:write","description":"Create a registration on a published event. Records a real participant, so it is never granted by default."},{"scope":"events:write","description":"Create and change the organization's own events: drafts, edits, publishing and cancelling. Publishing and cancelling also require `confirm: true` on the call."},{"scope":"campaigns:write","description":"Draft a campaign and queue its reminders. Queuing writes reminder rows; nothing is sent by Turnout in this build."}],"rule":"The tenant comes from the credential, never from the request. Another organization's slug is answered 404 (not 403), so the API does not confirm that it exists."},"calling":{"rest":{"toolList":"/api/agent/v1/tools","toolCall":"POST /api/agent/v1/tools/{name}","body":"{\"input\":{...},\"idempotencyKey\":\"...\",\"confirm\":false}"},"mcp":{"endpoint":"/api/agent/v1/mcp","transport":"streamable-http","protocolVersions":["2025-06-18","2025-03-26","2024-11-05"],"serverInfo":{"name":"turnout-agent","title":"Turnout Agent API","version":"1.0.0"}},"openapi":"/api/agent/v1/openapi.json","discoveryDocuments":["/.well-known/turnout-agent.json","/.well-known/mcp.json"]},"budgets":{"perMinute":{"credentialRead":120,"credentialWrite":20,"anonymousPerCallerIp":60},"perMinuteRule":"The enforced limit for one credential and one tool is the lower of these ceilings and the credential's own configured rate; each tool keeps its own bucket, so a busy tool cannot starve the others. get_budget_status reports the effective numbers.","daily":{"callsPerCredential":2000,"costMicroUsdPerCredential":2000000,"note":"Computed from the usage ledger, not from an in-memory counter, so a restart cannot reset a ceiling. See get_budget_status."}},"contract":{"envelope":"{ data, meta } on success; { error: { code, message }, meta } on failure — the same shapes over REST and MCP.","idempotency":"A write requires `idempotencyKey` (8-128 chars). Repeating a key with the same input returns the stored response (`meta.replayed: true`); reusing it with a different input is 409 `idempotency_key_reuse`.","costs":"Costs are integers in micro-USD (1e-6 USD). R2 tools call no paid provider, so a settled call is 0 with `cost.source: \"internal\"`."}}}