Skip to main content
This page documents all notable changes to the AthenaHQ API, including new features, improvements, and bug fixes. We recommend reviewing this changelog regularly to stay informed about updates that may affect your integration.

September 2026

New: activity feed for partner integrations

GET /api/v1/activity lists the changes made to prompts, topics and prompt tags across your organization, newest first by default, with the website, the kind of change, the affected ids and whether an API key made it. Filter by website_id, entity_type, change_type and a since/until window; page with page_num and page_size. To mirror prompts, pin a window per sync run (since = the previous run’s until, until = now minus one minute, order=asc) and walk the pages: one call per run replaces re-listing every website. An entry with entity_ids: null means re-list that website. Available to partner API keys.

New: Generate pitch reports through the API

POST /api/v1/pitches accepts website_url, with optional country and language. Use your global organization API key and an Idempotency-Key header. The API returns 202 with pitch_id, status, and a shareable report_url; poll GET /api/v1/pitches/{pitch_id} for completion. Reports appear in your Pitch Workspace. Each admitted attempt consumes one pitch allowance unit. Repeating the same request key reuses the report, including after completion or failure. Generation sends no email.

Fixed: MCP API-key connections from Optimizely Opal

The MCP endpoint now accepts AthenaHQ API keys in Authorization: Bearer <api-key>, enabling clients such as Optimizely Opal that cannot send a custom x-api-key header. Keys retain their existing organization, website scope, and write permissions. OAuth access tokens and x-api-key connections continue to work. REST endpoints under /api/v1 still require x-api-key. See Connect Optimizely Opal for OAuth and API-key setup.

Added: active period on prompt schedules

GET /api/v1/prompt-schedules (MCP: get_prompt_schedules) now returns starts_on and ends_on on every schedule: the first and last UTC calendar days (YYYY-MM-DD) a custom schedule fires, or null when that side has no bound. A schedule can be is_active: true and still not run because today is before starts_on or after ends_on; use the two dates to tell a seasonal schedule that has ended from one that is running. Default always returns null for both. Existing fields are unchanged.

Improved: tracked URLs include their Content Hub destination

POST /api/v1/content/track (MCP: track_content_urls) now includes destination_name and content_hub_url on each created and skipped result. The destination names the tab displaying the row, and the link opens that item with its website and tab selected. Shared-pool rows use the default view’s name when available, or “Shared pool” when no view exists. Omitting sheet_id still tracks URLs in the shared pool, regardless of the tab currently open in your browser. When a URL is already tracked, its result links to its existing location; retrying with another sheet_id does not move it. Use the created and skipped counts separately when reporting results, and include the destination and item links.

New: custom Content Hub column values when tracking URLs

POST /api/v1/content/track (MCP: track_content_urls) accepts an optional fields list per URL. Each entry names one of the customer-created columns on the destination tab, as shown in Content Hub (column, matched case-insensitively), and its value: a string for text columns, an http(s) URL for link columns, a number for number columns, an ISO 8601 date string for date columns (a date-only value is stored as UTC midnight, the same instant the Content Hub date picker records for that day), a boolean for checkbox columns, and an array of up to 50 option labels for multi-select columns. Multi-select options that do not exist yet are created; existing ones match case-insensitively. Values are written together with the created row, so a link tracked by an automation no longer needs its columns filled by hand. Skipped URLs (already tracked) keep their existing values. An unknown column name, a generated column (Ask Athena, approval, OG image), or a value that does not fit the column type returns 400 naming the field, and nothing is tracked. Requests without fields behave exactly as before.

New: POST /api/v1/metrics/shopping returns the Shopping Insights dashboard

The in-app Shopping page reports how a brand’s products show up in AI shopping answers (product carousels), but none of it was reachable over the API, which only covered share of voice, mention rate, citation rate and position. POST /api/v1/metrics/shopping returns that page in one call: a summary (shopping answers, own and competitor product appearances, appearance rate, average positions), an offer_summary (tracked offers and average price), top_own_products and top_competitor_products, the competitor_breakdown leaderboard, the merchant_breakdown of retailers, the position_distribution rank ladder, a price_comparison of own versus competitor offers, and a daily trend. It runs the same queries as the page, so the numbers match what a user sees in the app. The body follows the other metrics endpoints (website_id plus filters), with start_date and end_date as inclusive YYYY-MM-DD dates in UTC. Only the filters the page applies are accepted: schedule_id, models (chatgpt and ai_mode, the two models that answer with carousels), competitor_ids, prompt_tags with prompt_tags_operator, and target_identifiers. Any other filter key returns 400 rather than being ignored. top_products_limit (1 to 50, default 10) sizes both product lists. Rates are percentages from 0 to 100 and positions are 1-based. The endpoint requires a paid plan, matching the page. The same read is available over MCP as get_shopping_metrics. The endpoint is additive: every existing endpoint is unchanged.

Fixed: ad hoc response runs appear under Default

Runs started without a schedule now belong to Default, including runs started through the API or MCP. Previously Manual-tagged history is moved to Default during the data migration. The schedule catalog and response provenance emit only default or custom schedule selections. Existing schedule_id: "manual" filters remain accepted as an alias of default. POST /api/v1/response-streaming/start also accepts an optional schedule_id UUID. An explicit schedule supplies provenance and prompt selectors; supplied model, fan-out, persona and location options apply to that run. An ad hoc run can coexist with a scheduled run of the same schedule.

Changed: website provisioning skips competitors on the website’s own URL

POST /api/v1/websites/provision now ignores a competitor whose URL is the website’s own URL or a path under it, since such a record would attribute the website’s own citations to the competitor. The skip is logged and reflected in the returned competitorCount. Request and response shapes are unchanged.

New: credit usage and charge history over MCP

get_credit_usage returns organization credit totals, time buckets, and charged-pool breakdowns. get_credit_usage_events returns paginated charges, refunds, recorded actions, and originating-website attribution. Both support explicit UTC windows so calendar-month investigations and follow-up pages use the same interval. Signed-in callers need organization billing access. API-key callers need a global organization key; website-restricted keys cannot read these organization-wide details. Existing balance tools and REST endpoints are unchanged.

Changed: partner recommendation access uses Autumn entitlements

POST /api/v1/seo/recommendations now requires the organization’s seo_recommendations entitlement in Autumn. Access is configured through the organization’s plan or customer subscription. Deployment variables and a separate website activation allowlist are no longer required. Existing API-key scope and website content permissions still apply. The entitlement is checked before accepting a job and again before queued analysis starts. Missing access returns 403; an unavailable admission check returns 500 without creating work. Unstarted work stays pending when access cannot be verified, releasing its worker slot so other organizations can proceed. Grant and revocation propagation follows the shared entitlement cache: 15 minutes while healthy, with previously verified snapshots usable for up to one hour during an Autumn outage. Queue limits, request and response shapes, and authorized saved-result polling are unchanged.

New: asynchronous SEO recommendations for activated partners

POST /api/v1/seo/recommendations accepts a draft or public HTTPS page and returns a saved job to poll with GET /api/v1/seo/recommendations/{job_id}?website_id=.... Results contain source-grounded SEO and AI-search recommendations, evidence and token usage. Reusing a request ID with identical input returns the same job; changed input returns 409. Each website can queue up to 1,000 outstanding jobs. Analysis is limited to four workers per website and 16 across websites, with fair dispatch and recovery for interrupted starts. Submissions and polling share the existing organization API request budget (default 60 requests/minute); submissions also have a 120 requests/minute website ceiling. Honor Retry-After on 429 and back off when polling pending jobs. Submitting requires the organization’s seo_recommendations entitlement in Autumn and website content administration access. Polling requires website content view access. Both operations are included in the public /api/v1/openapi.json specification for client generation, with the same request and response schemas shown in this reference.

New: select one streaming schedule in response analytics

Response-derived metrics, responses, sources, and content attribution accept filters.schedule_id: default or a custom schedule’s UUID. Omitting it selects Default, not a combined view. Default includes all pre-cutover history, including ad hoc runs. The retired manual selection is a Default alias. Archived schedules remain queryable, and there is no All schedules selection. POST /api/v1/looker-studio/data applies the same filters.schedule_id selection to report metrics and dimensions, with separate cached results per schedule. Existing requests without the field continue to select Default. Use GET /api/v1/prompt-schedules?website_id=... (MCP: get_prompt_schedules) to discover names and selection values for your website. Pass the returned selection, not a stock schedule’s UUID. GET /api/v1/date-range accepts the same selection as the schedule_id query parameter. Response rows now include an additive schedule provenance field with that selection value, including default for legacy history. The generic query_metrics and query_rows tools also accept a top-level schedule selection. Existing fields are unchanged. Website-level information, such as billing and AI crawler visits, is not filtered by streaming schedule. If your MCP client cached tool definitions or approvals, refresh the tool list and re-approve the updated analytics tools when prompted.

Changed: editing a brand fact’s text creates a new fact and archives the current one

PATCH /api/v1/knowledge-base/facts/{fact_id} (and the MCP tool update_brand_fact) no longer rewrites a fact’s text in place. A request that changes text inserts a new fact carrying the new text, the fact’s pillar and provenance, and its current review status, and archives the fact you addressed. The response shape is unchanged, but fact.id is the new fact’s id; before still describes the fact you addressed. Requests that change only source_url or confidence keep updating the same fact in place. Archived facts are not returned by GET /api/v1/knowledge-base/facts and a PATCH against one returns 404. Facts captured from AI responses, facts hidden as duplicates, and rejected facts can no longer have their text changed through this endpoint and return 409.

New: Set, replace, or clear a website’s partner external_id with PATCH /api/v1/websites/{website_id}

Partner integrations can now change the identifier mapped to a website after it was created, instead of only at POST /api/v1/websites or POST /api/v1/websites/provision time:
  • Pass external_id as a non-empty string to set or replace the mapping.
  • Pass external_id: null to clear it.
  • An id already mapped to another website in your organization returns 409 with the current holder in existing, so moving an id between two websites is a clear on the old one followed by a set on the new one.
The endpoint requires a global partner API key and also works on deleted websites, since deleting a website does not release its external_id.

Changed: Optimize drafts can be marked published before their review runs

POST /api/v1/content/{content_id}/publish (and the MCP tool publish_content) no longer returns 409 for an Optimize draft that is waiting at stage pending with no Athena review in flight. An Optimize draft is created from a page that is already live on your site, so its live URL can be recorded, and citation tracking started, without running the review first. The same rule now drives the Track control in the Content Hub editor. A pending stage on any other content type still means the article is generating and is still rejected, as is an Optimize draft whose review is currently running. Response shapes are unchanged.

New: Filter responses by their search queries

POST /api/v1/responses and the MCP tool get_responses now support three response-only filters for inspecting the web and map searches an AI model used:
  • filters.has_search_queries selects responses based on whether they used at least one non-empty search query.
  • filters.search_query_text performs a case-insensitive text match across the queries used for each response.
  • filters.search_query_text_operator accepts contains (the default) or does_not_contain.
The filters compose with the existing date, model, prompt, competitor, location, and prompt metadata filters. Omitting them preserves the previous behavior and response shape.

Changed: group membership writes tolerate duplicates and large batches

Some group requests that previously failed with 500 now succeed. The notable cases:
  • POST /api/v1/groups with the same website ID listed more than once in website_ids now creates the group with a single membership per distinct ID and returns 201. The response’s websiteIds lists each ID once.
  • PATCH /api/v1/groups/ no longer returns 500 when two concurrent requests add the same website to a group; both return 200, matching the documented behavior that websites already in the group are silently ignored.
  • PATCH requests carrying very large add_website_ids or remove_website_ids arrays that previously failed against an internal statement limit are now processed in batches and succeed.
Every request that previously succeeded returns the same response as before; no endpoint paths, response shapes, or error strings changed.

September 2026

New: GET /api/v1/content/items lists every Content Hub item, unpublished drafts included

POST /api/v1/content lists content with citation and impression metrics only once its generation pipeline is finished, so a Content Hub sheet of freshly generated drafts came back empty over the REST API even though GET /api/v1/content/sheets listed the sheet. The MCP tool list_content already enumerated those rows, but it had no REST counterpart. GET /api/v1/content/items is that counterpart: it returns every content item a website has, published or not (in-flight drafts, briefs, snipes, optimize and slice runs, scheduled and failed items, plus tracked pages), with identity fields only (content_id, title, type, stage, sheet_id, sheet_name, topic_name, url, prompt_ids, prompt_count, timestamps) and no metrics. Filter by sheet_id, content_type, stage, or prompt_id, and page with the usual page_num / page_size. stage is the raw pipeline workflow state, not a publication flag: null for pages with no pipeline record, generated for text that is ready but unpublished (manual editor items too), done once published or marked done. An item counts as published when its stage is done (or it is an external or imported page) and it has a url. The listing reads from the primary store and is uncached, so an item is listable the moment POST /api/v1/content/generate returns its content_id. Read a row’s text with GET /api/v1/content/{content_id}. The endpoint is additive: POST /api/v1/content and every other endpoint are unchanged.

August 2026

New: mistral model filter value

Every endpoint that accepts a models filter now also accepts mistral, covering Mistral responses. The value is additive: existing filter values, response shapes and status codes are unchanged, and omitting the filter behaves exactly as before. If you start runs without selected_models, this costs you more. POST /api/v1/response-streaming/start treats an omitted selected_models as “every currently active model”, and Mistral is now one of them. An integration that has always omitted the field will start producing Mistral responses it never asked for, billed at 5 credits each like Claude, DeepSeek and Meta AI. Pass an explicit selected_models map to keep the previous set. Two things worth knowing before you filter on it. Mistral responses only exist for websites that have opted the model into their tracking, so an unfamiliar website will simply return no rows rather than an error. And Mistral is reached through Mistral’s own API using its built-in web search, which accepts no country parameter, so its country attribution reflects the country stated in the prompt rather than a request originating there. The same is true of claude, deepseek and meta_ai.

New: meta_ai model filter value

Every endpoint that accepts a models filter now also accepts meta_ai, covering Meta AI (Muse Spark) responses. The value is additive: existing filter values, response shapes and status codes are unchanged, and omitting the filter behaves exactly as before. If you start runs without selected_models, this costs you more. POST /api/v1/response-streaming/start treats an omitted selected_models as “every currently active model”, and Meta AI is now one of them. An integration that has always omitted the field will start producing Meta AI responses it never asked for, billed at 5 credits each like Claude and DeepSeek. Pass an explicit selected_models map to keep the previous set. Two things worth knowing before you filter on it. Meta AI responses only exist for websites that have opted the model into their tracking, so an unfamiliar website will simply return no rows rather than an error. And Meta AI is reached through Meta’s Model API rather than scraped from a country-local session, so its country attribution reflects the country stated in the prompt, not a request originating there. The same is true of claude and deepseek.

New: baseCountry on website listings

GET /api/v1/websites (and the MCP tool list_websites) now includes each website’s baseCountry: the country market the website targets, e.g. "United States". Organizations that track one brand across several countries model each market as its own website, and until now nothing in the listing distinguished them, so same-name websites were impossible to tell apart without out-of-band knowledge. The value is editable in the website’s brand profile (the “Base location” setting). Websites that never chose a country carry the default "United States", so treat that value as unconfirmed for sites that do not look US-based. The field is additive: no existing fields or status codes changed. The external-id lookup paths (the legacy ?external_id= filter on this endpoint and GET /api/v1/websites/by-external-id/{external_id}) do not include the new field. If you generate a typed client from this spec, regenerating will pick up the new optional field.

New: Knowledge Base pillar curation

Pillars stop being create-only and facts stop being stuck where they landed: four new endpoints close the Knowledge Base curation loop. All four require a website-admin API key and the Knowledge Base to be enabled for the organization.
  • PATCH /api/v1/knowledge-base/pillars/ updates a pillar’s name, description, or status (at least one required; only provided fields change). Renaming returns a 409 when another pillar already uses the name. Status accepts published or archived: archiving hides the pillar from listings and stops its facts feeding content generation while keeping them stored, so it is the safe alternative to deletion. Also available as the MCP tool update_pillar.
  • POST /api/v1/knowledge-base/facts/move files 1-50 facts under a published pillar (also the MCP tool move_brand_facts). This works for unfiled facts, which until now were readable over the API but not fixable, and re-files facts from other pillars. Review status never changes, and only approved facts under a published pillar feed content generation, so moving is what puts approved facts to work. Missing ids are reported in not_found_ids rather than failing the call, so retries are idempotent.
  • POST /api/v1/knowledge-base/pillars/merge folds 1-20 source pillars into a published target: every fact on the sources re-files onto the target, page links carry over keeping the stronger signal, and the source pillars are then deleted (also the MCP tool merge_pillars). Unlike the other Knowledge Base writes, retries are strict: missing source ids are rejected rather than skipped, so re-read the pillars before retrying a failed merge.
  • POST /api/v1/knowledge-base/pillars/delete permanently deletes 1-20 pillars and every fact filed under them; facts are deleted, not unfiled, and there is no undo (also the MCP tool delete_pillars). The response reports each deleted pillar’s name and fact count, since a deleted pillar cannot be re-fetched. Like delete_brand_facts, OAuth MCP connections gate on the signed-in user’s admin role rather than the brand-knowledge write permission.

New: Remove group member access with DELETE /api/v1/groups/{group_id}/members/{user_id}

The new endpoint deprovisions a user from a group in one request. It removes the direct group membership and explicit website memberships for websites currently in that group.
  • The response reports whether the group membership was removed and how many website memberships were removed. Retries and well-formed missing targets are idempotent and return zero removal counts.
  • Organization membership, membership in other groups, and website membership outside the group are preserved. Access to a shared website remains when another group still grants it.
  • Organization members return 409 because their organization-derived access would remain. The endpoint requires a global API key and prohibits removing the API key’s own user.

Changed: Attribute direction now selects response-level perception

The four attribute endpoints keep their existing paths and response shapes, but attributes are now direction-neutral topics. The positive value in a response identifies the selected positive or negative observation series instead of permanently classifying the attribute name.
  • GET /api/v1/attributes, POST /api/v1/attributes/cumulative, POST /api/v1/attributes/competitors, and POST /api/v1/attributes/time-series accept an optional top-level positive boolean.
  • Omitting positive defaults to true, preserving existing requests. Pass positive: false for negative observations of the same attribute ID.
  • Percentages remain directional prevalence among sampled, successfully analyzed responses that mention the entity. A response with both directions can count once in each series, and an analyzed response with no attribute claim remains in the denominator.
No response fields, endpoint paths, or envelopes changed.

Changed: request bodies now enforce explicit size bounds

Website, provisioning, and prompt write endpoints previously accepted unbounded strings and arrays. They now validate size limits and return 400 when a field exceeds them. Every limit sits well above real production usage (the largest identifier set ever stored is 166 entries; the longest prompt is under 1,000 characters), so requests that were working before continue to work unchanged. The OpenAPI spec now declares the corresponding maxLength / maxItems values.
  • POST /api/v1/websites: url up to 2,048 characters.
  • POST /api/v1/websites/provision: url up to 2,048 characters and name up to 255; identifiers up to 200 entries of 500 characters each (also per competitor); competitors up to 500 entries, each with the same name/URL bounds; prompts up to 10,000 entries with text up to 2,000 characters. 500 is also the ceiling on live competitors per website across the whole API: create requests past it return 400.
  • POST /api/v1/prompts and PATCH /api/v1/prompts/{prompt_id}: prompt text up to 2,000 characters. The existing batch bound of 1,000 prompts per create call is unchanged.
  • POST /api/v1/prompts is now also rate limited to 60 requests per minute per website, independent of your organization-level API rate limit. Each call accepts up to 1,000 prompts, so batching keeps effective throughput at roughly 60,000 prompts per minute per website; exceeding the window returns 429.
If you generate a typed client from this spec, regenerating will pick up the new bounds as validation annotations. No field shapes or names changed; the only status-code additions are the 400 on oversized payloads described above and the 429 on POST /api/v1/prompts when the per-website rate limit is exceeded.

Clarified: Response progress can reach 100% before analysis finishes

GET /api/v1/response-streaming/status keeps the same response shape and state values. Its documentation now makes the readiness boundary explicit: progress measures response collection, while state covers the whole run. A run can report state: "running" with progress.percentage: 100 while downstream sentiment and attribute analysis finishes. Treat only state: "completed" as the signal that downstream analysis is ready. No client migration is required.

New: Update a group member’s role with PATCH /api/v1/groups/{group_id}/members/{user_id}

Group membership was write-once over the API: POST /api/v1/invites/bulk could add a user to a group at a given role, and GET /api/v1/groups/{group_id}/members could read it back, but there was no way to change that role afterwards. The only options were removing and re-inviting the user, or editing the role in the dashboard. The new endpoint closes that gap, mirroring the existing Update Website Member Role and Update Organization Member Role endpoints:
  • Accepts either the coarse role (admin / viewer) or a role_id, which can be a system role or one of your organization’s custom roles from GET /api/v1/roles. Provide exactly one. The response echoes both the assigned role_id and its slug, the same role value the member listing returns.
  • It targets explicit group members only, the access_type: "group" entries in the member listing. Organization members have no group-level role to change, because their access to every group comes from their organization role; targeting one returns 409. Note that changing that organization role would take effect across every group and website in the organization, not only the group you were working on.
  • Requires a global API key, and cannot modify the key’s own user. Unlike the organization endpoint there is no last-admin restriction: a group’s only admin can be demoted, since organization admins keep admin access to every group in the organization.
  • Roles outside admin and viewer are honored for access but are not yet rendered in the dashboard. Assigning a group member a role_id such as Editor or Billing Manager grants that role’s real permissions on every website in the group, but the dashboard’s group member list currently displays any non-admin role as “Viewer”. Until that is addressed, prefer admin / viewer at group scope when a customer administrator also needs to see the role.

New: GET /api/v1/content/{content_id} returns the prompts a page targets

The response carries two new fields, prompts and prompts_total. prompts lists the prompts the content was written to target, the same ids POST /api/v1/content/generate accepts as prompt_ids, each with its text, topic, and status. Until now that association was write-only over the API: you could set it when generating content and never read it back.
  • Ordered oldest association first and capped at 100 entries, with the untruncated size in prompts_total. Bulk imports can attach thousands of prompts to one page, so check prompts_total before assuming the list is complete. Associations past the 100th are not currently retrievable.
  • status is active, paused, or deleted. Deleted prompts are included because the page was written for them and the Content Hub still displays them. GET /api/v1/prompts does not list deleted prompts, so an id from here may not resolve there; the text is returned inline for that reason.
  • An empty array means the page has no prompt targeting recorded. That is normal for imported and externally tracked pages.
This is a different question from POST /api/v1/content/prompts, which lists the prompts whose AI responses cited the page over a date range. Targeting comes from the content record and does not depend on any citation having happened, so a page with no citations yet still returns its prompts here. Both fields are additive; existing clients are unaffected.

New: Lighter response payloads with include_response and compact_sources

POST /api/v1/responses (and the MCP tool get_responses) accepts two optional booleans that let you drop the two fields that dominate a row’s size. Both default to today’s behavior, so a call that omits them returns exactly the same body as before, and page_size is unchanged at a maximum of 100.
  • include_response: false omits the response field. The answer text averages around 2.2 KB per row and is usually the single heaviest thing you page through.
  • compact_sources: true replaces the sources array of per-URL objects with source_domains, a deduped and alphabetically sorted list of cited root domains, averaging around 3.1 KB less per row. Hosts are lower-cased with any leading www. removed, and subdomains are preserved, so shop.example.com stays distinct from example.com.
Together these are the intended way to export prompt fan-out variations at volume. Pair them with filters.variation_filter: ["variation"] and each row still carries the base prompt, the generated variation, the model, the mentioned and cited flags for your domain, and the cited domains, at roughly a tenth of the previous size. If you generate a typed client from this spec, read this before upgrading. response and sources are no longer listed as required on the response row, because they are genuinely absent from the payload when you opt out. Regenerating your client will make both fields optional and may surface as a compile error even if you never set either flag. The runtime shape of a call that omits both flags has not changed. Opted-out fields are absent from the row rather than empty, since an empty response would read as “the model answered nothing”. GET /api/v1/responses/{response_id} is unchanged and always returns the full row.

July 2026

New: Track existing URLs as content with POST /api/v1/content/track

Publishing closed one half of the attribution loop; this closes the other. Until now the API could only attach a URL to content Athena generated (publish), so pages that already existed, guest posts, partner placements, or any page written outside Athena, could not be tracked at all. The new track endpoint (also the MCP tool track_content_urls) registers a list of up to 500 URLs as tracked Content Hub pages in one call:
  • Each URL becomes an external content row whose normalized URL is the citation/metric join key, so citations and mentions in your monitored AI responses start attributing immediately. There is no publish step, no generation, and no credit cost. Third-party domains work; tracking is not limited to your own site.
  • The call is idempotent per URL: a URL already present on the website (any content type) is never duplicated. The response splits created and skipped, and each skipped entry carries the existing row’s content_id and type, so you can tell an already-tracked page apart from a collision with first-party generated content before chaining other calls onto the id. Tracking-param variants of the same page collapse to one row.
  • Rows are created without page text, which citation tracking does not need. The page body can be pulled on demand from the Content Hub (Fetch content, on external pages without a body).
  • Optional sheet_id files the new rows under a Content Hub tab; optional per-URL title overrides the slug-derived default.

Changed: get_tracked_content no longer hides external and imported pages once their body is fetched

Tracked external and imported pages disappeared from POST /api/v1/content (and the MCP tool get_tracked_content) as soon as their page text was scraped, because the fill stamped an internal pipeline status that the listing’s published filter excluded. The pages were still tracked, their citations still attributed, and the Content Hub still showed them; only this listing dropped them. External and imported pages are now always listed. Expect row counts to increase on websites with imported content: pages that were silently hidden reappear with their metrics intact. Generated content is unchanged and still appears only once published.

New: Edit and delete brand facts

The Knowledge Base write surface is no longer append-only: an integration that adds facts can now also fix or remove them. Both endpoints require a website-admin API key and the Knowledge Base to be enabled for the organization.
  • PATCH /api/v1/knowledge-base/facts/ updates a fact’s text, source_url, or confidence. Only provided fields change, and source_url: "" clears the stored source. Editing text re-embeds the fact for semantic search; a 409 is returned when the new text collides with a sibling fact captured from the same AI response (duplicates across other facts are left to Knowledge Base dedup). Review status never changes through this endpoint. Also available as the MCP tool update_brand_fact on API-key connections and org-bound OAuth sessions with brand-knowledge write permission.
  • POST /api/v1/knowledge-base/facts/delete permanently deletes 1-50 facts in one call (a single delete is a one-element array). There is no undo. Ids with no fact on the website are reported in not_found_ids instead of failing the call, so retries are idempotent. Deleted facts drop out of semantic search and the owning pillar’s document regenerates without them. Also available as the MCP tool delete_brand_facts; on OAuth sessions it requires the signed-in user to hold the admin role, matching the dashboard’s delete gate.

New: website objects carry the partner external_id everywhere

For partner integrations, every endpoint that returns website objects now includes the partner-supplied identifier mapped to that website, so you can match sites by your own id instead of by URL (a site’s websiteUrl changes when its primary URL is edited in Athena):
  • GET /api/v1/groups and GET /api/v1/groups/{group_id}: each website object inside websites carries externalId (null when no mapping exists), alongside the group-level externalId. The PATCH /api/v1/groups/{group_id} response has the same shape. These per-website fields have been returned since early July and are now documented.
  • GET /api/v1/users/{user_id}/websites: each item now includes external_id (snake_case, matching that endpoint’s field naming).
The field appears only on responses to partner API keys; direct-customer responses omit it entirely. Mappings are created by passing external_id to POST /api/v1/websites or POST /api/v1/websites/provision; GET /api/v1/websites and GET /api/v1/websites/by-external-id/{external_id} already exposed them.

Changed: fact listings exclude Oracle-generated facts by default (new include_oracle parameter)

GET /api/v1/knowledge-base/facts (and the MCP tool get_brand_facts) now excludes facts produced by Oracle’s automated analysis from every listing, including the default one. Those facts are never routed to pillars and on most websites dominated the corpus (often millions of rows, inserted continuously), burying the user-curated facts the endpoint exists to surface. The response shape is unchanged; only which rows match changed. To include Oracle-generated facts, pass the new include_oracle=true parameter; it composes with every other filter, so include_oracle=true&unfiled=true reproduces the previous unfiled behavior. GET /api/v1/knowledge-base/facts/search is unaffected and still searches the full approved corpus, so a fact returned by search (with pillar: null) is not guaranteed to appear in a default listing.

New: Mark content as published with POST /api/v1/content/{content_id}/publish

Until now the content workflow ended one step short over the API: you could create a piece, revise it, approve its brief, and publish it to your site yourself, but there was no way to tell Athena the piece was live. The item stayed unpublished forever, and since the URL is the join key citations and mentions attach to, citation tracking never started. The new publish endpoint (also the MCP tool publish_content) closes that loop. Call it after the piece is actually live, passing the exact public URL of the page:
  • The URL is stored as the item’s citation/metric join key (protocol and trailing slashes are stripped; a bare acme.com/blog/post is fine). Query strings and fragments are excluded from the join key for non-YouTube URLs, so pasting a UTM-tagged URL does not break attribution; the display URL keeps them. YouTube URLs keep their params, since the video id lives there.
  • The publish time is stamped and the pipeline stage moves to done, so the item now counts as published in Content Hub and in get_tracked_content.
  • A first publish requires a finished article (stage generated). Briefs, still-generating or failed pieces, and items with a scheduled CMS publish return 409 with the reason, so incomplete content cannot be marked live.
  • Calling it again on an already-published item updates the tracked URL only, keeping the original publish time. The response’s already_published flag tells you which of the two happened. External and imported pages are live by definition, so a call on them is always a URL-only update.
This is the only way to set a content URL over the API: the rename endpoint (PATCH /api/v1/content/{content_id}) remains deliberately title-only.

New: list_content MCP tool for enumerating unpublished drafts

get_tracked_content lists content with citation and impression metrics only once its generation pipeline is finished, so a sheet full of in-progress drafts listed as empty: there was no way to enumerate a sheet’s unpublished pipeline items without already holding their content_ids. Rather than blur that tool’s “tracked” semantics with a flag, there is now a dedicated MCP tool, list_content, that enumerates every content item a website has, published or not: in-flight drafts, briefs, snipes, optimize and slice runs, scheduled and failed items, plus tracked pages. It returns identity fields only (content_id, title, type, stage, sheet_id, sheet_name, topic_name, url, timestamps), no metrics, and supports sheet_id, content_type, and stage filters with the usual page_num / page_size pagination. Passing the sheet_id of a view-type tab (see sheet_type on get_content_hub_sheets) lists the shared main pool that views overlay, without the view’s saved filters. stage is the raw pipeline workflow state, not a publication flag: null for pages with no pipeline record (most external and imported tracked pages), otherwise the stage the pipeline is at (pending_brief, generated, done, scheduled, failed, and so on). An item counts as published in Content Hub terms when its stage is done (or it is an external or imported page) and it has a url; manual editor items carry generated. The listing reads from the primary store and is uncached, so an item is listable the moment create_content returns its content_id, before any brief approval, generation, or publish. get_tracked_content and POST /api/v1/content are unchanged.

New: Deterministic draft edits with POST /api/v1/content/{content_id}/edit

Until now the only way to change an article body over the API was revise, which hands your instruction to an AI model and saves whatever it writes. The new edit endpoint (also the MCP tool edit_content) is the deterministic counterpart: you send exact find-and-replace pairs and your replacement text lands verbatim, no model involved.
  • Each edit’s find text must match the current body exactly once. Zero matches (stale snapshot, wrong anchor) or multiple matches (ambiguous anchor) fail the whole call with 400 and nothing is saved; extend the find text until it is unique.
  • Up to 20 edits per call, applied in order, with later edits seeing earlier results. The batch is recorded as one new version, restorable like any other.
  • Optional expected_version_number (from the draft endpoint) makes the call fail with 409 when a new version was recorded after your read (a revision, edit, or restore). An app autosave changes the body without recording a version; that drift is caught by the anchors themselves, since a moved or vanished find text fails the call.
The revise and brief/revise descriptions now also state explicitly that their rewrites are AI-generated text, not your literal wording, so it is clear which of the two tools to reach for.

Fixed: Narrow revise instructions could replace the whole article with the edited passage

POST /api/v1/content/{content_id}/revise (and the MCP tool revise_content) could destroy a draft when given a narrowly-scoped instruction such as “change only this sentence, alter nothing else”: the model sometimes returned only the edited passage, and that fragment was saved as the new article. The endpoint now guards against this. A revision that comes back dramatically shorter than the current draft, without an instruction that calls for shortening, is retried with an explicit whole-document directive; if it still comes back as a fragment, the call fails with a 500 and the draft is left unchanged. Nothing is ever silently truncated, and earlier text remains restorable via the version endpoints. The response now also includes body_word_count and previous_body_word_count (both integers), so callers can verify the size change themselves. The returned body is always the complete replacement article. Same additions apply to POST /api/v1/content/{content_id}/brief/revise’s underlying guard, though its response shape is unchanged.

Changed: filters.competitor_ids on content endpoints is now strict

POST /api/v1/content and POST /api/v1/content/prompts accept filters.competitor_ids. Previously the filter also kept responses that mention no competitors at all, so filtered citation rates and response counts included competitor-free responses. The filter is now strict: a response counts only when it mentions at least one of the given competitor ids, matching how the same filter behaves on the responses and sources endpoints. Requests that omit the filter are unchanged. Both endpoints are also significantly faster.

New: fact_review on content generation

POST /api/v1/content/generate (and the MCP tool create_content) now accepts an optional fact_review object for draft mode: { topic_ids, kept_claim_ids, excluded_claim_ids }. When present, generation uses exactly the reviewed knowledge-base pillars and kept facts instead of computing a selection of its own, so what a reviewer approved is what the draft is written from. pinned_claim_ids no longer influences selection when a review is present (the review already reflects any pins); it is still recorded for display. Requires the knowledge base to be enabled: sending fact_review with kb_mode: "none" or a non-draft mode returns a validation error. Content generation is also now strictly pillar-scoped: knowledge-base claims without a pillar are never used, whether selected automatically, pinned via pinned_claim_ids, or kept via fact_review. Ids that resolve to an unfiled claim are dropped and reported in the run’s selection outcome. File a fact under a pillar to make it available to generation.

New: 409 when deleting a website that holds your subscription

POST /api/v1/websites/{website_id}/delete now returns 409 Conflict instead of silently succeeding when the target website carries your organization’s subscription. Plans attach per organization, but subscriptions bought before that change sit on one specific website. Deleting that website used to soft-delete the record while leaving the subscription and its credit balance stranded on it: billing continued, and every remaining website reported zero credits. The endpoint now refuses the delete and returns the reason, so the plan can be cancelled or moved first. Deletes of websites that do not hold a subscription are unaffected, as are all restore calls. If you delete websites programmatically, handle 409 as “cancel or move billing first”, not as a retryable error.

Fixed: cited was always false on responses

POST /api/v1/responses and GET /api/v1/responses/{response_id} returned cited: false on every response, even when the response’s own sources[] contained your website’s domain. The flag was read from a stored value that the ingestion pipeline never populated. It is now derived from the response’s sources at read time: cited is true when any source resolves to your website’s root domain (protocol, www., and letter-case insensitive). The fix applies to all historical responses, not just new ones, and the response shape is unchanged. Aggregate citation metrics (such as the sources endpoints) were computed independently and were always correct.

Fixed: Content detail returned null text for brief-stage drafts

GET /api/v1/content/{content_id} (and the MCP tool get_content_detail) returned status, brief, body, and every other generation field as null for any draft whose article had not been written yet, even though the brief existed and was visible in the dashboard. The bug hid exactly the pieces most worth reading over the API: drafts parked at generated_brief awaiting brief approval. These now return their brief and status correctly. A genuinely unknown or inaccessible content_id continues to return 404; null fields now only mean the field is actually empty.

New: stage on the content status endpoint

GET /api/v1/content/{content_id}/status (and the MCP tool get_content_status) now returns a stage field alongside the normalized status. status is unchanged (running | succeeded | failed), but it cannot distinguish “actively generating” from “brief ready, waiting for approval”: both report running, and a draft created with auto_approve: false waits at the brief stage indefinitely. stage exposes the raw pipeline stage (scheduled, pending, pending_brief, generated_brief, pending_article, generated, done, failed, or null when generation has not started), so a poller seeing stage: "generated_brief" knows to call POST /api/v1/content/{content_id}/brief/approve rather than keep waiting.

New: Review and iterate on briefs and drafts

Content generation used to be one-shot: you could start a piece and poll it, but changing what came back meant generating a new one and paying for it again. You can now read a draft, revise it with a plain-language instruction, and step back through its history — for the brief as well as the article. Every revision is recorded as a version, so nothing is overwritten irrecoverably. The brief and the article share one timeline: get_content_versions tells the whole story of a piece, from the brief it was planned with to the article it became. The brief phase. Pass auto_approve: false to POST /api/v1/content/generate and the piece stops at generated_brief instead of writing the article. That gate already existed; these endpoints make it usable end to end.
  • GET /api/v1/content//draft — The current text: article body, brief, meta description, status, and the latest version number. A narrow read for when you only want the text — GET /api/v1/content/{content_id} remains the full record. Also available as the MCP tool get_content_draft.
  • POST /api/v1/content//brief/revise — Rewrites the brief from an instruction (“add a section on pricing objections”). Also revise_brief.
  • POST /api/v1/content//brief/approve — Approves the brief and starts the article from it, as the brief currently stands. Consumes content credits. Also approve_brief.
The article phase.
  • POST /api/v1/content//revise — Rewrites the article from an instruction. Iterate by calling it again; each call starts from the current text, so there is no version to pass. Also revise_content.
  • POST /api/v1/content//versions — The version history, newest first, with each entry’s label, how it was produced, and who made it. POST rather than GET because it takes pagination in the body. Also get_content_versions.
  • GET /api/v1/content//versions/ — The full text of one version. Fetch two and diff them yourself to see what a pass changed. Also get_content_version.
  • POST /api/v1/content//versions//restore — Brings an earlier version’s text back as the current draft. Also restore_content_version.
Three things worth knowing:
  • Restoring never deletes. The restored text is appended as a new version on top of the history, so the passes in between stay readable and restorable, and the history records that a restore happened.
  • Revising is not regenerating. A revision edits the text you already have; it does not re-run research or scraping. Revising a brief after the article exists corrects the brief for a future regeneration — it does not rewrite the article.
  • A piece being generated cannot be revised. While a generation is in flight these endpoints return 409; poll GET /api/v1/content/{content_id}/status until it reports succeeded.

New: Create saved views over MCP

You can now create saved views (dashboard filter presets) through the MCP server. A saved view captures a named set of filters and appears in the dashboard filter bar for every member of the website.
  • create_saved_view (MCP tool) — Creates a saved view from a name, an optional icon, and a filters object. The filters payload is stored verbatim (keys are not case-mapped) and is capped at 64KB. There is deliberately no REST equivalent: creating filter presets is an assistant workflow, not a partner-integration one.
Views created on an API-key MCP connection have no owning user: user_id is null in GET /api/v1/saved-views responses (previously it was always a user ID) and the dashboard shows them as created via API. Such views can only be edited or deleted by website admins, in the dashboard. Views created over a sign-in MCP connection are attributed to the connected user, same as views created in the dashboard.

New: AthenaHQ plugin for ChatGPT

AthenaHQ is now available as an official plugin in ChatGPT, so connecting no longer requires a custom MCP connector or developer mode. Install the plugin, sign in with your AthenaHQ account, and mention @AthenaHQ in any conversation to ask about share of voice, citations, sources, tracked prompts, and more. Plugin connections are read-only, same as other sign-in connections. See MCP Server for details.

New: Attributes (brand-perception keywords)

This release introduced retrieval of brand-perception attributes and how often each appeared in AI responses. The August 2026 update changed attributes to neutral traits, such as Pricing or Support responsiveness, with positive and negative observations of the same trait. Current attributes do not have a fixed sentiment.
  • GET /api/v1/attributes — Lists the attributes tracked for a website ({ "id", "name", "positive" }). Use it to discover attribute IDs. Also available as the MCP tool get_attributes.
  • POST /api/v1/attributes/cumulative — Per attribute, how many responses mentioned it across the date range, and the percentage. Attributes nobody mentioned are returned with response_count 0 rather than omitted. Also available as the MCP tool get_attribute_metrics.
  • POST /api/v1/attributes/competitors — The same breakdown per tracked competitor, so you can compare how your brand is described against theirs. Each competitor has its own denominator; competitors with no responses in the date range are omitted. Also available as the MCP tool get_competitor_attribute_metrics.
  • POST /api/v1/attributes/time-series — The daily trend for one attribute, brand and competitors side by side. Requires an attribute_id from the list endpoint. Days with no data are returned as zeros, so the series is gap-free.
Two things worth knowing when reading this data:
  • positive describes the selected directional observation series. Attribute names are neutral and the same attribute ID can have both positive and negative observations.
  • percentage is the share of analyzed responses, not of all responses. Athena samples responses for attribute extraction, and un-analyzed ones are excluded from the denominator.

New: Knowledge Base writes

You can now feed a website’s brand Knowledge Base through the API. Write inputs are declarative — fact text, optional source URL, optional pillar — with no status or approval field: every fact runs the full ingestion pipeline (deduplication, approval gates, pillar routing) and the response reports what the pipeline decided, per fact. The outcome values (approved, pending, duplicate) form an extensible enum: new values may be added as the pipeline evolves, so treat unknown values as non-terminal. Both endpoints require a website-admin API key and the Knowledge Base to be enabled for the organization.
  • POST /api/v1/knowledge-base/facts — Adds 1-50 brand facts in one call and returns one outcome per fact ({ "index", "outcome", "fact_id", "pillar_id" }), which doubles as the ingest report. Facts pinned to a pillar_id land exactly there; the rest are routed automatically and may land unfiled. Also available as the MCP tool add_brand_facts on API-key connections and org-bound OAuth sessions with brand-knowledge write permission.
  • POST /api/v1/knowledge-base/pillars — Creates a pillar from a name and optional description. Get-or-create semantics: an existing name returns the pillar with created: false, keeping retries idempotent. Also available as the MCP tool create_pillar.

New: Knowledge Base reads

You can now read a website’s brand Knowledge Base through the API: the approved brand facts (statements like “Our Pro plan starts at $99/month”, each backed by a source) and the pillars that organize them. All four endpoints require the Knowledge Base to be enabled for the organization; when it isn’t, they return a 403 with "Knowledge Base is not enabled for this organization".
  • GET /api/v1/knowledge-base/facts/search — Semantic search over a website’s approved brand facts. Combines vector similarity with full-text search, so it also reaches facts not filed under any pillar. Also available as the MCP tool search_brand_facts.
  • GET /api/v1/knowledge-base/facts — Lists brand facts, newest first, with offset paging and filters: pillar, review status (defaults to approved), source type, and unfiled=true for facts not under any published pillar. Also available as the MCP tool get_brand_facts.
  • GET /api/v1/knowledge-base/pillars — Lists the website’s pillars (excluding archived ones) with approved-fact counts, whether a synthesized document exists, and when each was last researched. Also available as the MCP tool get_pillars.
  • GET /api/v1/knowledge-base/pillars//document — Fetches a pillar’s synthesized markdown document; returns document: null when the pillar exists but has no document yet. Also available as the MCP tool get_pillar_document.

New: Persona discovery

You can now discover a website’s personas through the API. Persona IDs previously appeared as bare persona_id values in other responses with no way to resolve them to names.
  • New GET /api/v1/personas endpoint lists a website’s personas with per-persona assigned-prompt counts ({ "id", "name", "description", "prompt_count" }). Also available as the MCP tool get_personas.

New: Content generation endpoints and MCP actions

You can now create and manage content in Athena programmatically, closing the loop from insight to action: spot a gap, then draft content for it without leaving the API or your AI assistant.
  • POST /api/v1/content/generate — Starts the content pipeline in one of four modes: draft (write new content from prompt_ids), snipe (outrank a competitor URL), optimize (improve an existing URL for AI search), or slice (split one URL into several articles). Returns durable content_ids you can poll. Consumes content credits. Requires a website-admin API key. Also available as the MCP tool create_content on API-key connections.
  • GET /api/v1/content//status — Returns the normalized generation status (running, succeeded, or failed) for one content item. Poll it per content_id returned by the generate endpoint. Also available as the MCP tool get_content_status.
  • PATCH /api/v1/content/ — Renames a content item’s title. The URL is the item’s stable identity and cannot be changed. Requires a website-admin API key. Also available as the MCP tool rename_content on API-key connections (initially shipped as update_content; renamed because the tool only renames the title).
  • DELETE /api/v1/content/ — Permanently deletes a content item (no undo). Requires a website-admin API key. Also available as the MCP tool delete_content on API-key connections.
Listing and reading content were already available (POST /api/v1/content, GET /api/v1/content/, and the MCP tools get_tracked_content / get_content_detail).

New: Topic management endpoints

You can now manage topics via the API, completing the programmatic prompt management round trip: create a topic, get back its id, and pass it as prompts[].topic_id when creating prompts.
  • GET /api/v1/topics — Lists a website’s active topics with the count of active prompts in each. Use it to resolve a topic name to its id. Also available as the MCP tool get_topics.
  • POST /api/v1/topics — Creates a topic on a website from a name and optional description. Get-or-create semantics: if an active topic with the same name already exists, the existing topic is returned with created: false instead of failing, so retries and re-imports are idempotent. Requires a website-admin API key. Also available as the MCP tool create_topic on API-key connections.
  • PATCH /api/v1/topics/ — Updates a topic’s name and/or description. Renames don’t affect metrics or prompt grouping (everything joins on the topic’s id). Requires a website-admin API key. Also available as the MCP tool update_topic on API-key connections.
  • DELETE /api/v1/topics/ — Soft-deletes a topic. By default its prompts are kept (they stay active and can be re-categorized); pass delete_prompts=true to also soft-delete every active prompt in the topic. Reversible: creating a topic with the same name restores it. Requires a website-admin API key. Also available as the MCP tool delete_topic on API-key connections.

New: Prompt tag discovery

You can now discover a website’s prompt tags through the API. Previously the prompt_tags filter on GET /api/v1/prompts and the tag_ids field on POST /api/v1/prompts required tag UUIDs that the API did not expose anywhere.
  • GET /api/v1/prompts now includes a tags array on each prompt ([{ "id", "name" }], sorted by name, empty when untagged). Existing fields are unchanged.
  • New GET /api/v1/prompt-tags endpoint lists a website’s tags with per-tag prompt counts ({ "id", "name", "prompt_count" }), so a tag picker can populate without fetching all prompts. Also available as the MCP tool get_prompt_tags.

Changed: Provisioning responses report billing entity creation

Website and group creation responses now include billingEntityCreated: whether the billing entity that backs credits was provisioned. When false, the website or group was created but cannot hold credits yet: setting credits fails with a 400 until it is repaired (for groups, call PATCH /api/v1/groups/ with billing_enabled: true; for websites, contact AthenaHQ support). Previously a failed billing setup was silent and only surfaced when setting credits later. Affects POST /api/v1/websites and POST /api/v1/websites/provision (always present), and POST /api/v1/groups (present when billing_enabled is requested).

New: Write tools on the MCP server

The MCP server is no longer read-only. Connections authenticated with an API key now get write tools mirroring the corresponding REST endpoints 1:1: same field names (snake_case), same validation, same audit logging. OAuth-connected MCP sessions remain read-only and do not see these tools.
  • create_prompts, update_prompt, set_prompts_status, delete_prompt: the prompt management operations (POST/PATCH/DELETE /api/v1/prompts...).
  • create_location, update_location, delete_location: the location management operations (POST/PATCH/DELETE /api/v1/locations...).
  • start_response_streaming: starts a response-streaming run (POST /api/v1/response-streaming/start). Status polling was already available as get_response_streaming_status.
Read endpoints that previously had no MCP tool are now exposed too, completing 1:1 coverage of the procedure-backed API surface: get_user_by_email (POST /api/v1/users/by-email, global API key required), get_response_detail (GET /api/v1/responses/), get_groups and get_group_detail (GET /api/v1/groups…, global API key required), check_ai_access (POST /api/v1/ai-access/check), and get_position_distribution (POST /api/v1/metrics/position/distribution).

New: Role management endpoints

You can now manage roles via the API — list the built-in system roles (Admin, Editor, Viewer, Billing) and your organization’s custom roles, and create, update, or delete custom roles with per-category permission matrices. Creating custom roles is available on the Enterprise plan. All role endpoints require a global API key.
  • GET /api/v1/roles — List all roles with their resolved permission matrices.
  • POST /api/v1/roles — Create a custom role (Enterprise plan).
  • GET /api/v1/roles/ — Fetch a single role.
  • PATCH /api/v1/roles/ — Update a custom role’s permissions.
  • DELETE /api/v1/roles/ — Delete a custom role (fails if still assigned).

Changed: Assign any role with role_id

Invites and organization member role updates now accept a role_id — a system role or one of your organization’s custom roles — in addition to the coarse role (admin/viewer), which keeps working unchanged. role and role_id are mutually exclusive. Affects POST /api/v1/invites, POST /api/v1/invites/bulk, and PATCH /api/v1/organizations/members/.

Changed: Mention rate and share of voice entries include relative mention rate

The mention rate and share of voice endpoints now return a relative_mention_rate field for every brand/competitor entry: the entity’s mentions as a percentage (0-100) of responses that mention at least one tracked brand. This is the same “Relative” rate shown in the dashboard’s mention rate card. The existing mention_rate field stays absolute (share of all responses), and all existing fields are unchanged. Affects POST /api/v1/metrics/mention-rate/cumulative, POST /api/v1/metrics/mention-rate/time-series, POST /api/v1/metrics/share-of-voice/cumulative, and POST /api/v1/metrics/share-of-voice/time-series (per entry, per day on the time-series variants). Also returned by the MCP tools get_mention_rate_cumulative, get_mention_rate_time_series, get_share_of_voice_cumulative, and get_share_of_voice_time_series.

New: Trigger response streaming via the API

You can now start a response-streaming run (query the AI models for a website’s prompts and ingest the answers) and poll its progress via the API — the same run the dashboard’s stream dialog kicks off.
  • POST /api/v1/response-streaming/start — Start a run for all active prompts or a selection (up to 1,000), with optional model selection, prompt variations, personas, and locations. Returns the workflow_id and status: "running". Requires a website-admin API key.
  • GET /api/v1/response-streaming/status — Read the latest run’s state (idle / running / completed / failed / cancelled) and queue progress (pending, processing, completed, failed, total, percentage). Pass workflow_id to read a specific run.

New: Look up a user by email

You can now resolve a user’s user_id from their email address, scoped to your organization. The email is sent in the request body so it never lands in logs.
  • POST /api/v1/users/by-email — Returns the user’s user_id, name, and access_type (organization or website) when the email belongs to your organization; 404 otherwise (indistinguishable from an unknown email, so it cannot be used to enumerate accounts). Requires a global API key.

New: Remove a user from websites

You can now batch-remove a user’s explicit website memberships in one call — the companion to listing a user’s websites.
  • DELETE /api/v1/users//websites — Remove the user from the websites named in the website_ids query parameter (comma-separated, up to 100). Idempotent: websites where the user has no explicit membership return status not_a_member instead of failing. Requires a global API key.

Changed: Filter prompts by tag

GET /api/v1/prompts now accepts an optional prompt_tags filter (one or more prompt tag IDs) plus a prompt_tags_operator (is_any_of (default) or has_all_of) to control how multiple tags combine. All tag IDs must belong to the same website as website_id. Also available on the MCP get_prompts tool.

New: Read a single content item

You can now fetch the full detail of one tracked content item — including its actual text: the content brief (brief) and the article/page body (body, which holds generated drafts, optimize rewrites, snipe articles, authored text, and scraped tracked-page bodies). The response also carries status, so you can poll it while generation runs — generated_brief means the brief is ready while the article is still being written.
  • GET /api/v1/content/ — Full detail for one content item, including brief, body, cited source URLs, links, and status. Also available as the MCP tool get_content_detail.

Changed: manual content type filter

The tracked-content endpoint’s content_type filter now accepts manual — user-authored content created directly in the editor (the “Blank Editor” flow), as opposed to Athena-generated or imported content. Existing values are unchanged. Affects POST /api/v1/content (content_type filter).

June 2026

Changed: Member role is now the role’s slug

Member-list endpoints now return each member’s role as its slug — the role’s normalized name (lowercase, no spaces) — rather than only admin/viewer. Built-in roles are admin, viewer, editor, and billing; a custom role uses its own id (e.g. contentmanager). admin and viewer are unchanged, so integrations that check for them keep working; members that previously reported viewer because of a non-admin role (Editor, Billing, custom) now surface their real role. Affects GET /api/v1/organizations/members, GET /api/v1/websites//members, GET /api/v1/groups//members, and GET /api/v1/users//websites.

New: Manage prompts via the API

Prompts are no longer read-only. You can now create, update, pause/unpause, and delete prompts via the API — the same operations the dashboard performs, with the same geography and ownership validation.
  • POST /api/v1/prompts — Create one or more prompts (up to 1,000 per request), with optional topic, geography, persona/location/tag associations, and paused state.
  • PATCH /api/v1/prompts/ — Update a prompt’s text, type, volume, topic, geography, or locations. Returns a before/after snapshot.
  • PATCH /api/v1/prompts/status — Pause or unpause multiple prompts in one transaction.
  • DELETE /api/v1/prompts/ — Delete a prompt (soft when it has responses, hard otherwise).

New: Manage locations via the API

You can now create, update, and delete locations via the API. country is accepted case-insensitively and stored as its canonical label, and any attached prompts are validated against the location’s country.

May 2026

New: MCP server for Claude, ChatGPT, and other AI assistants

You can now connect AthenaHQ to Claude, ChatGPT, and other AI assistants over the Model Context Protocol. The read-only server exposes your metrics, prompts, responses, content, sources, competitors, and pitches as tools, so an assistant can answer questions about your AI search visibility in plain language.
  • Connect Claude.ai or ChatGPT by adding a custom connector and signing in with your AthenaHQ account (no API key).
  • Connect other MCP clients (including Claude Code) with an API key via the x-api-key header.
Server URL: https://api.athenahq.ai/api/mcp.

New: Sources endpoints

You can now pull cited-sources data via the API (and via MCP — these are the get_sources / get_source_pages tools). Each row is classified as owned / competitor / partner / third_party based on your tracked competitors, configured partners (affiliates), and per-website source-type overrides.
  • POST /api/v1/sources — Domain-grouped sources for a website. Top cited root domains with citation, mention, brand-mention, and impression metrics for the date range. Supports filters, search, sort, and pagination.
  • POST /api/v1/source-pages — Per-URL companion to the above. Returns one row per cited normalized_url with the same metrics plus a per-URL daily citation sparkline (daily_mentions).

New: Pitch Workspace endpoints

You can now read pitch workspace reports via the API. Pitches are organization-level resources (not scoped to a single website), so these endpoints require a global API key — website-scoped keys are rejected with 403 Forbidden.
  • GET /api/v1/pitches — List non-deleted pitches in the calling organization, with company metadata and run status.
  • GET /api/v1/pitches/ — Fetch a single pitch report: tracked competitors with mention/sentiment stats, generated prompts, positive/negative attributes for both the target and its competitors, top citing domains, and aggregate metrics (total_responses, brand_mentions, sentiment_score, response_rate).

New: Content Hub endpoints

You can now read Content Hub data — tracked URLs, citation/impression metrics, and per-URL prompt breakdowns — via the API. These power GEO reporting that includes both 1st-party (Athena-created) and 3rd-party (link-building placements, Reddit, partner posts) content in one place.
  • GET /api/v1/content/sheets — List the Content Hub tabs configured for a website. Call this first to discover the sheet_ids you can filter by.
  • POST /api/v1/content — Paginated tracked content with citations, citation %, estimated impressions, and total responses for the date range. Filter by sheet_id, content_type, mentioned, and the standard filter knobs.
  • POST /api/v1/content/prompts — For a single content item, the list of prompts whose AI responses cite it — with per-prompt citation counts and percentages. Works for both topic-attached and 3rd-party content without a topic assignment.

New: Saved views endpoint

You can now fetch saved views (filter presets) for a website via the API. Saved views capture a named set of dashboard filters that can be reused across the product.
  • GET /api/v1/saved-views — List saved views for a website. Returns each view’s filters, icon, and creator metadata.

New: Group saved views endpoint

You can now fetch group-level saved views via the API. Group saved views capture filter state shared across the websites in a group rather than scoped to a single website.

April 2026

Updated: Multi-country support on prompts

GET /api/v1/prompts now returns a countries array on each prompt, listing every country targeted by that prompt along with an is_primary flag. The primary country is listed first; remaining countries follow in alphabetical order.
  • countries: [{ country: string, is_primary: boolean }] — New field. Empty array when the prompt has no countries assigned.
  • country: string — Deprecated, but still populated for backward compatibility. Returns the primary country (or the alphabetically first, or "United States" as a final fallback). New integrations should read countries instead.

New: Delete and restore websites

You can now delete and restore websites via the API. Deleting removes a website from your organization and stops all associated processing; restoring brings it back exactly as it was, with all historical data preserved. This is the recommended flow for partner integrations that need to handle customer churn and reactivation.
  • POST /api/v1/websites/{website_id}/delete — Deletes a website.
  • POST /api/v1/websites/{website_id}/restore — Reactivates a deleted website.
Both endpoints require a global API key. Both are idempotent: re-calling them on a website that is already in the target state returns 200.

Updated: Invite users to groups

POST /api/v1/invites now accepts type: "group" with a group_id, granting the user access to every website in the group. POST /api/v1/invites/bulk accepts a new group_ids array (mutually exclusive with website_ids, max 50) for inviting a user to multiple groups in one request. Both branches behave consistently with the existing org/website invites: invite_method: "direct" adds the user immediately (creating a new user account automatically if the email has none) without sending any email, while invite_method: "email" creates a pending invite and sends an invitation. Group invites require a global API key. Users who are already members of the organization cannot be invited to individual groups — they already have cascade access to every group.

New: Team management endpoints

You can now manage team members and pending invites programmatically. These endpoints unblock automated user provisioning, role changes, and cleanup without touching the AthenaHQ dashboard. List members and access
  • GET /api/v1/organizations/members — Lists all members of your organization with their email, name, and role. Requires a global API key.
  • GET /api/v1/websites//members — Lists all users with access to a website, tagged with access_type (organization for implicit access via org membership, website for explicit website membership). Accepts a global or website-scoped API key.
  • GET /api/v1/users//websites — Lists the websites a specific user has access to within your organization. Requires a global API key.
Update and remove members Revoke pending invites Mutating endpoints include safety guards: you cannot modify the API key’s owning user, the organization owner, or the last remaining admin of an organization.

Updated: Direct invite responses now include user_id

POST /api/v1/invites and POST /api/v1/invites/bulk now return user_id in their success responses for invite_method: "direct" only. In direct mode, the user is looked up or created immediately, so returning the ID gives you everything you need to call the new member-management endpoints without a discovery round-trip. Email-method invites intentionally do not return user_id — doing so would let any API key holder enumerate account existence across tenants by email. This change is backward-compatible; existing integrations continue to work unchanged.

New: Provision Website endpoint (Beta)

You can now create a fully configured website in a single API call, skipping the onboarding wizard. Optionally set up competitors, prompts, and a processing schedule — all in one request. This endpoint is currently in beta — the request and response schemas may change.

New: Get group credits

You can now retrieve the current credit balance for a group, including the monthly and one-off breakdown.

March 2026

New: Get and update individual groups

You can now fetch a single group by ID and update it — rename the group, add websites, or remove websites. Both endpoints require a global API key.

New: Bulk invite to websites

You can now invite a user to multiple websites in a single API call. The endpoint processes each website independently and returns per-website results, making it easy to onboard users across your organization.

February 2026

New: Groups endpoints

You can now create and manage groups of websites via the API. Groups let you organize multiple websites under a single entity within your organization. Both endpoints require a global API key.