{"openapi":"3.1.0","info":{"title":"arText Reports API","description":"Envía un texto, consulta el informe asíncrono y utiliza recomendaciones 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: unknown genre, or a text_type_slug that names more than one genre and needs domain_slug.","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":"2.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"}},"measurements":[{"metric_id":"word-count","value":12,"unit":"words","implementation_version":"1.0"}],"recommendations":[{"id":"rec_1","metric_id":"redundant-expressions","implementation_version":"1.0","metric_title":"Eliminación de expresiones redundantes","category":"lexical","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":""}]}],"guidance":["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 rule 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|rule)$"},{"type":"null"}],"title":"Kind"},"description":"Filter by measurement or rule."},{"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":[]}]}},"/v1/text-types/{domain_slug}/{text_type_slug}/glossaries":{"get":{"tags":["Text types"],"summary":"List the glossaries a genre resolves","description":"Returns which glossaries apply to a genre, how many entries each one resolves and which cascade level (genre, domain or language-wide) those entries come from. Glossary contents are not exposed.","operationId":"get_genre_glossaries_v1_text_types__domain_slug___text_type_slug__glossaries_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":"Glossary availability for the genre.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenreGlossaries"}}},"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."},"GenreGlossaries":{"properties":{"language":{"type":"string","title":"Language"},"domain_slug":{"type":"string","title":"Domain Slug"},"text_type_slug":{"type":"string","title":"Text Type Slug"},"glossaries":{"items":{"$ref":"#/components/schemas/Glossary"},"type":"array","title":"Glossaries"}},"type":"object","required":["language","domain_slug","text_type_slug","glossaries"],"title":"GenreGlossaries"},"Glossary":{"properties":{"key":{"type":"string","title":"Key","description":"Stable glossary identifier."},"title":{"type":"string","title":"Title","description":"Human-readable glossary name."},"entry_count":{"type":"integer","title":"Entry Count","description":"Entries resolved for this genre, active or not."},"active_entry_count":{"type":"integer","title":"Active Entry Count","description":"Entries actually applied; the rest are deliberately disabled."},"source_level":{"type":"string","enum":["text_type","domain","global"],"title":"Source Level","description":"Cascade level the entries come from. The most specific level holding rows wins: the genre's own list, the domain's, or the language-wide one."},"inherited":{"type":"boolean","title":"Inherited","description":"True when source_level is less specific than the requested genre."},"used_by_metrics":{"items":{"type":"string"},"type":"array","title":"Used By Metrics","description":"metric_id values enabled for this genre that read this glossary."}},"type":"object","required":["key","title","entry_count","active_entry_count","source_level","inherited"],"title":"Glossary","description":"Disponibilidad de un glosario para un género. Sin contenido."},"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","rule"],"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 recommendation."},"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."},"spans":{"items":{"$ref":"#/components/schemas/TextSpanBase"},"type":"array","title":"Spans","description":"Every appearance of this occurrence in the text, in reading order, when the metric identifies a unit rather than a single fragment: a repeated word or connector, or a glossary entry found in several places. The first entry is the one published in start/end. Empty when the occurrence appears only once."},"connectors":{"items":{"$ref":"#/components/schemas/SentenceConnectorBase"},"type":"array","title":"Connectors","description":"Discourse connectors found inside this occurrence, in reading order, with the alternatives that would fit if the sentence were split there. Only long sentences fill this; every other metric leaves it empty."},"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"},"Recommendation":{"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."},"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."},"guidance":{"items":{"type":"string"},"type":"array","title":"Guidance","description":"Human-readable review guidance written by the editorial team."},"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, in reading order: sorted by `start`, with occurrences that could not be located last. The order of the recommendations themselves is linguistic, not positional."}},"type":"object","required":["id","metric_id","implementation_version","metric_title","category","summary"],"title":"Recommendation","description":"Un problema encontrado por una regla, con todas sus apariciones.\n\nEl contrato público la llama `Recommendation` y no `Suggestion` porque es\nel término que usa el producto de cara a quien lo lee. El modelo interno\n(`app.domain.models.Suggestion`) conserva su nombre: el renombrado es de la\nproyección pública, no de la nomenclatura del motor."},"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":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Domain Slug","description":"Optional text domain (ámbito) the genre belongs to, as listed by GET /v1/text-types. Omit it to name the genre by text_type_slug alone; send it only to disambiguate after a 422 with code ambiguous-genre.","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":["text_type_slug","text"],"title":"ReportRequest","description":"Stable input accepted from API integrations."},"ReportResult":{"properties":{"schema_version":{"type":"string","const":"2.0","title":"Schema Version","description":"Version of this public result contract.","default":"2.0"},"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"},"recommendations":{"items":{"$ref":"#/components/schemas/Recommendation"},"type":"array","title":"Recommendations","description":"Issues found by the suggestion rules, one item per rule that matched."},"applied_thresholds":{"additionalProperties":{"additionalProperties":true,"type":"object"},"type":"object","title":"Applied Thresholds","description":"Thresholds each rule actually read while evaluating this report, keyed by metric_id. Rules disabled for the genre never run and are absent. Values are the effective ones, whether configured for the genre or inherited from the language defaults."},"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"}},"type":"object","required":["language","genre"],"title":"ReportResult"},"ReportStatus":{"type":"string","enum":["queued","running","completed","failed"],"title":"ReportStatus"},"SentenceConnectorBase":{"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":"Fragment found in this range."},"relation":{"type":"string","title":"Relation","description":"Semantic relation the connector expresses (cause, antithesis, concession…), as named by the discourse connector model."},"scope":{"type":"string","pattern":"^(intra|inter)$","title":"Scope","description":"Whether the connector joins clauses inside one sentence (intra) or links separate sentences (inter). The ones found inside a long sentence are intra; their alternatives are inter, because splitting turns the parts into sentences."},"alternatives":{"items":{"type":"string"},"type":"array","title":"Alternatives","description":"Inter-sentence connectors of the same relation, capitalised and in alphabetical order: what to write instead if the sentence is split at this point."}},"type":"object","required":["start","end","text","relation","scope"],"title":"SentenceConnectorBase","description":"A discourse connector found inside a long sentence, with its position.\n\nIt is not another appearance of the occurrence —that is what `spans` is for—\nbut something that lives inside it: the connector joining two clauses of a\nsentence the report proposes to split. Splitting turns those clauses into\nseparate sentences, so the connector would have to change with them, and\nthat is why it travels with its own alternatives."},"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"},"TextSpanBase":{"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":"Fragment found in this range."}},"type":"object","required":["start","end","text"],"title":"TextSpanBase","description":"Un tramo del texto original, con lo que hay escrito en él."},"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://demo.sistema-artext.com"}],"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":"rule","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":"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":"rec_1","metric_id":"acronym-consistency","implementation_version":"1.2","metric_title":"Sistematicidad en el uso de siglas","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"acronym-introduction","kind":"rule","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":"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":"rec_1","metric_id":"acronym-introduction","implementation_version":"1.2","metric_title":"Introducción de siglas","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"archaic-verb-forms","kind":"rule","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":"rec_1","metric_id":"archaic-verb-forms","implementation_version":"1.2","metric_title":"Eliminación de formas verbales arcaicas","category":"morphosyntax","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"connector-variety","kind":"rule","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":"rec_1","metric_id":"connector-variety","implementation_version":"1.2","metric_title":"Variación de conectores","category":"discourse","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"definition-usage","kind":"rule","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":"rec_1","metric_id":"definition-usage","implementation_version":"1.2","metric_title":"Uso de definiciones","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"difficult-expressions","kind":"rule","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":"rec_1","metric_id":"difficult-expressions","implementation_version":"1.2","metric_title":"Sustitución de expresiones difíciles de entender","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"gerunds","kind":"rule","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":"rec_1","metric_id":"gerunds","implementation_version":"1.2","metric_title":"Revisión de gerundios","category":"morphosyntax","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"imprecise-words","kind":"rule","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":"rec_1","metric_id":"imprecise-words","implementation_version":"1.2","metric_title":"Sustitución de palabras poco precisas","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"list-usage","kind":"rule","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, sin señalar ningún fragmento, cuando no se ha reconocido ninguna lista estructurada en el texto.","methodology":"Devuelve una única sugerencia de documento, sin posiciones de texto, 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":"rec_1","metric_id":"list-usage","implementation_version":"1.2","metric_title":"Inclusión de listas","category":"discourse","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-paragraphs","kind":"rule","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":"rec_1","metric_id":"long-paragraphs","implementation_version":"1.2","metric_title":"Revisión de párrafos largos","category":"discourse","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-sentences","kind":"rule","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 que superan el límite de palabras configurado para el género textual y, cuando la oración admite división, los puntos donde podría partirse.","methodology":"Selecciona oraciones con más de n_division_oraciones palabras; en las que tienen varios segmentos devuelve además 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":"rec_1","metric_id":"long-sentences","implementation_version":"1.2","metric_title":"Revisión de oraciones largas","category":"discourse","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-words","kind":"rule","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":"rec_1","metric_id":"long-words","implementation_version":"1.2","metric_title":"Revisión de palabras largas","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"missing-definitions","kind":"rule","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":"rec_1","metric_id":"missing-definitions","implementation_version":"1.2","metric_title":"Carencia de definiciones","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"negative-formulations","kind":"rule","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":"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":"rec_1","metric_id":"negative-formulations","implementation_version":"1.2","metric_title":"Reformulación de ideas expresadas en negativo","category":"morphosyntax","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"paragraph-initial-connectors","kind":"rule","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":"rec_1","metric_id":"paragraph-initial-connectors","implementation_version":"1.2","metric_title":"Introducción de conectores al inicio de párrafos","category":"discourse","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"participles","kind":"rule","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":"rec_1","metric_id":"participles","implementation_version":"1.2","metric_title":"Revisión de participios","category":"morphosyntax","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"passive-voice","kind":"rule","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":"rec_1","metric_id":"passive-voice","implementation_version":"1.2","metric_title":"Uso de la voz pasiva","category":"morphosyntax","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"redundant-expressions","kind":"rule","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":"rec_1","metric_id":"redundant-expressions","implementation_version":"1.2","metric_title":"Eliminación de expresiones redundantes","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"single-sentence-paragraphs","kind":"rule","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 que contenga exactamente una oración cerrada y llegue al mínimo de palabras del género; los fragmentos sin puntuación final (líneas de cabecera) no cuentan como oració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":"rec_1","metric_id":"single-sentence-paragraphs","implementation_version":"1.2","metric_title":"Revisión de párrafos-oración","category":"discourse","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"subjectivity-markers","kind":"rule","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":"rec_1","metric_id":"subjectivity-markers","implementation_version":"1.2","metric_title":"Uso de indicadores de subjetividad","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"transparent-terms","kind":"rule","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":"rec_1","metric_id":"transparent-terms","implementation_version":"1.2","metric_title":"Utilización de términos más transparentes","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"verb-person-consistency","kind":"rule","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":"rec_1","metric_id":"verb-person-consistency","implementation_version":"1.2","metric_title":"Sistematicidad en el uso de verbos en primera persona","category":"morphosyntax","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"verbal-nominalizations","kind":"rule","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","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":"rec_1","metric_id":"verbal-nominalizations","implementation_version":"1.2","metric_title":"Revisión de nominalizaciones verbales","category":"morphosyntax","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"word-repetition","kind":"rule","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":"rec_1","metric_id":"word-repetition","implementation_version":"1.2","metric_title":"Repetición de palabras","category":"lexical","summary":"Example detection returned by this metric.","explanation":null,"guidance":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"spans":[],"connectors":[],"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://demo.sistema-artext.com'\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.recommendations' <<<\"$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://demo.sistema-artext.com'\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, recommendation in enumerate(result[\"recommendations\"]):\n    print(index, recommendation[\"metric_id\"], len(recommendation[\"occurrences\"]))","javascript":"// Node.js 18+. Ejecuta este código en servidor; nunca expongas la clave en el navegador.\nconst API_BASE = \"http://demo.sistema-artext.com\";\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.recommendations.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://demo.sistema-artext.com\",\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 recommendation in report.result.recommendations:\n    print(recommendation.metric_id, recommendation.summary)\n    for occurrence in recommendation.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://demo.sistema-artext.com\",\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 recommendation of report.result.recommendations) {\n  console.log(recommendation.metric_id, recommendation.summary);\n  for (const occurrence of recommendation.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":"2.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"}},"measurements":[{"metric_id":"word-count","value":12,"unit":"words","implementation_version":"1.0"}],"recommendations":[{"id":"rec_1","metric_id":"redundant-expressions","implementation_version":"1.0","metric_title":"Eliminación de expresiones redundantes","category":"lexical","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":""}]}],"guidance":["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."}}}}}