{"openapi":"3.1.0","info":{"title":"arText Reports API","description":"Envía un texto, consulta el informe asíncrono y utiliza sugerencias lingüísticas estructuradas en tus propios productos.","version":"1.0.0"},"paths":{"/v1/reports":{"post":{"tags":["Reports"],"summary":"Crear un informe de análisis","description":"Valida y encola un texto. La respuesta 202 incluye recursos de estado y eventos; no implica que el análisis haya terminado.","operationId":"create_public_report_v1_reports_post","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":255},{"type":"null"}],"title":"Idempotency-Key"},"description":"Recommended unique key for safely retrying this creation request. It is scoped to the authenticated caller, retained for 24 hours, and must not be reused with a different payload.","example":"document-123-analysis-v1"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportRequest"},"examples":{"basic":{"summary":"Create a report","value":{"language":"es","domain_slug":"lenguaje-claro","text_type_slug":"texto-juridico-administrativo-dirigido-a-la-ciudadania","text":"En el día de hoy se realizará la revisión de la solicitud.","external_id":"document-123"}}}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportAccepted"},"examples":{"queued":{"summary":"Report accepted and queued","value":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"queued","status_url":"/v1/reports/rep_7f12a4c8","events_url":"/v1/reports/rep_7f12a4c8/events"}}}}},"headers":{"Location":{"description":"Canonical status resource for the accepted report.","schema":{"type":"string","example":"/v1/reports/rep_7f12a4c8"}},"Retry-After":{"description":"Minimum number of seconds before the first status poll.","schema":{"type":"integer","minimum":1,"example":2}},"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"409":{"description":"Idempotency-Key was reused with a different payload.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"413":{"description":"Text exceeds the configured word limit.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"422":{"description":"Request validation failed, including an unknown domain_slug/text_type_slug pair.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"503":{"description":"The analysis queue is unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}}},"security":[{"ApiKey":[]}]}},"/v1/reports/{report_id}":{"get":{"tags":["Reports"],"summary":"Consultar estado y resultado","description":"Devuelve el estado actual. En queued/running hay que respetar Retry-After; completed y failed son terminales.","operationId":"get_public_report_v1_reports__report_id__get","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"},"description":"Unique report identifier returned by POST /v1/reports."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Report"},"examples":{"queued":{"summary":"Report queued","value":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"queued","status_url":"/v1/reports/rep_7f12a4c8","events_url":"/v1/reports/rep_7f12a4c8/events"}},"completed":{"summary":"Report completed","value":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"completed","position_in_queue":null,"result":{"schema_version":"1.0","offset_unit":"unicode_code_points","language":"es","genre":{"domain":{"slug":"lenguaje-claro","name":"Lenguaje claro"},"text_type":{"slug":"texto-juridico-administrativo-dirigido-a-la-ciudadania","name":"Texto jurídico-administrativo dirigido a la ciudadanía"}},"measurements":[{"metric_id":"word-count","value":12,"unit":"words","implementation_version":"1.0"}],"suggestions":[{"id":"sug_1","metric_id":"redundant-expressions","implementation_version":"1.0","metric_title":"Eliminación de expresiones redundantes","category":"lexical","severity":"warning","summary":"Conviene formular el fragmento de manera más directa.","explanation":null,"occurrences":[{"id":"occ_1","text":"En el día de hoy","paragraph_index":0,"sentence_index":0,"start":0,"end":16,"replacement":null,"alternatives":[],"edits":[{"start":0,"end":16,"text":""}]}],"recommendations":["Elimina la expresión si el contexto lo permite."]}]},"error":null}},"failed":{"summary":"Report failed","value":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"failed","position_in_queue":null,"result":null,"error":{"code":"analysis_failed","message":"The analysis could not be completed."}}}}}},"headers":{"Retry-After":{"description":"Present for queued/running reports; wait at least this many seconds before polling again.","schema":{"type":"integer","minimum":1,"example":5}},"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"404":{"description":"Report not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"429":{"description":"Status rate limit or minimum polling interval exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"503":{"description":"Request protection is temporarily unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"422":{"description":"Validation Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/reports/{report_id}/events":{"get":{"tags":["Reports"],"summary":"Recibir transiciones por SSE","description":"Canal SSE opcional con el estado inicial, cambios y heartbeats. Se cierra al terminar; el polling sigue siendo el respaldo compatible.","operationId":"stream_public_report_events_v1_reports__report_id__events_get","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"},"description":"Unique report identifier returned by POST /v1/reports."}],"responses":{"200":{"description":"SSE status stream.","content":{"text/event-stream":{"schema":{"type":"string"},"examples":{"transitions":{"summary":"A stream closes after completed or failed","value":"event: status\\ndata: {\"report_id\":\"rep_7f12a4c8\",\"status\":\"running\",\"updated_at\":\"2026-07-15T10:00:02Z\"}\\n\\nevent: status\\ndata: {\"report_id\":\"rep_7f12a4c8\",\"status\":\"completed\",\"updated_at\":\"2026-07-15T10:00:06Z\"}\\n\\n"}}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Unauthorized","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Not Found","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"429":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Too Many Requests","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Service Unavailable","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"422":{"description":"Validation Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/metrics":{"get":{"tags":["Metrics"],"summary":"List metric implementations","description":"Returns measurement and suggestion implementations, including inactive variants and localized documentation. Narrowing the request to a genre reports which metrics that genre actually evaluates.","operationId":"list_metrics_v1_metrics_get","parameters":[{"name":"analysis_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Analysis Language"},"description":"Language implementation to return: es, ca or en."},{"name":"documentation_language","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Documentation Language"},"description":"Language used for titles and documentation: es, ca or en."},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(measurement|suggestion)$"},{"type":"null"}],"title":"Kind"},"description":"Filter by measurement or suggestion."},{"name":"enabled","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"description":"Filter by the current implementation status."},{"name":"domain_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional genre filter. domain_slug and text_type_slug go together and must name a genre listed by GET /v1/text-types; each metric then reports whether that genre evaluates it and with which thresholds.","title":"Domain Slug"},"description":"Text domain of a genre, as listed by GET /v1/text-types."},{"name":"text_type_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional genre filter. domain_slug and text_type_slug go together and must name a genre listed by GET /v1/text-types; each metric then reports whether that genre evaluates it and with which thresholds.","title":"Text Type Slug"},"description":"Text type (genre) inside domain_slug, as listed by GET /v1/text-types."}],"responses":{"200":{"description":"Metric implementations and their documentation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetricCatalog"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"422":{"description":"Incomplete or unknown genre filter.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/metrics/{metric_id}":{"get":{"tags":["Metrics"],"summary":"Get one metric implementation","description":"Returns the status, category and localized documentation for one language variant, optionally narrowed to a genre.","operationId":"get_metric_v1_metrics__metric_id__get","parameters":[{"name":"metric_id","in":"path","required":true,"schema":{"type":"string","title":"Metric Id"},"description":"Stable conceptual metric identifier."},{"name":"analysis_language","in":"query","required":true,"schema":{"type":"string","title":"Analysis Language"},"description":"Language implementation to return: es, ca or en."},{"name":"documentation_language","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Documentation Language"},"description":"Language used for titles and documentation: es, ca or en."},{"name":"domain_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional genre filter. domain_slug and text_type_slug go together and must name a genre listed by GET /v1/text-types; each metric then reports whether that genre evaluates it and with which thresholds.","title":"Domain Slug"},"description":"Text domain of a genre, as listed by GET /v1/text-types."},{"name":"text_type_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional genre filter. domain_slug and text_type_slug go together and must name a genre listed by GET /v1/text-types; each metric then reports whether that genre evaluates it and with which thresholds.","title":"Text Type Slug"},"description":"Text type (genre) inside domain_slug, as listed by GET /v1/text-types."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Metric"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"404":{"description":"Metric implementation not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"422":{"description":"Incomplete or unknown genre filter.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/text-types":{"get":{"tags":["Text types"],"summary":"List text domains and genres","description":"Returns the domains (ámbitos) and the genres (text types) available for a language. The slug pair of a genre is what POST /v1/reports expects.","operationId":"list_public_text_types_v1_text_types_get","parameters":[{"name":"language","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Language"},"description":"Catalog language: es, ca or en."}],"responses":{"200":{"description":"Editorial catalog for the requested language.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTextTypeCatalog"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"422":{"description":"Unsupported language.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/text-types/{domain_slug}/{text_type_slug}":{"get":{"tags":["Text types"],"summary":"Get one genre","description":"Returns the sections and contents recommended for a genre, plus the exact report_defaults to forward to POST /v1/reports.","operationId":"get_public_text_type_v1_text_types__domain_slug___text_type_slug__get","parameters":[{"name":"domain_slug","in":"path","required":true,"schema":{"type":"string","title":"Domain Slug"},"description":"Text domain of a genre, as listed by GET /v1/text-types."},{"name":"text_type_slug","in":"path","required":true,"schema":{"type":"string","title":"Text Type Slug"},"description":"Text type (genre) inside domain_slug, as listed by GET /v1/text-types."},{"name":"language","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Language"},"description":"Catalog language: es, ca or en."}],"responses":{"200":{"description":"Genre detail and its report defaults.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTextTypeDetail"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"404":{"description":"Genre not found for this language.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"422":{"description":"Unsupported language.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}}},"components":{"schemas":{"ApiProblem":{"properties":{"type":{"type":"string","title":"Type","description":"URI reference identifying the problem type."},"title":{"type":"string","title":"Title","description":"Short, stable problem title."},"status":{"type":"integer","title":"Status","description":"HTTP status code."},"detail":{"type":"string","title":"Detail","description":"Human-readable explanation for this occurrence."},"instance":{"type":"string","title":"Instance","description":"Request path for this occurrence."},"code":{"type":"string","title":"Code","description":"Stable machine-readable arText error code."},"request_id":{"type":"string","title":"Request Id","description":"Identifier to retain for support and tracing."},"retry_after_seconds":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Retry After Seconds"},"errors":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Errors"}},"type":"object","required":["type","title","status","detail","instance","code","request_id"],"title":"ApiProblem"},"Genre":{"properties":{"domain":{"$ref":"#/components/schemas/TextDomain"},"text_type":{"$ref":"#/components/schemas/TextType"}},"type":"object","required":["domain","text_type"],"title":"Genre","description":"Genre the report was analyzed with.\n\nIt names both coordinates because a text type slug is only unique inside\nits domain, and the pair is what determined the rules, the thresholds and\nthe glossaries used. Resubmitting these two slugs reproduces the analysis."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Measurement":{"properties":{"metric_id":{"type":"string","title":"Metric Id","description":"Stable metric identifier documented by GET /v1/metrics/{metric_id}."},"value":{"anyOf":[{"type":"integer"},{"type":"number"}],"title":"Value","description":"Calculated numeric value."},"unit":{"type":"string","title":"Unit","description":"Semantic unit defined by the metric catalog."},"implementation_version":{"type":"string","title":"Implementation Version","description":"Calculator version used for this report."}},"type":"object","required":["metric_id","value","unit","implementation_version"],"title":"Measurement"},"Metric":{"properties":{"id":{"type":"string","title":"Id"},"kind":{"type":"string","enum":["measurement","suggestion"],"title":"Kind"},"analysis_language":{"type":"string","title":"Analysis Language"},"enabled":{"type":"boolean","title":"Enabled","description":"Whether reports for analysis_language currently evaluate this variant."},"genre_scope":{"anyOf":[{"$ref":"#/components/schemas/MetricGenreScope"},{"type":"null"}],"description":"Genre-specific status, present only when the request selects a genre."},"available":{"type":"boolean","title":"Available","description":"False when the variant can never produce output for analysis_language with the current linguistic runtime; see availability_note.","default":true},"availability_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Availability Note","description":"Localized explanation present only when available is false."},"implementation_version":{"type":"string","title":"Implementation Version"},"category":{"type":"string","title":"Category"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit"},"documentation_language":{"type":"string","title":"Documentation Language","description":"Language actually used by the returned documentation."},"documentation":{"$ref":"#/components/schemas/MetricDocumentation"},"references":{"items":{"type":"string"},"type":"array","title":"References"},"example":{"additionalProperties":true,"type":"object","title":"Example","description":"Example conforming to the public report contract."}},"type":"object","required":["id","kind","analysis_language","enabled","implementation_version","category","documentation_language","documentation"],"title":"Metric"},"MetricCatalog":{"properties":{"genre":{"anyOf":[{"$ref":"#/components/schemas/Genre"},{"type":"null"}],"description":"Genre the genre_scope of every metric refers to, when one was requested."},"metrics":{"items":{"$ref":"#/components/schemas/Metric"},"type":"array","title":"Metrics"}},"type":"object","required":["metrics"],"title":"MetricCatalog"},"MetricDocumentation":{"properties":{"title":{"type":"string","title":"Title","default":""},"summary":{"type":"string","title":"Summary","default":""},"methodology":{"type":"string","title":"Methodology","default":""},"limitations":{"type":"string","title":"Limitations","default":""},"interpretation":{"type":"string","title":"Interpretation","default":""}},"type":"object","title":"MetricDocumentation"},"MetricGenreScope":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Whether reports for this genre evaluate the metric."},"params":{"additionalProperties":true,"type":"object","title":"Params","description":"Thresholds this genre configures for the metric; empty means the defaults."}},"type":"object","required":["enabled"],"title":"MetricGenreScope","description":"How a metric behaves for one genre.\n\nPresent only when the request narrows the catalog with domain_slug and\ntext_type_slug. `enabled` is the intersection of the language variant and\nthe genre configuration: a variant enabled for the language still does not\nrun unless the genre opts into it."},"Occurrence":{"properties":{"id":{"type":"string","title":"Id","description":"Identifier unique within its parent suggestion."},"text":{"type":"string","title":"Text","description":"Fragment identified by the metric."},"paragraph_index":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Paragraph Index","description":"Zero-based paragraph index when available."},"sentence_index":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Sentence Index","description":"Zero-based sentence index when available."},"start":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Start","description":"Inclusive offset in the original input text."},"end":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"End","description":"Exclusive offset in the original input text."},"replacement":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Replacement","description":"Preferred direct replacement, or null when none is available."},"alternatives":{"items":{"type":"string"},"type":"array","title":"Alternatives","description":"Additional reviewed replacement candidates, excluding replacement."},"edits":{"items":{"$ref":"#/components/schemas/TextEdit"},"type":"array","title":"Edits","description":"Validated edits for the preferred deterministic change, ordered from the highest offset to the lowest."}},"type":"object","required":["id","text"],"title":"Occurrence"},"PublicPhraseology":{"properties":{"value":{"type":"string","title":"Value","description":"Suggested wording for the content it belongs to."}},"type":"object","required":["value"],"title":"PublicPhraseology"},"PublicTextContent":{"properties":{"name":{"type":"string","title":"Name","description":"Content the section is expected to cover."},"occurrence":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Occurrence","description":"How many times the content usually appears in the section, when the catalog states it."},"phraseology":{"items":{"$ref":"#/components/schemas/PublicPhraseology"},"type":"array","title":"Phraseology","description":"Wordings the editorial team proposes for this content."}},"type":"object","required":["name"],"title":"PublicTextContent"},"PublicTextDomain":{"properties":{"slug":{"type":"string","title":"Slug","description":"Domain identifier. Send it as domain_slug in POST /v1/reports."},"name":{"type":"string","title":"Name","description":"Domain name in the requested language."},"clear_language":{"type":"boolean","title":"Clear Language","description":"True when the whole domain is plain-language.","default":false},"text_types":{"items":{"$ref":"#/components/schemas/PublicTextType"},"type":"array","title":"Text Types","description":"Genres available inside this domain."}},"type":"object","required":["slug","name"],"title":"PublicTextDomain"},"PublicTextSection":{"properties":{"name":{"type":"string","title":"Name","description":"Section of the document, in reading order."},"contents":{"items":{"$ref":"#/components/schemas/PublicTextContent"},"type":"array","title":"Contents"}},"type":"object","required":["name"],"title":"PublicTextSection"},"PublicTextType":{"properties":{"slug":{"type":"string","title":"Slug","description":"Genre identifier. Send it as text_type_slug in POST /v1/reports."},"name":{"type":"string","title":"Name","description":"Genre name in the requested language."},"clear_language":{"type":"boolean","title":"Clear Language","description":"True when the genre is written in plain language for the general public.","default":false}},"type":"object","required":["slug","name"],"title":"PublicTextType"},"PublicTextTypeCatalog":{"properties":{"language":{"type":"string","title":"Language","description":"Language the catalog is returned in."},"domains":{"items":{"$ref":"#/components/schemas/PublicTextDomain"},"type":"array","title":"Domains"}},"type":"object","required":["language","domains"],"title":"PublicTextTypeCatalog"},"PublicTextTypeDetail":{"properties":{"language":{"type":"string","title":"Language"},"domain":{"$ref":"#/components/schemas/PublicTextDomain","description":"Domain the genre belongs to, without its sibling genres."},"text_type":{"$ref":"#/components/schemas/PublicTextType"},"structure":{"items":{"$ref":"#/components/schemas/PublicTextSection"},"type":"array","title":"Structure","description":"Sections and contents the editorial team recommends for this genre."},"report_defaults":{"$ref":"#/components/schemas/ReportDefaults","description":"Exact payload fragment to forward to POST /v1/reports."}},"type":"object","required":["language","domain","text_type","report_defaults"],"title":"PublicTextTypeDetail"},"Report":{"properties":{"report_id":{"type":"string","title":"Report Id"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id"},"status":{"$ref":"#/components/schemas/ReportStatus"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"UTC acceptance timestamp."},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"UTC last-transition timestamp."},"started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Started At","description":"UTC processing start timestamp."},"finished_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Finished At","description":"UTC terminal timestamp."},"position_in_queue":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position In Queue","description":"Approximate queue position while waiting."},"phase":{"anyOf":[{"$ref":"#/components/schemas/ReportPhase"},{"type":"null"}],"description":"Pipeline stage while the report is running: `analyzing` (linguistic analysis, by far the longest part) or `building` (applying rules). Not a percentage: it marks what the server is doing, not how far along it is."},"estimated_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Seconds","description":"Rough estimate of the analysis duration, derived from the text length when the report was accepted. Indicative only: it excludes queue wait and does not react to server load."},"result":{"anyOf":[{"$ref":"#/components/schemas/ReportResult"},{"type":"null"}],"description":"Analysis result, present only when completed."},"error":{"anyOf":[{"$ref":"#/components/schemas/ReportFailure"},{"type":"null"}],"description":"Safe failure information, present only when failed."}},"type":"object","required":["report_id","status"],"title":"Report"},"ReportAccepted":{"properties":{"report_id":{"type":"string","title":"Report Id","description":"Unique arText report identifier."},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Caller-provided correlation identifier."},"status":{"$ref":"#/components/schemas/ReportStatus","description":"Initial queue status; always queued for a newly accepted report."},"status_url":{"type":"string","title":"Status Url","description":"Relative URL of the report status resource."},"events_url":{"type":"string","title":"Events Url","description":"Relative URL of the optional SSE status stream."},"estimated_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Seconds","description":"Rough estimate of how long the analysis will take, derived from the length of the submitted text. Indicative only: it excludes queue wait and does not react to server load."}},"type":"object","required":["report_id","status","status_url","events_url"],"title":"ReportAccepted"},"ReportDefaults":{"properties":{"language":{"type":"string","title":"Language","description":"Analysis language the slugs below belong to."},"domain_slug":{"type":"string","title":"Domain Slug","description":"Value for the domain_slug field of a report request."},"text_type_slug":{"type":"string","title":"Text Type Slug","description":"Value for the text_type_slug field of a report request."}},"type":"object","required":["language","domain_slug","text_type_slug"],"title":"ReportDefaults","description":"Exact fields to forward to POST /v1/reports for this genre."},"ReportFailure":{"properties":{"code":{"type":"string","const":"analysis_failed","title":"Code","default":"analysis_failed"},"message":{"type":"string","title":"Message","description":"Safe failure description suitable for API clients."}},"type":"object","required":["message"],"title":"ReportFailure"},"ReportPhase":{"type":"string","enum":["analyzing","building"],"title":"ReportPhase","description":"Punto de la tubería mientras el estado es `running`.\n\nNo es un porcentaje y no debe presentarse como tal: `analyzing` se lleva el\n94 % del tiempo (una única llamada a FreeLing, que no informa de su avance),\nasí que una barra basada en esto se quedaría clavada casi todo el rato. Sirve\npara decir qué está pasando y que sigue vivo; para «cuánto queda» está\n`estimated_seconds`."},"ReportRequest":{"properties":{"language":{"type":"string","title":"Language","description":"Language used for analysis: es, ca or en.","default":"es"},"domain_slug":{"type":"string","minLength":1,"title":"Domain Slug","description":"Text domain (ámbito) the genre belongs to, as listed by GET /v1/text-types.","examples":["lenguaje-claro"]},"text_type_slug":{"type":"string","minLength":1,"title":"Text Type Slug","description":"Text type (genre) inside domain_slug, as listed by GET /v1/text-types. The pair selects the rules, thresholds and glossaries applied to the text.","examples":["texto-juridico-administrativo-dirigido-a-la-ciudadania"]},"text":{"type":"string","minLength":1,"title":"Text","description":"Plain text to analyze."},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Optional caller-owned correlation identifier returned unchanged."}},"additionalProperties":false,"type":"object","required":["domain_slug","text_type_slug","text"],"title":"ReportRequest","description":"Stable input accepted from API integrations."},"ReportResult":{"properties":{"schema_version":{"type":"string","const":"1.0","title":"Schema Version","description":"Version of this public result contract.","default":"1.0"},"offset_unit":{"type":"string","const":"unicode_code_points","title":"Offset Unit","description":"Unit used by every start and end offset in the result.","default":"unicode_code_points"},"language":{"type":"string","title":"Language","description":"Language actually used for analysis."},"genre":{"$ref":"#/components/schemas/Genre"},"measurements":{"items":{"$ref":"#/components/schemas/Measurement"},"type":"array","title":"Measurements"},"suggestions":{"items":{"$ref":"#/components/schemas/Suggestion"},"type":"array","title":"Suggestions"}},"type":"object","required":["language","genre"],"title":"ReportResult"},"ReportStatus":{"type":"string","enum":["queued","running","completed","failed"],"title":"ReportStatus"},"Suggestion":{"properties":{"id":{"type":"string","title":"Id","description":"Identifier unique within this report."},"metric_id":{"type":"string","title":"Metric Id","description":"Stable metric identifier documented by GET /v1/metrics/{metric_id}."},"implementation_version":{"type":"string","title":"Implementation Version","description":"Detector version used for this report."},"metric_title":{"type":"string","title":"Metric Title","description":"Localized human-readable metric title captured for this report."},"category":{"type":"string","title":"Category","description":"Stable linguistic category used by the metric catalog."},"severity":{"type":"string","title":"Severity","description":"Review priority; it is not a confidence score."},"summary":{"type":"string","title":"Summary","description":"Explanation of the issue found in this report."},"explanation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Explanation","description":"Optional extended contextual explanation."},"recommendations":{"items":{"type":"string"},"type":"array","title":"Recommendations","description":"Human-readable review guidance."},"examples":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Examples","description":"Optional worked examples illustrating the recommendation, as plain text with meaningful line breaks. Editorial copy attached to the metric: it never quotes the analysed text and does not vary between reports. Null when the metric's copy has no examples."},"occurrences":{"items":{"$ref":"#/components/schemas/Occurrence"},"type":"array","title":"Occurrences","description":"Concrete appearances of this issue in the submitted text."}},"type":"object","required":["id","metric_id","implementation_version","metric_title","category","severity","summary"],"title":"Suggestion"},"TextDomain":{"properties":{"slug":{"type":"string","title":"Slug","description":"Stable text domain identifier accepted by POST /v1/reports."},"name":{"type":"string","title":"Name","description":"Localized text domain name."}},"type":"object","required":["slug","name"],"title":"TextDomain"},"TextEdit":{"properties":{"start":{"type":"integer","minimum":0.0,"title":"Start","description":"Inclusive offset in the original input text."},"end":{"type":"integer","minimum":0.0,"title":"End","description":"Exclusive offset in the original input text."},"text":{"type":"string","title":"Text","description":"Text to splice into the range. Empty text deletes it; start == end inserts text."}},"type":"object","required":["start","end","text"],"title":"TextEdit"},"TextType":{"properties":{"slug":{"type":"string","title":"Slug","description":"Stable text type identifier accepted by POST /v1/reports."},"name":{"type":"string","title":"Name","description":"Localized text type name."}},"type":"object","required":["slug","name"],"title":"TextType"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","bearerFormat":"art_live_…"}}},"servers":[{"url":"http://uned.tail9cc3dd.ts.net"}],"x-artext-metrics":[{"id":"average-sentence-words","kind":"measurement","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"readability","unit":"words","documentation_language":"es","documentation":{"title":"Average sentence words","summary":"Promedio de palabras por oración.","methodology":"Divide word-count entre sentence-count y redondea el resultado a dos decimales; devuelve 0 si no hay oraciones.","limitations":"El valor depende de la normalización y segmentación del analizador; debe interpretarse junto con las demás métricas y el género textual.","interpretation":"`value` contiene el resultado numérico y `unit` es `words`. La ausencia indica que la variante estaba desactivada."},"references":[],"example":{"metric_id":"average-sentence-words","value":24,"unit":"words","implementation_version":"1.0"}},{"id":"maximum-paragraph-words","kind":"measurement","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"readability","unit":"words","documentation_language":"es","documentation":{"title":"Maximum paragraph words","summary":"Longitud, en palabras, del párrafo más extenso.","methodology":"Obtiene el máximo de word_count entre los párrafos detectados; devuelve 0 si no hay ninguno.","limitations":"El valor depende de la normalización y segmentación del analizador; debe interpretarse junto con las demás métricas y el género textual.","interpretation":"`value` contiene el resultado numérico y `unit` es `words`. La ausencia indica que la variante estaba desactivada."},"references":[],"example":{"metric_id":"maximum-paragraph-words","value":24,"unit":"words","implementation_version":"1.0"}},{"id":"maximum-sentence-words","kind":"measurement","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"readability","unit":"words","documentation_language":"es","documentation":{"title":"Maximum sentence words","summary":"Longitud, en palabras, de la oración más extensa.","methodology":"Obtiene el máximo de word_count entre las oraciones segmentadas; devuelve 0 si no hay ninguna.","limitations":"El valor depende de la normalización y segmentación del analizador; debe interpretarse junto con las demás métricas y el género textual.","interpretation":"`value` contiene el resultado numérico y `unit` es `words`. La ausencia indica que la variante estaba desactivada."},"references":[],"example":{"metric_id":"maximum-sentence-words","value":24,"unit":"words","implementation_version":"1.0"}},{"id":"paragraph-count","kind":"measurement","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"es","documentation":{"title":"Paragraph count","summary":"Número de párrafos no vacíos detectados.","methodology":"Segmenta el texto de párrafos y cuenta las unidades con contenido analizable.","limitations":"El valor depende de la normalización y segmentación del analizador; debe interpretarse junto con las demás métricas y el género textual.","interpretation":"`value` contiene el resultado numérico y `unit` es `count`. La ausencia indica que la variante estaba desactivada."},"references":[],"example":{"metric_id":"paragraph-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"segment-count","kind":"measurement","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"es","documentation":{"title":"Segment count","summary":"Número de segmentos internos de oración reconocidos.","methodology":"Suma los segmentos sintáctico-discursivos producidos por DiSeg para todas las oraciones.","limitations":"El valor depende de la normalización y segmentación del analizador; debe interpretarse junto con las demás métricas y el género textual.","interpretation":"`value` contiene el resultado numérico y `unit` es `count`. La ausencia indica que la variante estaba desactivada."},"references":[],"example":{"metric_id":"segment-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"sentence-count","kind":"measurement","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"es","documentation":{"title":"Sentence count","summary":"Número de oraciones segmentadas por el analizador.","methodology":"Cuenta las oraciones reconocidas por FreeLing/DiSeg, no solo los puntos del texto.","limitations":"El valor depende de la normalización y segmentación del analizador; debe interpretarse junto con las demás métricas y el género textual.","interpretation":"`value` contiene el resultado numérico y `unit` es `count`. La ausencia indica que la variante estaba desactivada."},"references":[],"example":{"metric_id":"sentence-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"token-count","kind":"measurement","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"es","documentation":{"title":"Token count","summary":"Número de unidades léxicas producidas por el analizador.","methodology":"Cuenta los tokens de FreeLing/DiSeg; puntuación y contracciones pueden hacer que difiera de word-count.","limitations":"El valor depende de la normalización y segmentación del analizador; debe interpretarse junto con las demás métricas y el género textual.","interpretation":"`value` contiene el resultado numérico y `unit` es `count`. La ausencia indica que la variante estaba desactivada."},"references":[],"example":{"metric_id":"token-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"word-count","kind":"measurement","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"es","documentation":{"title":"Word count","summary":"Número de palabras del texto normalizado.","methodology":"Normaliza los espacios del texto y cuenta cada elemento no vacío separado por espacios.","limitations":"El valor depende de la normalización y segmentación del analizador; debe interpretarse junto con las demás métricas y el género textual.","interpretation":"`value` contiene el resultado numérico y `unit` es `count`. La ausencia indica que la variante estaba desactivada."},"references":[],"example":{"metric_id":"word-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"acronym-consistency","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"acronyms","unit":null,"documentation_language":"es","documentation":{"title":"Sistematicidad en el uso de siglas","summary":"Detecta repeticiones del término desarrollado después de que ya se haya introducido su sigla.","methodology":"Después de identificar sigla y desarrollo en su primera aparición, busca si el desarrollo completo vuelve a aparecer más adelante.","limitations":"Es una señal de revisión, no una corrección automática ni una evaluación completa de la calidad del texto.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"acronym-consistency","implementation_version":"1.2","metric_title":"Sistematicidad en el uso de siglas","category":"acronyms","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"acronym-introduction","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"acronyms","unit":null,"documentation_language":"es","documentation":{"title":"Introducción de siglas","summary":"Detecta la primera aparición de siglas en mayúsculas que no parecen acompañadas por su desarrollo.","methodology":"Extrae candidatos en mayúsculas, ignora palabras vacías, conserva la primera aparición y busca un desarrollo adyacente antes o después.","limitations":"Es una señal de revisión, no una corrección automática ni una evaluación completa de la calidad del texto.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"acronym-introduction","implementation_version":"1.2","metric_title":"Introducción de siglas","category":"acronyms","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"archaic-verb-forms","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Eliminación de formas verbales arcaicas","summary":"Detecta formas etiquetadas como futuro de subjuntivo, consideradas arcaicas en el uso general actual.","methodology":"Busca etiquetas FreeLing que empiezan por VSSF, VASF o VMSF, también en vaux-cat, excluyendo auxiliares.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"archaic-verb-forms","implementation_version":"1.2","metric_title":"Eliminación de formas verbales arcaicas","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"connector-variety","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Variación de conectores","summary":"Detecta conectores discursivos repetidos por encima del umbral y ofrece alternativas de la misma relación.","methodology":"Cuenta nodos disc_mk del XML, normaliza sus formas y señala conectores del modelo cuya frecuencia supera n_variacion_conectores. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"connector-variety","implementation_version":"1.2","metric_title":"Variación de conectores","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"definition-usage","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Uso de definiciones","summary":"Compara el número de indicadores de definición del texto con el valor de referencia del género textual.","methodology":"Cuenta los marcadores de definición del texto y los compara con el valor de referencia del género; el parámetro de modo decide si el aviso se emite por exceso o por defecto. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"definition-usage","implementation_version":"1.2","metric_title":"Uso de definiciones","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"difficult-expressions","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Sustitución de expresiones difíciles de entender","summary":"Localiza expresiones difíciles de entender incluidas en el catálogo y devuelve alternativas cuando existen.","methodology":"Busca expresiones completas directamente sobre el texto, sin distinguir mayúsculas ni acentos, respetando límites de palabra. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"difficult-expressions","implementation_version":"1.2","metric_title":"Sustitución de expresiones difíciles de entender","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"gerunds","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de gerundios","summary":"Localiza gerundios independientes que conviene revisar, excluyendo auxiliares y construcciones compuestas reconocidas.","methodology":"Usa etiquetas verbales de gerundio (VBG en EN) y atributos auxiliares para excluir perífrasis ya reconocidas.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"gerunds","implementation_version":"1.2","metric_title":"Revisión de gerundios","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"imprecise-words","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Sustitución de palabras poco precisas","summary":"Localiza palabras o secuencias poco precisas incluidas en el catálogo editable y ofrece sus alternativas.","methodology":"Normaliza los lemas de tokens y busca secuencias completas del catálogo activo; devuelve término, alternativa y posiciones de texto cuando están disponibles. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"imprecise-words","implementation_version":"1.2","metric_title":"Sustitución de palabras poco precisas","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"list-usage","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Inclusión de listas","summary":"Avisa cuando no se ha reconocido ninguna lista estructurada en el documento.","methodology":"Devuelve una sugerencia documental cuando has_list es falso; has_list se obtiene del texto y del HTML estructurado.","limitations":"Depende de la segmentación del texto plano y de que los marcadores estructurales sean reconocibles; no implica por sí sola un error de redacción.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"list-usage","implementation_version":"1.2","metric_title":"Inclusión de listas","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-paragraphs","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de párrafos largos","summary":"Señala párrafos que superan el límite de palabras configurado para el género textual.","methodology":"Compara word_count de cada párrafo con n_palabras_por_parrafo del género; solo informa valores estrictamente superiores.","limitations":"Depende de la segmentación del texto plano y de que los marcadores estructurales sean reconocibles; no implica por sí sola un error de redacción.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"long-paragraphs","implementation_version":"1.2","metric_title":"Revisión de párrafos largos","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-segmented-sentences","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"División de oraciones largas","summary":"Señala oraciones largas con varios segmentos y propone puntos estructurales donde podrían dividirse.","methodology":"Selecciona oraciones con más de n_division_oraciones palabras y varios segmentos; devuelve segmentos, conectores y posibles límites de división.","limitations":"Depende de la segmentación del texto plano y de que los marcadores estructurales sean reconocibles; no implica por sí sola un error de redacción.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"long-segmented-sentences","implementation_version":"1.2","metric_title":"División de oraciones largas","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-unsegmented-sentences","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de oraciones largas","summary":"Señala oraciones largas que DiSeg no ha dividido en segmentos internos.","methodology":"Selecciona oraciones con más de n_division_oraciones palabras y como máximo un segmento DiSeg.","limitations":"Depende de la segmentación del texto plano y de que los marcadores estructurales sean reconocibles; no implica por sí sola un error de redacción.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"long-unsegmented-sentences","implementation_version":"1.2","metric_title":"Revisión de oraciones largas","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-words","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de palabras largas","summary":"Localiza palabras largas incluidas en el catálogo editorial y propone alternativas más breves cuando existen.","methodology":"Normaliza los lemas de tokens y busca secuencias completas del catálogo activo; devuelve término, alternativa y posiciones de texto cuando están disponibles. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"long-words","implementation_version":"1.2","metric_title":"Revisión de palabras largas","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"missing-definitions","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Carencia de definiciones","summary":"Avisa cuando el texto no contiene ningún indicador de definición y el género textual suele incluir definiciones.","methodology":"Busca marcadores de definición (un verbo definitorio junto a una palabra de contorno) y devuelve un aviso documental si el texto no contiene ninguno. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"missing-definitions","implementation_version":"1.2","metric_title":"Carencia de definiciones","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"negative-formulations","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Reformulación de ideas expresadas en negativo","summary":"Localiza palabras o secuencias negativas catalogadas para favorecer formulaciones afirmativas y directas.","methodology":"Normaliza los lemas de tokens y busca secuencias completas del catálogo activo; devuelve término, alternativa y posiciones de texto cuando están disponibles. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"negative-formulations","implementation_version":"1.2","metric_title":"Reformulación de ideas expresadas en negativo","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"paragraph-initial-connectors","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Introducción de conectores al inicio de párrafos","summary":"Detecta párrafos que no comienzan con un conector discursivo reconocido.","methodology":"Descarta párrafos vacíos o con has_initial_connector; para el resto devuelve el párrafo y los grupos de conectores editables. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"paragraph-initial-connectors","implementation_version":"1.2","metric_title":"Introducción de conectores al inicio de párrafos","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"participles","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de participios","summary":"Localiza participios independientes y evita los que forman tiempos compuestos o pasivas reconocidas.","methodology":"Usa etiquetas de participio (VBN en EN) y excluye auxiliares, tiempos compuestos, pasivas y lemas auxiliares.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"participles","implementation_version":"1.2","metric_title":"Revisión de participios","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"passive-voice","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Uso de la voz pasiva","summary":"Localiza construcciones que el análisis morfosintáctico identifica como posibles voces pasivas.","methodology":"En ES/CA usa nodos verb_pass o ger_pas y, como respaldo, secuencias ser/ésser + participio. En EN busca be + VBN en vb_chunk y después en tokens.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"passive-voice","implementation_version":"1.2","metric_title":"Uso de la voz pasiva","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"redundant-expressions","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Eliminación de expresiones redundantes","summary":"Localiza expresiones redundantes del catálogo para eliminarlas o reformularlas.","methodology":"Busca expresiones completas directamente sobre el texto, sin distinguir mayúsculas ni acentos, respetando límites de palabra. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"redundant-expressions","implementation_version":"1.2","metric_title":"Eliminación de expresiones redundantes","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"single-sentence-paragraphs","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de párrafos-oración","summary":"Localiza párrafos formados por una sola oración para revisar si concentran demasiada información o necesitan desarrollo.","methodology":"Selecciona cada párrafo no vacío cuyo sentence_count sea exactamente 1.","limitations":"Depende de la segmentación del texto plano y de que los marcadores estructurales sean reconocibles; no implica por sí sola un error de redacción.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"single-sentence-paragraphs","implementation_version":"1.2","metric_title":"Revisión de párrafos-oración","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"subjectivity-markers","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Uso de indicadores de subjetividad","summary":"Combina léxico, secuencias, categorías morfológicas y exclamaciones para localizar posibles marcas de subjetividad.","methodology":"Une coincidencias de lemas simples, secuencias compuestas, categorías morfológicas configuradas y fragmentos entre signos de exclamación; después elimina duplicados.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"subjectivity-markers","implementation_version":"1.2","metric_title":"Uso de indicadores de subjetividad","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"transparent-terms","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Utilización de términos más transparentes","summary":"Detecta términos difíciles catalogados y propone denominaciones más transparentes.","methodology":"Normaliza los lemas de tokens y busca secuencias completas del catálogo activo; devuelve término, alternativa y posiciones de texto cuando están disponibles. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"transparent-terms","implementation_version":"1.2","metric_title":"Utilización de términos más transparentes","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"verb-person-consistency","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Sistematicidad en el uso de verbos en primera persona","summary":"Avisa cuando conviven verbos en primera persona singular y plural en el mismo documento.","methodology":"Extrae persona y número de etiquetas verbales; solo genera resultado si encuentra al menos una forma 1S y una 1P.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"verb-person-consistency","implementation_version":"1.2","metric_title":"Sistematicidad en el uso de verbos en primera persona","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"verbal-nominalizations","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de nominalizaciones verbales","summary":"Detecta sustantivos con patrones productivos de nominalización verbal y excluye las excepciones catalogadas.","methodology":"Filtra sustantivos minúsculos: -ción/-ciones en ES, -ció/-cions en CA y -tion/-tions en EN; elimina entradas del catálogo de excepciones.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"verbal-nominalizations","implementation_version":"1.2","metric_title":"Revisión de nominalizaciones verbales","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"word-repetition","kind":"suggestion","analysis_language":"es","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Repetición de palabras","summary":"Señala las palabras con contenido léxico que se repiten en el texto por encima del umbral configurado para el género.","methodology":"Cuenta cuántas veces aparece cada lema, descartando los lemas auxiliares y copulativos, y señala los lemas de nombre, adjetivo, adverbio o verbo cuya frecuencia supera el umbral del género.","limitations":"Es una señal de revisión, no una corrección automática ni una evaluación completa de la calidad del texto.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"word-repetition","implementation_version":"1.2","metric_title":"Repetición de palabras","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}}],"x-artext-guides":{"language":"es","examples":{"curl":"# Guarda la clave fuera del código y cárgala desde el entorno.\n# Requiere curl y jq.\n# export ARTEXT_API_KEY='art_live_...'\nset -euo pipefail\nAPI_BASE='http://uned.tail9cc3dd.ts.net'\nIDEMPOTENCY_KEY=\"report-$(date +%s)-$RANDOM\"\nheaders_file=$(mktemp)\ntrap 'rm -f \"$headers_file\"' EXIT\n\n# Crea el informe una sola vez. Idempotency-Key hace seguro repetir el POST tras un fallo de red.\ncreated=$(curl --fail-with-body --silent --show-error --dump-header \"$headers_file\" \\\n  --request POST \"$API_BASE/v1/reports\" \\\n  --header \"Authorization: Bearer $ARTEXT_API_KEY\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header 'Content-Type: application/json' \\\n  --data-binary '{\"language\":\"es\",\"domain_slug\":\"lenguaje-claro\",\"text_type_slug\":\"texto-juridico-administrativo-dirigido-a-la-ciudadania\",\"text\":\"En el día de hoy se realizará la revisión de la solicitud.\",\"external_id\":\"document-123\"}')\n\n# status_url es relativo: resuélvelo contra API_BASE.\nstatus_url=\"$API_BASE$(jq -r '.status_url' <<<\"$created\")\"\nretry_after=$(awk 'BEGIN { IGNORECASE=1 } /^Retry-After:/ { gsub(\"\\r\", \"\", $2); print $2 }' \"$headers_file\" | tail -1)\nretry_after=${retry_after:-2}\n\n# Consulta el mismo informe respetando Retry-After y con un límite total.\nfor attempt in $(seq 1 90); do\n  sleep \"$retry_after\"\n  report=$(curl --fail-with-body --silent --show-error --dump-header \"$headers_file\" \\\n    --header \"Authorization: Bearer $ARTEXT_API_KEY\" \\\n    \"$status_url\")\n  status=$(jq -r '.status' <<<\"$report\")\n  case \"$status\" in\n    completed) break ;;\n    failed) jq -r '.error.message // \"Analysis failed\"' <<<\"$report\" >&2; exit 1 ;;\n    queued|running)\n      retry_after=$(awk 'BEGIN { IGNORECASE=1 } /^Retry-After:/ { gsub(\"\\r\", \"\", $2); print $2 }' \"$headers_file\" | tail -1)\n      retry_after=${retry_after:-2}\n      ;;\n    *) echo \"Unknown status: $status\" >&2; exit 1 ;;\n  esac\ndone\n\n# Procesa el resultado únicamente después de completed.\ntest \"$status\" = completed || { echo 'Polling timed out' >&2; exit 1; }\njq '.result.measurements, .result.suggestions' <<<\"$report\"","python":"import os\nimport time\nimport uuid\nfrom urllib.parse import urljoin\n\nimport requests\n\n# Guarda la clave fuera del código y cárgala desde el entorno.\nAPI_BASE = 'http://uned.tail9cc3dd.ts.net'\nAPI_KEY = os.environ[\"ARTEXT_API_KEY\"]\nHEADERS = {\"Authorization\": f\"Bearer {API_KEY}\"}\npayload = {'language': 'es', 'domain_slug': 'lenguaje-claro', 'text_type_slug': 'texto-juridico-administrativo-dirigido-a-la-ciudadania', 'text': 'En el día de hoy se realizará la revisión de la solicitud.', 'external_id': 'document-123'}\n\n# Crea el informe una sola vez. Idempotency-Key hace seguro repetir el POST tras un fallo de red.\nresponse = requests.post(\n    f\"{API_BASE}/v1/reports\",\n    headers={**HEADERS, \"Idempotency-Key\": str(uuid.uuid4())},\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\ncreated = response.json()\n# status_url es relativo: resuélvelo contra API_BASE.\nstatus_url = urljoin(f\"{API_BASE}/\", created[\"status_url\"])\nretry_after = int(response.headers.get(\"Retry-After\", \"2\"))\n\n# Consulta el mismo informe respetando Retry-After y con un límite total.\ndeadline = time.monotonic() + 180\nwhile True:\n    time.sleep(retry_after)\n    response = requests.get(status_url, headers=HEADERS, timeout=30)\n    retry_after = int(response.headers.get(\"Retry-After\", \"2\"))\n    if response.status_code == 429:\n        continue\n    response.raise_for_status()\n    report = response.json()\n    status = report[\"status\"]\n    if status == \"completed\":\n        break\n    if status == \"failed\":\n        raise RuntimeError((report.get(\"error\") or {}).get(\"message\") or \"Analysis failed\")\n    if status not in {\"queued\", \"running\"}:\n        raise RuntimeError(f\"Unknown report status: {status}\")\n    if time.monotonic() >= deadline:\n        raise TimeoutError(\"Report polling timed out\")\n\n# Procesa el resultado únicamente después de completed.\nresult = report[\"result\"]\nfor measurement in result[\"measurements\"]:\n    print(measurement[\"metric_id\"], measurement[\"value\"], measurement[\"unit\"])\nfor index, suggestion in enumerate(result[\"suggestions\"]):\n    print(index, suggestion[\"metric_id\"], len(suggestion[\"occurrences\"]))","javascript":"// Node.js 18+. Ejecuta este código en servidor; nunca expongas la clave en el navegador.\nconst API_BASE = \"http://uned.tail9cc3dd.ts.net\";\nconst API_KEY = process.env.ARTEXT_API_KEY;\nif (!API_KEY) throw new Error(\"ARTEXT_API_KEY is required\");\n\nconst headers = {\n  \"Authorization\": `Bearer ${API_KEY}`,\n  \"Content-Type\": \"application/json\"\n};\nconst api = async (path, options = {}) => {\n  const response = await fetch(new URL(path, `${API_BASE}/`), {\n    ...options,\n    headers: { ...headers, ...options.headers },\n    signal: AbortSignal.timeout(30_000)\n  });\n  if (!response.ok && response.status !== 429) {\n    throw new Error(`${response.status}: ${await response.text()}`);\n  }\n  return response;\n};\nconst delay = (milliseconds) =>\n  new Promise((resolve) => setTimeout(resolve, milliseconds));\n\n// Crea el informe una sola vez. Idempotency-Key hace seguro repetir el POST tras un fallo de red.\nconst createdResponse = await api(\"v1/reports\", {\n  method: \"POST\",\n  headers: { \"Idempotency-Key\": crypto.randomUUID() },\n  body: JSON.stringify({\"language\":\"es\",\"domain_slug\":\"lenguaje-claro\",\"text_type_slug\":\"texto-juridico-administrativo-dirigido-a-la-ciudadania\",\"text\":\"En el día de hoy se realizará la revisión de la solicitud.\",\"external_id\":\"document-123\"})\n});\nconst created = await createdResponse.json();\nconst deadline = Date.now() + 180_000;\nlet retryAfter = Number(createdResponse.headers.get(\"Retry-After\") || 2);\nlet report;\n\n// Consulta el mismo informe respetando Retry-After y con un límite total.\nwhile (Date.now() < deadline) {\n  await delay(retryAfter * 1_000);\n  const statusResponse = await api(created.status_url);\n  retryAfter = Number(statusResponse.headers.get(\"Retry-After\") || 2);\n  if (statusResponse.status === 429) continue;\n  report = await statusResponse.json();\n  if (report.status === \"completed\") break;\n  if (report.status === \"failed\") {\n    throw new Error(report.error?.message || \"Analysis failed\");\n  }\n  if (![\"queued\", \"running\"].includes(report.status)) {\n    throw new Error(`Unknown report status: ${report.status}`);\n  }\n}\nif (report?.status !== \"completed\") {\n  throw new Error(\"Report polling timed out\");\n}\n\n// Procesa el resultado únicamente después de completed.\nfor (const item of report.result.measurements) {\n  console.log(item.metric_id, item.value, item.unit);\n}\nfor (const [index, item] of report.result.suggestions.entries()) {\n  console.log(index, item.metric_id, item.occurrences.length);\n}"},"sdk":{"python":"# Instala el cliente oficial: pip install artext\n# Ejecuta este código en servidor; nunca expongas la clave en el navegador.\nimport os\n\nfrom artext import Artext\n\nclient = Artext(\n    api_key=os.environ[\"ARTEXT_API_KEY\"],\n    base_url=\"http://uned.tail9cc3dd.ts.net\",\n)\n\n# analyze crea el informe, espera respetando Retry-After y devuelve el resultado.\n# Un análisis fallido lanza una excepción con el detalle del error.\nreport = client.analyze(\n    \"En el día de hoy se realizará la revisión de la solicitud.\",\n    language=\"es\",\n    domain_slug=\"lenguaje-claro\",\n    text_type_slug=\"texto-juridico-administrativo-dirigido-a-la-ciudadania\",\n)\n\nprint(report.result.measurement(\"word-count\").value)\nfor suggestion in report.result.suggestions:\n    print(suggestion.metric_id, suggestion.summary)\n    for occurrence in suggestion.occurrences:\n        print(\"  \", occurrence.text, \"->\", occurrence.replacement)\n","javascript":"// Instala el cliente oficial: npm install artext\n// Ejecuta este código en servidor; nunca expongas la clave en el navegador.\nimport { Artext } from \"artext\";\n\nconst client = new Artext({\n  apiKey: process.env.ARTEXT_API_KEY,\n  baseUrl: \"http://uned.tail9cc3dd.ts.net\",\n});\n\n// analyze crea el informe, espera respetando Retry-After y devuelve el resultado.\n// Un análisis fallido lanza una excepción con el detalle del error.\nconst report = await client.analyze({\n  text: \"En el día de hoy se realizará la revisión de la solicitud.\",\n  language: \"es\",\n  domainSlug: \"lenguaje-claro\",\n  textTypeSlug: \"texto-juridico-administrativo-dirigido-a-la-ciudadania\",\n});\n\nfor (const suggestion of report.result.suggestions) {\n  console.log(suggestion.metric_id, suggestion.summary);\n  for (const occurrence of suggestion.occurrences) {\n    console.log(\"  \", occurrence.text, \"->\", occurrence.replacement);\n  }\n}\n"},"request":{"language":"es","domain_slug":"lenguaje-claro","text_type_slug":"texto-juridico-administrativo-dirigido-a-la-ciudadania","text":"En el día de hoy se realizará la revisión de la solicitud.","external_id":"document-123"},"responses":{"queued":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"queued","status_url":"/v1/reports/rep_7f12a4c8","events_url":"/v1/reports/rep_7f12a4c8/events"},"completed":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"completed","position_in_queue":null,"result":{"schema_version":"1.0","offset_unit":"unicode_code_points","language":"es","genre":{"domain":{"slug":"lenguaje-claro","name":"Lenguaje claro"},"text_type":{"slug":"texto-juridico-administrativo-dirigido-a-la-ciudadania","name":"Texto jurídico-administrativo dirigido a la ciudadanía"}},"measurements":[{"metric_id":"word-count","value":12,"unit":"words","implementation_version":"1.0"}],"suggestions":[{"id":"sug_1","metric_id":"redundant-expressions","implementation_version":"1.0","metric_title":"Eliminación de expresiones redundantes","category":"lexical","severity":"warning","summary":"Conviene formular el fragmento de manera más directa.","explanation":null,"occurrences":[{"id":"occ_1","text":"En el día de hoy","paragraph_index":0,"sentence_index":0,"start":0,"end":16,"replacement":null,"alternatives":[],"edits":[{"start":0,"end":16,"text":""}]}],"recommendations":["Elimina la expresión si el contexto lo permite."]}]},"error":null},"failed":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"failed","position_in_queue":null,"result":null,"error":{"code":"analysis_failed","message":"The analysis could not be completed."}}}}}