Skip to content

Advisor Inspector

The Advisor Inspector tool allows your app to contribute a check to the Crowdin Advisor and report insights about translation quality, glossary consistency, brand tone, or any other custom signal.

index.js
const crowdinModule = require('@crowdin/app-project-module');
const { reportAdvisorInsight } = crowdinModule;
// In-memory dedup: BE may fire multiple nudges within a short window
// (install + project-create + user-recheck). Keep only one in flight.
const inFlight = new Set();
const configuration = {
baseUrl: 'https://123.ngrok.io',
clientId: 'clientId',
clientSecret: 'clientSecret',
name: 'Sample App',
identifier: 'sample-app',
description: 'Sample App description',
dbFolder: __dirname,
imagePath: __dirname + '/' + 'logo.png',
advisorInspector: [
{
key: 'daily-llm-check',
title: 'Daily LLM Check',
summary: 'Looks at recent translations and flags anything suspicious.',
severity: 'medium',
category: 'context',
refreshPolicy: 'daily',
uiPath: __dirname + '/public',
onRecheck: async ({ projectId, moduleKey, inspectorId, crowdinId }) => {
const dedupKey = `${projectId}:${moduleKey}`;
if (inFlight.has(dedupKey)) return;
inFlight.add(dedupKey);
try {
// ...simulate your async/queued check (could take minutes or hours)...
await new Promise((resolve) => setTimeout(resolve, 30_000));
await reportAdvisorInsight(configuration, {
crowdinId,
projectId,
moduleKey,
outcome: 'flagged',
metrics: [
{ key: 'suspicious-strings', value: 7, unit: 'count', tone: 'danger' }
],
recommendations: [
{ id: 'review-recent-translations', primary: true }
],
payload: { reviewedAt: new Date().toISOString() },
});
} finally {
inFlight.delete(dedupKey);
}
}
}
]
};
crowdinModule.createApp(configuration);
ParameterDescriptionAllowed valuesDefault value
keyRaw module key. Becomes the URL segment for the recheck/insight endpoints and the value of jwt.module.Stringderived from identifier
titleHeader shown in the Advisor card.String
summaryOptional summary line under the title. ≤70 chars.String
severityVisual weight of the insight.'high' | 'medium' | 'low'
categoryInspector category.'context'
refreshPolicyHow often the BE may send recheck nudges.'hourly' | 'daily'
onRecheckAsync callback invoked when Crowdin pushes a recheck nudge. The SDK answers 200 to Crowdin immediately; your callback runs in the background.Function
uiPathPath to the iframe content folder (e.g. __dirname + '/public').String
fileNameEntry filename inside uiPath.String'index.html'
formSchemaJSON Schema for an auto-generated form (alternative to uiPath).Object
environmentsRestrict the module to crowdin and/or crowdin-enterprise.'crowdin' | 'crowdin-enterprise' or arrayboth

Receives a single object with these properties:

  • projectId – Numeric Crowdin project ID.
  • moduleKey – Raw module key (e.g. daily-llm-check). Pass this to reportAdvisorInsight.
  • inspectorId – Fully-prefixed app:<identifier>:<moduleKey>. Only useful if you need to correlate with Crowdin-side logs.
  • crowdinId – Tenant identifier. Persist this if you’ll call reportAdvisorInsight later — the value is the only piece you need to bootstrap a token outside of the callback.
  • client – Crowdin API client. May go stale after ~1h; for short-lived work only.
  • contextContext object.

Crowdin may fire multiple nudges in a short window (install + project-create + user-triggered recheck). The SDK does not dedup for you — key your own work on projectId + moduleKey.

Reports the result of an inspector run back to Crowdin. Importable from @crowdin/app-project-module:

const { reportAdvisorInsight } = require('@crowdin/app-project-module');
await reportAdvisorInsight(config, {
crowdinId, // from onRecheck args
projectId, // from onRecheck args
moduleKey, // from onRecheck args (raw key, NOT inspectorId)
outcome: 'flagged', // 'flagged' | 'clear' | 'not_applicable'
metrics: [
{ key: 'demo', value: 1, unit: 'count' }
],
recommendations: [
{ id: 'demo-rec', primary: true, params: { foo: 'bar' } }
],
payload: { foo: 'bar' },
// timeoutMs: 60_000, // optional; default 5 minutes
});
  • outcome'flagged' | 'clear' | 'not_applicable'. Required.
  • metrics — optional, ≤100, unique by key. Each metric:
    • key (string, required) — stable identifier within the metrics array.
    • value (number, required).
    • unit'percent' | 'count'.
    • threshold (number) — comparison baseline shown in the UI.
    • tone'default' | 'success' | 'danger'.
    • checkedAt (RFC3339 string) — per-metric timestamp.
  • recommendations — optional, ≤20, unique by id. Each:
    • id (string, required) — opaque to Crowdin; resolved by your iframe.
    • primary (boolean).
    • params (object) — string-keyed.
  • payload — optional. Free-form object or array (PHP-array-shaped — indexed or associative). Crowdin rejects raw scalars.
  • checkedAt (RFC3339 string) — optional top-level timestamp. When omitted, Crowdin sets checked_at to now at upsert time. Supplied values are clamped if more than 1 hour in the future or 7 days in the past.
  • timeoutMs (number) — optional request timeout for the insight PUT. Default 300000 (5 minutes).

The upsert is full-replace: omitting metrics or recommendations clears those fields on the BE side. The helper sends [] when you omit them, so passing nothing intentionally clears. If you want to preserve previous values, re-send them.