/** * Typed wrappers around every ``/api/visibility/*`` endpoint on the agent * backend. Each function unwraps the standard ``BaseResponse`` envelope so * callers receive the ``data`` payload directly. * * HTTP calls go through {@link BaseAgentService}, which owns its own axios * instance - no global interceptors, so 403s don't trigger the session * refresh modal that the main Recomaze axios instance wires up. */ import type { AxiosError } from 'axios'; import BaseAgentService from '../BaseAgentService'; import type { AdoptFromChatRequest, AdoptFromChatResponse, StopTrackingChatRequest, StopTrackingChatResponse, ArticleDeleteResponse, ArticleAeoChecklistResponse, ArticleDetailResponse, ArticleDispatchResponse, ArticleEditRequest, ArticleGenerateRequest, ArticleListResponse, ArticleRegenerateRequest, ArticleVersionDetailResponse, ArticleVersionsResponse, BrandCreateRequest, BrandListResponse, BrandResponse, BrandUpdateRequest, StoreContextResponse, StoreContextEditRequest, BulkGenerateRequest, BulkJobResponse, ChatPromptVolumesResponse, CitedSourcesResponse, CompetitorLeaderboardResponse, FunnelCoverageResponse, ManualCompetitorAddRequest, ManualCompetitorListResponse, ExcludedCompetitorAddRequest, ExcludedCompetitorListResponse, GenerateSuggestionsFromBriefRequest, PageTemplate, PageTemplateCreateRequest, PageTemplateListResponse, PageTemplatePreset, PageTemplatePresetListResponse, PageTemplateUpdateRequest, ProblemsResponse, PromptListResponse, PromptMetricsResponse, PromptReplaceRequest, PromptResponse, PromptUpdateRequest, PromptResponsesResponse, ArticlePromptImpactResponse, RefreshSuggestionsRequest, ScanDispatchResponse, ScanHistoryResponse, ScanTriggerRequest, SuggestionEntry, SuggestionListResponse, VisibilityBaseResponse, VisibilitySettingsResponse, VisibilitySettingsUpdateRequest, WeeklyReportResponse, } from './visibility.interface'; import { InsufficientCreditsError } from './visibility.interface'; import { VISIBILITY_ARTICLES, VISIBILITY_ARTICLES_BULK, VISIBILITY_ARTICLES_GENERATE, VISIBILITY_BRANDS, VISIBILITY_PAGE_TEMPLATE_PRESETS, VISIBILITY_SETTINGS, visibilityArticle, visibilityArticleAeoChecklist, visibilityArticlePublish, visibilityArticlePublishToBlog, visibilityArticleRegenerate, visibilityArticlesBulkJob, visibilityArticleUnpublish, visibilityArticleVersion, visibilityArticleVersions, visibilityBrand, visibilityBrandStoreContext, visibilityBrandStoreContextRegenerate, visibilityBrandAdoptFromChat, visibilityBrandStopTrackingChat, visibilityBrandChatVolumes, visibilityBrandCitedSources, visibilityBrandCompetitors, visibilityBrandFunnelCoverage, visibilityBrandProblems, visibilityBrandPromptMetrics, visibilityBrandPrompts, visibilityBrandPromptsRegenerate, visibilityArticlePromptImpact, visibilityPrompt, visibilityPromptRegenerate, visibilityBrandReport, visibilityBrandScan, visibilityBrandScans, visibilityBrandSuggestion, visibilityBrandSuggestions, visibilityBrandSuggestionsFromBrief, visibilityBrandSuggestionsRefresh, visibilityPageTemplate, visibilityPageTemplates, visibilityPromptResponses, visibilityTrackedCompetitor, visibilityTrackedCompetitors, visibilityExcludedCompetitor, visibilityExcludedCompetitors, } from './visibility.routes'; const unwrap = (response: VisibilityBaseResponse): T => response.data; const throwAgentError = (err: unknown, fallback: string): never => { const axiosErr = err as AxiosError> | undefined; if (!axiosErr?.isAxiosError) { throw err; } const status = axiosErr.response?.status; const body = axiosErr.response?.data; const pick = (key: string): string | undefined => typeof body?.[key] === 'string' ? (body[key] as string) : undefined; const serverMessage = pick('message') || pick('detail') || pick('error') || fallback; if (status === 402) { throw new InsufficientCreditsError(serverMessage); } throw new Error(serverMessage); }; /* ------------------------------------------------------------------ */ /* settings */ /* ------------------------------------------------------------------ */ export const fetchSettings = async ( clientId: string, token: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(VISIBILITY_SETTINGS, clientId, token); return unwrap(response); }; export const updateSettings = async ( clientId: string, token: string, body: VisibilitySettingsUpdateRequest ): Promise => { const response = await BaseAgentService.putWithClientId< VisibilityBaseResponse >(VISIBILITY_SETTINGS, body, clientId, token); return unwrap(response); }; /* ------------------------------------------------------------------ */ /* brands */ /* ------------------------------------------------------------------ */ export const listBrands = async ( clientId: string, token: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(VISIBILITY_BRANDS, clientId, token); return unwrap(response); }; export const createBrand = async ( clientId: string, token: string, body: BrandCreateRequest ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(VISIBILITY_BRANDS, body, clientId, token); return unwrap(response); }; export const getBrand = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityBrand(brandId), clientId, token); return unwrap(response); }; export const patchBrand = async ( clientId: string, token: string, brandId: string, body: BrandUpdateRequest ): Promise => { const response = await BaseAgentService.patchWithClientId< VisibilityBaseResponse >(visibilityBrand(brandId), body, clientId, token); return unwrap(response); }; export const getStoreContext = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityBrandStoreContext(brandId), clientId, token); return unwrap(response); }; export const editStoreContext = async ( clientId: string, token: string, brandId: string, body: StoreContextEditRequest ): Promise => { const response = await BaseAgentService.putWithClientId< VisibilityBaseResponse >(visibilityBrandStoreContext(brandId), body, clientId, token); return unwrap(response); }; export const regenerateStoreContext = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityBrandStoreContextRegenerate(brandId), {}, clientId, token); return unwrap(response); }; /* ------------------------------------------------------------------ */ /* scans */ /* ------------------------------------------------------------------ */ /** * Dispatch a fresh scan. The backend returns ``402`` when the merchant has * run out of credits. We detect that case and throw a typed * {@link InsufficientCreditsError} so the UI can render a friendly warning * banner. */ export const startScan = async ( clientId: string, token: string, brandId: string, body: ScanTriggerRequest = {} ): Promise => { const result = await BaseAgentService.probePostWithClientId< VisibilityBaseResponse >(visibilityBrandScan(brandId), body, clientId, token); if (result.ok && result.data) { return unwrap(result.data); } if (result.status === 402) { throw new InsufficientCreditsError(); } throw new Error( `Failed to start scan (status ${result.status || 'network'}).` ); }; export const listScans = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityBrandScans(brandId), clientId, token); return unwrap(response); }; export const getWeeklyReport = async ( clientId: string, token: string, brandId: string, weekIso: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityBrandReport(brandId, weekIso), clientId, token); return unwrap(response); }; /* ------------------------------------------------------------------ */ /* competitors */ /* ------------------------------------------------------------------ */ export const getCompetitors = async ( clientId: string, token: string, brandId: string, weekIso?: string, limit = 50 ): Promise => { const params = new URLSearchParams(); if (weekIso) params.set('week_iso', weekIso); params.set('limit', String(limit)); const url = `${visibilityBrandCompetitors(brandId)}?${params.toString()}`; const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(url, clientId, token); return unwrap(response); }; export const listTrackedCompetitors = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityTrackedCompetitors(brandId), clientId, token); return unwrap(response); }; export const addTrackedCompetitor = async ( clientId: string, token: string, brandId: string, body: ManualCompetitorAddRequest ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityTrackedCompetitors(brandId), body, clientId, token); return unwrap(response); }; export const removeTrackedCompetitor = async ( clientId: string, token: string, brandId: string, competitorDomain: string ): Promise => { const response = await BaseAgentService.deleteWithClientId< VisibilityBaseResponse >(visibilityTrackedCompetitor(brandId, competitorDomain), clientId, token); return unwrap(response); }; /** * Suppress a competitor domain from the leaderboard and skip it on future * scans. Idempotent - re-excluding the same domain returns the existing * list unchanged. * * @param {string} clientId - Recomaze client id used for agent-backend auth. * @param {string} token - Recomaze auth token used for agent-backend auth. * @param {string} brandId - Target brand id. * @param {ExcludedCompetitorAddRequest} body - Domain to exclude. * @returns {Promise} Updated excluded-domains list. */ export const excludeCompetitor = async ( clientId: string, token: string, brandId: string, body: ExcludedCompetitorAddRequest ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityExcludedCompetitors(brandId), body, clientId, token); return unwrap(response); }; /** * Bring a previously excluded competitor back into the leaderboard. * * @param {string} clientId - Recomaze client id used for agent-backend auth. * @param {string} token - Recomaze auth token used for agent-backend auth. * @param {string} brandId - Target brand id. * @param {string} competitorDomain - Canonical competitor domain. * @returns {Promise} Updated excluded-domains list. */ export const unexcludeCompetitor = async ( clientId: string, token: string, brandId: string, competitorDomain: string ): Promise => { const response = await BaseAgentService.deleteWithClientId< VisibilityBaseResponse >(visibilityExcludedCompetitor(brandId, competitorDomain), clientId, token); return unwrap(response); }; /* ------------------------------------------------------------------ */ /* prompts */ /* ------------------------------------------------------------------ */ export const listPrompts = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityBrandPrompts(brandId), clientId, token); return unwrap(response); }; /** * Per-prompt composite-score trends for a brand's prompt list (Beta). * * @param {string} clientId - Recomaze client id used for agent-backend auth. * @param {string} token - Recomaze auth token used for agent-backend auth. * @param {string} brandId - Target brand id. * @returns {Promise} One trend per tracked prompt. */ export const getPromptMetrics = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityBrandPromptMetrics(brandId), clientId, token); return unwrap(response); }; export const updatePrompts = async ( clientId: string, token: string, brandId: string, body: PromptReplaceRequest ): Promise => { const response = await BaseAgentService.putWithClientId< VisibilityBaseResponse >(visibilityBrandPrompts(brandId), body, clientId, token); return unwrap(response); }; /** * Regenerate the whole prompt set from a fresh live-site crawl. The backend * re-reads what the brand actually sells (grounded) and rebuilds the 16 * prompts, optionally steered by a corrected business type and free-text * guidance. The old set is disabled, not deleted. * * @param clientId {string} Recomaze client id. * @param token {string} Recomaze auth token. * @param brandId {string} Target brand id. * @param body {{ custom_guidance?: string; business_type?: string }} Optional * steer: what the brand sells / corrected business type. * @returns {Promise} The freshly generated prompt list. */ export const regeneratePrompts = async ( clientId: string, token: string, brandId: string, body?: { custom_guidance?: string; business_type?: string } ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityBrandPromptsRegenerate(brandId), body ?? {}, clientId, token); return unwrap(response); }; export const updatePrompt = async ( clientId: string, token: string, brandId: string, promptId: string, body: PromptUpdateRequest ): Promise => { const response = await BaseAgentService.patchWithClientId< VisibilityBaseResponse >(visibilityPrompt(brandId, promptId), body, clientId, token); return unwrap(response); }; /** * Permanently remove a single tracked prompt. * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @param brandId {string} Target brand id. * @param promptId {string} Prompt id to delete. * @returns {Promise} Remaining tracked prompts. */ export const deletePrompt = async ( clientId: string, token: string, brandId: string, promptId: string ): Promise => { const response = await BaseAgentService.deleteWithClientId< VisibilityBaseResponse >(visibilityPrompt(brandId, promptId), clientId, token); return unwrap(response); }; /** * Regenerate a single tracked prompt with the LLM, keeping its category. An * optional free-text instruction steers the rewrite. The backend anchors it to * the brand's other prompts so it stays on-catalog, and persists the result. * * @param clientId {string} Recomaze client id. * @param token {string} Recomaze auth token. * @param brandId {string} Target brand id. * @param promptId {string} Prompt id to rewrite. * @param body {{ instruction?: string }} Optional rewrite instruction. * @returns {Promise} The regenerated prompt row. */ export const regeneratePrompt = async ( clientId: string, token: string, brandId: string, promptId: string, body?: { instruction?: string } ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityPromptRegenerate(brandId, promptId), body ?? {}, clientId, token); return unwrap(response); }; export const getPromptResponses = async ( clientId: string, token: string, brandId: string, promptId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityPromptResponses(brandId, promptId), clientId, token); return unwrap(response); }; /* ------------------------------------------------------------------ */ /* problems & suggestions */ /* ------------------------------------------------------------------ */ export const listProblems = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityBrandProblems(brandId), clientId, token); return unwrap(response); }; export const listSuggestions = async ( clientId: string, token: string, brandId: string, force: boolean = false ): Promise => { // force bypasses the 5-min server cache so a poll sees idea cards flip to // "generated" as soon as their content is ready. const base: string = visibilityBrandSuggestions(brandId); const url: string = force ? `${base}?force=true` : base; const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(url, clientId, token); return unwrap(response); }; export const refreshSuggestions = async ( clientId: string, token: string, brandId: string, body: RefreshSuggestionsRequest = {} ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityBrandSuggestionsRefresh(brandId), body, clientId, token); return unwrap(response); }; /** * Generate up to five article ideas from a merchant-supplied brief (typed * text or pasted txt/csv). The ideas persist as ``source = "user"`` * suggestions, so {@link generateArticle} works on them unchanged. * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @param brandId {string} Target brand id. * @param body {GenerateSuggestionsFromBriefRequest} Brief text + optional language. * @returns {Promise} The freshly generated ideas. */ export const generateSuggestionsFromBrief = async ( clientId: string, token: string, brandId: string, body: GenerateSuggestionsFromBriefRequest ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityBrandSuggestionsFromBrief(brandId), body, clientId, token); return unwrap(response); }; export const rejectSuggestion = async ( clientId: string, token: string, brandId: string, suggestionId: string ): Promise => { const response = await BaseAgentService.deleteWithClientId< VisibilityBaseResponse >(visibilityBrandSuggestion(brandId, suggestionId), clientId, token); return unwrap(response); }; /* ------------------------------------------------------------------ */ /* page templates */ /* ------------------------------------------------------------------ */ /** * List a brand's saved page-content briefs, optionally narrowed to one type. * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @param brandId {string} Target brand id. * @param contentType {string} [optional] Page-type filter. * @returns {Promise} The brand's templates, newest update first. */ export const listPageTemplates = async ( clientId: string, token: string, brandId: string, contentType?: string ): Promise => { const base = visibilityPageTemplates(brandId); const url = contentType ? `${base}?content_type=${encodeURIComponent(contentType)}` : base; const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(url, clientId, token); return unwrap(response).templates; }; /** * Fetch one saved page-content brief by id. * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @param brandId {string} Target brand id. * @param templateId {string} Template identifier. * @returns {Promise} The template row. */ export const getPageTemplate = async ( clientId: string, token: string, brandId: string, templateId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityPageTemplate(brandId, templateId), clientId, token); return unwrap(response); }; /** * Save a new page-content brief. * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @param brandId {string} Target brand id. * @param body {PageTemplateCreateRequest} The brief to persist. * @returns {Promise} The created template. */ export const createPageTemplate = async ( clientId: string, token: string, brandId: string, body: PageTemplateCreateRequest ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityPageTemplates(brandId), body, clientId, token); return unwrap(response); }; /** * Partially update a saved page-content brief. * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @param brandId {string} Target brand id. * @param templateId {string} Template to edit. * @param body {PageTemplateUpdateRequest} Fields to overwrite. * @returns {Promise} The updated template. */ export const updatePageTemplate = async ( clientId: string, token: string, brandId: string, templateId: string, body: PageTemplateUpdateRequest ): Promise => { const response = await BaseAgentService.patchWithClientId< VisibilityBaseResponse >(visibilityPageTemplate(brandId, templateId), body, clientId, token); return unwrap(response); }; /** * Delete a saved page-content brief. * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @param brandId {string} Target brand id. * @param templateId {string} Template to remove. * @returns {Promise} Resolves when the row is gone. */ export const deletePageTemplate = async ( clientId: string, token: string, brandId: string, templateId: string ): Promise => { await BaseAgentService.deleteWithClientId< VisibilityBaseResponse> >(visibilityPageTemplate(brandId, templateId), clientId, token); }; /** * Fetch the global Recomaze-shipped page-template presets, seeded server-side * and returned in curated gallery order. * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @returns {Promise} The preset catalog. */ export const listPageTemplatePresets = async ( clientId: string, token: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(VISIBILITY_PAGE_TEMPLATE_PRESETS, clientId, token); return unwrap(response).presets; }; /* ------------------------------------------------------------------ */ /* articles */ /* ------------------------------------------------------------------ */ export const generateArticle = async ( clientId: string, token: string, body: ArticleGenerateRequest ): Promise => { try { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(VISIBILITY_ARTICLES_GENERATE, body, clientId, token); return unwrap(response); } catch (err) { return throwAgentError(err, 'Failed to start generation.'); } }; export const bulkArticles = async ( clientId: string, token: string, body: BulkGenerateRequest ): Promise => { try { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(VISIBILITY_ARTICLES_BULK, body, clientId, token); return unwrap(response); } catch (err) { return throwAgentError(err, 'Failed to start bulk generation.'); } }; export const getBulkProgress = async ( clientId: string, token: string, jobId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityArticlesBulkJob(jobId), clientId, token); return unwrap(response); }; export const listArticles = async ( clientId: string, token: string, filters: { status?: string; brand_id?: string; language?: string; content_type?: string; force?: boolean; limit?: number; } = {} ): Promise => { const params = new URLSearchParams(); if (filters.status) params.set('status', filters.status); if (filters.brand_id) params.set('brand_id', filters.brand_id); if (filters.language) params.set('language', filters.language); if (filters.content_type) params.set('content_type', filters.content_type); if (filters.limit) params.set('limit', String(filters.limit)); // The content list is cached for 5 min server-side; callers polling a live // generation pass force to bypass it so a freshly-ready content shows up. if (filters.force) params.set('force', 'true'); const query = params.toString(); const url = query ? `${VISIBILITY_ARTICLES}?${query}` : VISIBILITY_ARTICLES; const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(url, clientId, token); return unwrap(response); }; export const getArticle = async ( clientId: string, token: string, articleId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityArticle(articleId), clientId, token); return unwrap(response); }; /** * Per-prompt visibility impact for one article (Beta). Returns, for each * targeted prompt, the composite score before publish vs the latest week so * the merchant can gauge whether the content moved visibility. * * @param {string} clientId - Recomaze client id used for agent-backend auth. * @param {string} token - Recomaze auth token used for agent-backend auth. * @param {string} articleId - Target article id. * @returns {Promise} Per-prompt impact payload. */ export const getArticlePromptImpact = async ( clientId: string, token: string, articleId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityArticlePromptImpact(articleId), clientId, token); return unwrap(response); }; export const patchArticle = async ( clientId: string, token: string, articleId: string, body: ArticleEditRequest ): Promise => { const response = await BaseAgentService.patchWithClientId< VisibilityBaseResponse >(visibilityArticle(articleId), body, clientId, token); return unwrap(response); }; /** * Mark a ``ready`` article as ``published``. Stamps ``published_at`` on * the row so the per-week grouping in the UI can bucket it. * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @param articleId {string} Target article id. * @returns {Promise} Article detail with the new status. */ export const publishArticle = async ( clientId: string, token: string, articleId: string ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityArticlePublish(articleId), {}, clientId, token); return unwrap(response); }; /** * Publish a ready article to the store blog in the background, optionally * generating and embedding AI images first (25 credits each). * * @param {string} clientId - Merchant client id. * @param {string} token - Merchant JWT. * @param {string} articleId - Article identifier. * @param {boolean} generateImages - Also generate and embed AI images. * @returns {Promise<{ queued: boolean; with_images: boolean }>} Queued ack. */ export const publishArticleToBlog = async ( clientId: string, token: string, articleId: string, generateImages: boolean ): Promise<{ queued: boolean; with_images: boolean }> => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse<{ queued: boolean; with_images: boolean }> >( visibilityArticlePublishToBlog(articleId), { generate_images: generateImages }, clientId, token ); return unwrap(response); }; /** * Revert a ``published`` article back to ``ready`` (clears ``published_at``). * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @param articleId {string} Target article id. * @returns {Promise} Article detail with the reset status. */ export const unpublishArticle = async ( clientId: string, token: string, articleId: string ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityArticleUnpublish(articleId), {}, clientId, token); return unwrap(response); }; /** * Hard-delete a generated article and all its version history. * * @param clientId {string} Recomaze client id used for agent-backend auth. * @param token {string} Recomaze auth token used for agent-backend auth. * @param articleId {string} Target article id. * @returns {Promise} Deleted article id + version count. */ export const deleteArticle = async ( clientId: string, token: string, articleId: string ): Promise => { const response = await BaseAgentService.deleteWithClientId< VisibilityBaseResponse >(visibilityArticle(articleId), clientId, token); return unwrap(response); }; /** * Dispatch regeneration of an existing article (creates a new version). * * The optional ``body.instructions`` is a free-form merchant directive * passed straight to the LLM as the highest-priority constraint - * e.g. "rewrite as a listicle", "shift to a casual tone", "keep current * article but don't use the word X". */ export const regenerateArticle = async ( clientId: string, token: string, articleId: string, body?: ArticleRegenerateRequest ): Promise => { try { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityArticleRegenerate(articleId), body ?? {}, clientId, token); return unwrap(response); } catch (err) { return throwAgentError(err, 'Failed to start regeneration.'); } }; export const getArticleVersions = async ( clientId: string, token: string, articleId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityArticleVersions(articleId), clientId, token); return unwrap(response); }; export const getArticleVersion = async ( clientId: string, token: string, articleId: string, versionId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityArticleVersion(articleId, versionId), clientId, token); return unwrap(response); }; /* ------------------------------------------------------------------ */ /* chat-prompt mining */ /* ------------------------------------------------------------------ */ export const getChatPromptVolumes = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityBrandChatVolumes(brandId), clientId, token); return unwrap(response); }; export const adoptFromChat = async ( clientId: string, token: string, brandId: string, body: AdoptFromChatRequest ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityBrandAdoptFromChat(brandId), body, clientId, token); return unwrap(response); }; /** * Reverse of {@link adoptFromChat}: permanently delete the tracked AI-search * prompt that was previously promoted from a chat theme and clear the * theme's tracking pointer. * * @param {string} clientId - Recomaze client id used for agent-backend auth. * @param {string} token - Recomaze auth token used for agent-backend auth. * @param {string} brandId - Target brand id. * @param {StopTrackingChatRequest} body - Theme ids whose tracked prompt to delete. * @returns {Promise} Per-theme untrack result. */ export const stopTrackingFromChat = async ( clientId: string, token: string, brandId: string, body: StopTrackingChatRequest ): Promise => { const response = await BaseAgentService.postWithClientId< VisibilityBaseResponse >(visibilityBrandStopTrackingChat(brandId), body, clientId, token); return unwrap(response); }; /** Per-funnel-stage coverage + demand-ranked content gaps for a brand (Beta). */ export const getFunnelCoverage = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityBrandFunnelCoverage(brandId), clientId, token); return unwrap(response); }; /** Cited-domain landscape + co-citation cluster for a brand (Beta). */ export const getCitedSources = async ( clientId: string, token: string, brandId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityBrandCitedSources(brandId), clientId, token); return unwrap(response); }; /** Per-signal AEO breakdown + composite AEO/SEO/Quality/compliance scores (Beta). */ export const getArticleAeoChecklist = async ( clientId: string, token: string, articleId: string ): Promise => { const response = await BaseAgentService.getWithClientId< VisibilityBaseResponse >(visibilityArticleAeoChecklist(articleId), clientId, token); return unwrap(response); };