{"openapi":"3.1.0","info":{"title":"arText Reports API","description":"Envía un texto, consulta el informe asíncrono y utiliza sugerencias lingüísticas estructuradas en tus propios productos.","version":"1.0.0"},"paths":{"/v1/reports":{"post":{"tags":["Reports"],"summary":"Crear un informe de análisis","description":"Valida y encola un texto. La respuesta 202 incluye recursos de estado y eventos; no implica que el análisis haya terminado.","operationId":"create_public_report_v1_reports_post","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":255},{"type":"null"}],"title":"Idempotency-Key"},"description":"Recommended unique key for safely retrying this creation request. It is scoped to the authenticated caller, retained for 24 hours, and must not be reused with a different payload.","example":"document-123-analysis-v1"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportRequest"},"examples":{"basic":{"summary":"Create a report","value":{"language":"es","genre":"informe","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.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"503":{"description":"The analysis queue is unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}}},"security":[{"ApiKey":[]}]}},"/v1/reports/{report_id}":{"get":{"tags":["Reports"],"summary":"Consultar estado y resultado","description":"Devuelve el estado actual. En queued/running hay que respetar Retry-After; completed y failed son terminales.","operationId":"get_public_report_v1_reports__report_id__get","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"},"description":"Unique report identifier returned by POST /v1/reports."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Report"},"examples":{"queued":{"summary":"Report queued","value":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"queued","status_url":"/v1/reports/rep_7f12a4c8","events_url":"/v1/reports/rep_7f12a4c8/events"}},"completed":{"summary":"Report completed","value":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"completed","position_in_queue":null,"result":{"schema_version":"1.0","offset_unit":"unicode_code_points","language":"es","genre":{"id":"informe","title":"Informe"},"measurements":[{"metric_id":"word-count","value":12,"unit":"words","implementation_version":"1.0"}],"suggestions":[{"id":"sug_1","metric_id":"redundant-expressions","implementation_version":"1.0","metric_title":"Eliminación de expresiones redundantes","category":"lexical","severity":"warning","summary":"Conviene formular el fragmento de manera más directa.","explanation":null,"occurrences":[{"id":"occ_1","text":"En el día de hoy","paragraph_index":0,"sentence_index":0,"start":0,"end":16,"replacement":null,"alternatives":[],"edits":[{"start":0,"end":16,"text":""}]}],"recommendations":["Elimina la expresión si el contexto lo permite."]}]},"error":null}},"failed":{"summary":"Report failed","value":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"failed","position_in_queue":null,"result":null,"error":{"code":"analysis_failed","message":"The analysis could not be completed."}}}}}},"headers":{"Retry-After":{"description":"Present for queued/running reports; wait at least this many seconds before polling again.","schema":{"type":"integer","minimum":1,"example":5}},"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"404":{"description":"Report not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"429":{"description":"Status rate limit or minimum polling interval exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"503":{"description":"Request protection is temporarily unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"422":{"description":"Validation Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/reports/{report_id}/events":{"get":{"tags":["Reports"],"summary":"Recibir transiciones por SSE","description":"Canal SSE opcional con el estado inicial, cambios y heartbeats. Se cierra al terminar; el polling sigue siendo el respaldo compatible.","operationId":"stream_public_report_events_v1_reports__report_id__events_get","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"},"description":"Unique report identifier returned by POST /v1/reports."}],"responses":{"200":{"description":"SSE status stream.","content":{"text/event-stream":{"schema":{"type":"string"},"examples":{"transitions":{"summary":"A stream closes after completed or failed","value":"event: status\\ndata: {\"report_id\":\"rep_7f12a4c8\",\"status\":\"running\",\"updated_at\":\"2026-07-15T10:00:02Z\"}\\n\\nevent: status\\ndata: {\"report_id\":\"rep_7f12a4c8\",\"status\":\"completed\",\"updated_at\":\"2026-07-15T10:00:06Z\"}\\n\\n"}}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Unauthorized","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Not Found","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"429":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Too Many Requests","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Service Unavailable","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"422":{"description":"Validation Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/metrics":{"get":{"tags":["Metrics"],"summary":"List metric implementations","description":"Returns measurement and suggestion implementations, including inactive variants and localized documentation.","operationId":"list_metrics_v1_metrics_get","parameters":[{"name":"analysis_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Analysis Language"},"description":"Language implementation to return: es, ca or en."},{"name":"documentation_language","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Documentation Language"},"description":"Language used for titles and documentation: es, ca or en."},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(measurement|suggestion)$"},{"type":"null"}],"title":"Kind"},"description":"Filter by measurement or suggestion."},{"name":"enabled","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"description":"Filter by the current implementation status."}],"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":"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/{metric_id}":{"get":{"tags":["Metrics"],"summary":"Get one metric implementation","description":"Returns the status, category and localized documentation for one language variant.","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."}],"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":"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":[]}]}}},"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":{"id":{"type":"string","title":"Id","description":"Stable genre identifier."},"title":{"type":"string","title":"Title","description":"Localized genre title."}},"type":"object","required":["id","title"],"title":"Genre"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Measurement":{"properties":{"metric_id":{"type":"string","title":"Metric Id","description":"Stable metric identifier documented by GET /v1/metrics/{metric_id}."},"value":{"anyOf":[{"type":"integer"},{"type":"number"}],"title":"Value","description":"Calculated numeric value."},"unit":{"type":"string","title":"Unit","description":"Semantic unit defined by the metric catalog."},"implementation_version":{"type":"string","title":"Implementation Version","description":"Calculator version used for this report."}},"type":"object","required":["metric_id","value","unit","implementation_version"],"title":"Measurement"},"Metric":{"properties":{"id":{"type":"string","title":"Id"},"kind":{"type":"string","enum":["measurement","suggestion"],"title":"Kind"},"analysis_language":{"type":"string","title":"Analysis Language"},"enabled":{"type":"boolean","title":"Enabled","description":"Whether reports for analysis_language currently evaluate this variant."},"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":{"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"},"Occurrence":{"properties":{"id":{"type":"string","title":"Id","description":"Identifier unique within its parent suggestion."},"text":{"type":"string","title":"Text","description":"Fragment identified by the metric."},"paragraph_index":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Paragraph Index","description":"Zero-based paragraph index when available."},"sentence_index":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Sentence Index","description":"Zero-based sentence index when available."},"start":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Start","description":"Inclusive offset in the original input text."},"end":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"End","description":"Exclusive offset in the original input text."},"replacement":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Replacement","description":"Preferred direct replacement, or null when none is available."},"alternatives":{"items":{"type":"string"},"type":"array","title":"Alternatives","description":"Additional reviewed replacement candidates, excluding replacement."},"edits":{"items":{"$ref":"#/components/schemas/TextEdit"},"type":"array","title":"Edits","description":"Validated edits for the preferred deterministic change, ordered from the highest offset to the lowest."}},"type":"object","required":["id","text"],"title":"Occurrence"},"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."},"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."}},"type":"object","required":["report_id","status","status_url","events_url"],"title":"ReportAccepted"},"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"},"ReportRequest":{"properties":{"language":{"type":"string","title":"Language","description":"Language used for analysis: es, ca or en.","default":"es"},"genre":{"type":"string","title":"Genre","description":"Text genre identifier used to select thresholds and rules.","default":"informe"},"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"],"title":"ReportRequest","description":"Stable input accepted from API integrations."},"ReportResult":{"properties":{"schema_version":{"type":"string","const":"1.0","title":"Schema Version","description":"Version of this public result contract.","default":"1.0"},"offset_unit":{"type":"string","const":"unicode_code_points","title":"Offset Unit","description":"Unit used by every start and end offset in the result.","default":"unicode_code_points"},"language":{"type":"string","title":"Language","description":"Language actually used for analysis."},"genre":{"$ref":"#/components/schemas/Genre"},"measurements":{"items":{"$ref":"#/components/schemas/Measurement"},"type":"array","title":"Measurements"},"suggestions":{"items":{"$ref":"#/components/schemas/Suggestion"},"type":"array","title":"Suggestions"}},"type":"object","required":["language","genre"],"title":"ReportResult"},"ReportStatus":{"type":"string","enum":["queued","running","completed","failed"],"title":"ReportStatus"},"Suggestion":{"properties":{"id":{"type":"string","title":"Id","description":"Identifier unique within this report."},"metric_id":{"type":"string","title":"Metric Id","description":"Stable metric identifier documented by GET /v1/metrics/{metric_id}."},"implementation_version":{"type":"string","title":"Implementation Version","description":"Detector version used for this report."},"metric_title":{"type":"string","title":"Metric Title","description":"Localized human-readable metric title captured for this report."},"category":{"type":"string","title":"Category","description":"Stable linguistic category used by the metric catalog."},"severity":{"type":"string","title":"Severity","description":"Review priority; it is not a confidence score."},"summary":{"type":"string","title":"Summary","description":"Explanation of the issue found in this report."},"explanation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Explanation","description":"Optional extended contextual explanation."},"recommendations":{"items":{"type":"string"},"type":"array","title":"Recommendations","description":"Human-readable review guidance."},"occurrences":{"items":{"$ref":"#/components/schemas/Occurrence"},"type":"array","title":"Occurrences","description":"Concrete appearances of this issue in the submitted text."}},"type":"object","required":["id","metric_id","implementation_version","metric_title","category","severity","summary"],"title":"Suggestion"},"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"},"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://wuned.tail9cc3dd.ts.net"}],"x-artext-metrics":[{"id":"average-sentence-words","kind":"measurement","analysis_language":"es","enabled":true,"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,"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,"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,"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,"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,"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,"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,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"es","documentation":{"title":"Word count","summary":"Número de palabras del texto normalizado.","methodology":"Normaliza los espacios del texto y cuenta cada elemento no vacío separado por espacios.","limitations":"El valor depende de la normalización y segmentación del analizador; debe interpretarse junto con las demás métricas y el género textual.","interpretation":"`value` contiene el resultado numérico y `unit` es `count`. La ausencia indica que la variante estaba desactivada."},"references":[],"example":{"metric_id":"word-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"acronym-consistency","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"acronyms","unit":null,"documentation_language":"es","documentation":{"title":"Sistematicidad en el uso de siglas","summary":"Detecta repeticiones del término desarrollado después de que ya se haya introducido su sigla.","methodology":"Después de identificar sigla y desarrollo en su primera aparición, busca si el desarrollo completo vuelve a aparecer más adelante.","limitations":"Es una señal de revisión, no una corrección automática ni una evaluación completa de la calidad del texto.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"acronym-consistency","implementation_version":"1.2","metric_title":"Sistematicidad en el uso de siglas","category":"acronyms","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"acronym-introduction","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"acronyms","unit":null,"documentation_language":"es","documentation":{"title":"Introducción de siglas","summary":"Detecta la primera aparición de siglas en mayúsculas que no parecen acompañadas por su desarrollo.","methodology":"Extrae candidatos en mayúsculas, ignora palabras vacías, conserva la primera aparición y busca un desarrollo adyacente antes o después.","limitations":"Es una señal de revisión, no una corrección automática ni una evaluación completa de la calidad del texto.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"acronym-introduction","implementation_version":"1.2","metric_title":"Introducción de siglas","category":"acronyms","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"archaic-verb-forms","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Eliminación de formas verbales arcaicas","summary":"Detecta formas etiquetadas como futuro de subjuntivo, consideradas arcaicas en el uso general actual.","methodology":"Busca etiquetas FreeLing que empiezan por VSSF, VASF o VMSF, también en vaux-cat, excluyendo auxiliares.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"archaic-verb-forms","implementation_version":"1.2","metric_title":"Eliminación de formas verbales arcaicas","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"connector-variety","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Variación de conectores","summary":"Detecta conectores discursivos repetidos por encima del umbral y ofrece alternativas de la misma relación.","methodology":"Cuenta nodos disc_mk del XML, normaliza sus formas y señala conectores del modelo cuya frecuencia supera n_variacion_conectores. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"connector-variety","implementation_version":"1.2","metric_title":"Variación de conectores","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"difficult-expressions","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Sustitución de expresiones difíciles de entender","summary":"Localiza expresiones difíciles de entender incluidas en el catálogo y devuelve alternativas cuando existen.","methodology":"Busca expresiones completas directamente sobre el texto, sin distinguir mayúsculas ni acentos, respetando límites de palabra. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"difficult-expressions","implementation_version":"1.2","metric_title":"Sustitución de expresiones difíciles de entender","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"gerunds","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de gerundios","summary":"Localiza gerundios independientes que conviene revisar, excluyendo auxiliares y construcciones compuestas reconocidas.","methodology":"Usa etiquetas verbales de gerundio (VBG en EN) y atributos auxiliares para excluir perífrasis ya reconocidas.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"gerunds","implementation_version":"1.2","metric_title":"Revisión de gerundios","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"imprecise-words","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Sustitución de palabras poco precisas","summary":"Localiza palabras o secuencias poco precisas incluidas en el catálogo editable y ofrece sus alternativas.","methodology":"Normaliza los lemas de tokens y busca secuencias completas del catálogo activo; devuelve término, alternativa y posiciones de texto cuando están disponibles. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"imprecise-words","implementation_version":"1.2","metric_title":"Sustitución de palabras poco precisas","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"list-usage","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Inclusión de listas","summary":"Avisa cuando no se ha reconocido ninguna lista estructurada en el documento.","methodology":"Devuelve una sugerencia documental cuando has_list es falso; has_list se obtiene del texto y del HTML estructurado.","limitations":"Depende de la segmentación del texto plano y de que los marcadores estructurales sean reconocibles; no implica por sí sola un error de redacción.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"list-usage","implementation_version":"1.2","metric_title":"Inclusión de listas","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-paragraphs","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de párrafos largos","summary":"Señala párrafos que superan el límite de palabras configurado para el género textual.","methodology":"Compara word_count de cada párrafo con n_palabras_por_parrafo del género; solo informa valores estrictamente superiores.","limitations":"Depende de la segmentación del texto plano y de que los marcadores estructurales sean reconocibles; no implica por sí sola un error de redacción.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"long-paragraphs","implementation_version":"1.2","metric_title":"Revisión de párrafos largos","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-segmented-sentences","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"División de oraciones largas","summary":"Señala oraciones largas con varios segmentos y propone puntos estructurales donde podrían dividirse.","methodology":"Selecciona oraciones con más de n_division_oraciones palabras y varios segmentos; devuelve segmentos, conectores y posibles límites de división.","limitations":"Depende de la segmentación del texto plano y de que los marcadores estructurales sean reconocibles; no implica por sí sola un error de redacción.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"long-segmented-sentences","implementation_version":"1.2","metric_title":"División de oraciones largas","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-unsegmented-sentences","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de oraciones largas","summary":"Señala oraciones largas que DiSeg no ha dividido en segmentos internos.","methodology":"Selecciona oraciones con más de n_division_oraciones palabras y como máximo un segmento DiSeg.","limitations":"Depende de la segmentación del texto plano y de que los marcadores estructurales sean reconocibles; no implica por sí sola un error de redacción.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"long-unsegmented-sentences","implementation_version":"1.2","metric_title":"Revisión de oraciones largas","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-words","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de palabras largas","summary":"Localiza palabras largas incluidas en el catálogo editorial y propone alternativas más breves cuando existen.","methodology":"Normaliza los lemas de tokens y busca secuencias completas del catálogo activo; devuelve término, alternativa y posiciones de texto cuando están disponibles. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"long-words","implementation_version":"1.2","metric_title":"Revisión de palabras largas","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"negative-formulations","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Reformulación de ideas expresadas en negativo","summary":"Localiza palabras o secuencias negativas catalogadas para favorecer formulaciones afirmativas y directas.","methodology":"Normaliza los lemas de tokens y busca secuencias completas del catálogo activo; devuelve término, alternativa y posiciones de texto cuando están disponibles. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"negative-formulations","implementation_version":"1.2","metric_title":"Reformulación de ideas expresadas en negativo","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"paragraph-initial-connectors","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Introducción de conectores al inicio de párrafos","summary":"Detecta párrafos que no comienzan con un conector discursivo reconocido.","methodology":"Descarta párrafos vacíos o con has_initial_connector; para el resto devuelve el párrafo y los grupos de conectores editables. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"paragraph-initial-connectors","implementation_version":"1.2","metric_title":"Introducción de conectores al inicio de párrafos","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"participles","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de participios","summary":"Localiza participios independientes y evita los que forman tiempos compuestos o pasivas reconocidas.","methodology":"Usa etiquetas de participio (VBN en EN) y excluye auxiliares, tiempos compuestos, pasivas y lemas auxiliares.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"participles","implementation_version":"1.2","metric_title":"Revisión de participios","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"passive-voice","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Uso de la voz pasiva","summary":"Localiza construcciones que el análisis morfosintáctico identifica como posibles voces pasivas.","methodology":"En ES/CA usa nodos verb_pass o ger_pas y, como respaldo, secuencias ser/ésser + participio. En EN busca be + VBN en vb_chunk y después en tokens.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"passive-voice","implementation_version":"1.2","metric_title":"Uso de la voz pasiva","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"redundant-expressions","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Eliminación de expresiones redundantes","summary":"Localiza expresiones redundantes del catálogo para eliminarlas o reformularlas.","methodology":"Busca expresiones completas directamente sobre el texto, sin distinguir mayúsculas ni acentos, respetando límites de palabra. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"redundant-expressions","implementation_version":"1.2","metric_title":"Eliminación de expresiones redundantes","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"single-sentence-paragraphs","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de párrafos-oración","summary":"Localiza párrafos formados por una sola oración para revisar si concentran demasiada información o necesitan desarrollo.","methodology":"Selecciona cada párrafo no vacío cuyo sentence_count sea exactamente 1.","limitations":"Depende de la segmentación del texto plano y de que los marcadores estructurales sean reconocibles; no implica por sí sola un error de redacción.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"single-sentence-paragraphs","implementation_version":"1.2","metric_title":"Revisión de párrafos-oración","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"subjectivity-markers","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Uso de indicadores de subjetividad","summary":"Combina léxico, secuencias, categorías morfológicas y exclamaciones para localizar posibles marcas de subjetividad.","methodology":"Une coincidencias de lemas simples, secuencias compuestas, categorías morfológicas configuradas y fragmentos entre signos de exclamación; después elimina duplicados.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"subjectivity-markers","implementation_version":"1.2","metric_title":"Uso de indicadores de subjetividad","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"transparent-terms","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"es","documentation":{"title":"Utilización de términos más transparentes","summary":"Detecta términos difíciles catalogados y propone denominaciones más transparentes.","methodology":"Normaliza los lemas de tokens y busca secuencias completas del catálogo activo; devuelve término, alternativa y posiciones de texto cuando están disponibles. La detección utiliza el catálogo editorial activo para el idioma.","limitations":"Depende de la cobertura y calidad del catálogo activo para el idioma; una coincidencia requiere revisión contextual.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"transparent-terms","implementation_version":"1.2","metric_title":"Utilización de términos más transparentes","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"verb-person-consistency","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Sistematicidad en el uso de verbos en primera persona","summary":"Avisa cuando conviven verbos en primera persona singular y plural en el mismo documento.","methodology":"Extrae persona y número de etiquetas verbales; solo genera resultado si encuentra al menos una forma 1S y una 1P.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"verb-person-consistency","implementation_version":"1.2","metric_title":"Sistematicidad en el uso de verbos en primera persona","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"verbal-nominalizations","kind":"suggestion","analysis_language":"es","enabled":true,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"es","documentation":{"title":"Revisión de nominalizaciones verbales","summary":"Detecta sustantivos con patrones productivos de nominalización verbal y excluye las excepciones catalogadas.","methodology":"Filtra sustantivos minúsculos: -ción/-ciones en ES, -ció/-cions en CA y -tion/-tions en EN; elimina entradas del catálogo de excepciones.","limitations":"Depende del etiquetado de FreeLing/DiSeg y puede producir falsos positivos o perder construcciones ambiguas.","interpretation":"Cada elemento de `occurrences` identifica un fragmento afectado. `start` es inclusivo y `end` exclusivo, ambos medidos en puntos de código Unicode sobre el texto enviado. `replacement`, `alternatives` y `edits` solo aparecen con contenido cuando existe un cambio seguro; la ausencia de esta sugerencia significa que no hubo coincidencias, no que el texto sea necesariamente correcto."},"references":[],"example":{"id":"sug_1","metric_id":"verbal-nominalizations","implementation_version":"1.2","metric_title":"Revisión de nominalizaciones verbales","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}}],"x-artext-guides":{"language":"es","examples":{"curl":"# Guarda la clave fuera del código y cárgala desde el entorno.\n# Requiere curl y jq.\n# export ARTEXT_API_KEY='art_live_...'\nset -euo pipefail\nAPI_BASE='http://wuned.tail9cc3dd.ts.net'\nIDEMPOTENCY_KEY=\"report-$(date +%s)-$RANDOM\"\nheaders_file=$(mktemp)\ntrap 'rm -f \"$headers_file\"' EXIT\n\n# Crea el informe una sola vez. Idempotency-Key hace seguro repetir el POST tras un fallo de red.\ncreated=$(curl --fail-with-body --silent --show-error --dump-header \"$headers_file\" \\\n  --request POST \"$API_BASE/v1/reports\" \\\n  --header \"Authorization: Bearer $ARTEXT_API_KEY\" \\\n  --header \"Idempotency-Key: $IDEMPOTENCY_KEY\" \\\n  --header 'Content-Type: application/json' \\\n  --data-binary '{\"language\":\"es\",\"genre\":\"informe\",\"text\":\"En el día de hoy se realizará la revisión de la solicitud.\",\"external_id\":\"document-123\"}')\n\n# status_url es relativo: resuélvelo contra API_BASE.\nstatus_url=\"$API_BASE$(jq -r '.status_url' <<<\"$created\")\"\nretry_after=$(awk 'BEGIN { IGNORECASE=1 } /^Retry-After:/ { gsub(\"\\r\", \"\", $2); print $2 }' \"$headers_file\" | tail -1)\nretry_after=${retry_after:-2}\n\n# Consulta el mismo informe respetando Retry-After y con un límite total.\nfor attempt in $(seq 1 90); do\n  sleep \"$retry_after\"\n  report=$(curl --fail-with-body --silent --show-error --dump-header \"$headers_file\" \\\n    --header \"Authorization: Bearer $ARTEXT_API_KEY\" \\\n    \"$status_url\")\n  status=$(jq -r '.status' <<<\"$report\")\n  case \"$status\" in\n    completed) break ;;\n    failed) jq -r '.error.message // \"Analysis failed\"' <<<\"$report\" >&2; exit 1 ;;\n    queued|running)\n      retry_after=$(awk 'BEGIN { IGNORECASE=1 } /^Retry-After:/ { gsub(\"\\r\", \"\", $2); print $2 }' \"$headers_file\" | tail -1)\n      retry_after=${retry_after:-2}\n      ;;\n    *) echo \"Unknown status: $status\" >&2; exit 1 ;;\n  esac\ndone\n\n# Procesa el resultado únicamente después de completed.\ntest \"$status\" = completed || { echo 'Polling timed out' >&2; exit 1; }\njq '.result.measurements, .result.suggestions' <<<\"$report\"","python":"import os\nimport time\nimport uuid\nfrom urllib.parse import urljoin\n\nimport requests\n\n# Guarda la clave fuera del código y cárgala desde el entorno.\nAPI_BASE = 'http://wuned.tail9cc3dd.ts.net'\nAPI_KEY = os.environ[\"ARTEXT_API_KEY\"]\nHEADERS = {\"Authorization\": f\"Bearer {API_KEY}\"}\npayload = {'language': 'es', 'genre': 'informe', 'text': 'En el día de hoy se realizará la revisión de la solicitud.', 'external_id': 'document-123'}\n\n# Crea el informe una sola vez. Idempotency-Key hace seguro repetir el POST tras un fallo de red.\nresponse = requests.post(\n    f\"{API_BASE}/v1/reports\",\n    headers={**HEADERS, \"Idempotency-Key\": str(uuid.uuid4())},\n    json=payload,\n    timeout=30,\n)\nresponse.raise_for_status()\ncreated = response.json()\n# status_url es relativo: resuélvelo contra API_BASE.\nstatus_url = urljoin(f\"{API_BASE}/\", created[\"status_url\"])\nretry_after = int(response.headers.get(\"Retry-After\", \"2\"))\n\n# Consulta el mismo informe respetando Retry-After y con un límite total.\ndeadline = time.monotonic() + 180\nwhile True:\n    time.sleep(retry_after)\n    response = requests.get(status_url, headers=HEADERS, timeout=30)\n    retry_after = int(response.headers.get(\"Retry-After\", \"2\"))\n    if response.status_code == 429:\n        continue\n    response.raise_for_status()\n    report = response.json()\n    status = report[\"status\"]\n    if status == \"completed\":\n        break\n    if status == \"failed\":\n        raise RuntimeError((report.get(\"error\") or {}).get(\"message\") or \"Analysis failed\")\n    if status not in {\"queued\", \"running\"}:\n        raise RuntimeError(f\"Unknown report status: {status}\")\n    if time.monotonic() >= deadline:\n        raise TimeoutError(\"Report polling timed out\")\n\n# Procesa el resultado únicamente después de completed.\nresult = report[\"result\"]\nfor measurement in result[\"measurements\"]:\n    print(measurement[\"metric_id\"], measurement[\"value\"], measurement[\"unit\"])\nfor index, suggestion in enumerate(result[\"suggestions\"]):\n    print(index, suggestion[\"metric_id\"], len(suggestion[\"occurrences\"]))","javascript":"// Node.js 18+. Ejecuta este código en servidor; nunca expongas la clave en el navegador.\nconst API_BASE = \"http://wuned.tail9cc3dd.ts.net\";\nconst API_KEY = process.env.ARTEXT_API_KEY;\nif (!API_KEY) throw new Error(\"ARTEXT_API_KEY is required\");\n\nconst headers = {\n  \"Authorization\": `Bearer ${API_KEY}`,\n  \"Content-Type\": \"application/json\"\n};\nconst api = async (path, options = {}) => {\n  const response = await fetch(new URL(path, `${API_BASE}/`), {\n    ...options,\n    headers: { ...headers, ...options.headers },\n    signal: AbortSignal.timeout(30_000)\n  });\n  if (!response.ok && response.status !== 429) {\n    throw new Error(`${response.status}: ${await response.text()}`);\n  }\n  return response;\n};\nconst delay = (milliseconds) =>\n  new Promise((resolve) => setTimeout(resolve, milliseconds));\n\n// Crea el informe una sola vez. Idempotency-Key hace seguro repetir el POST tras un fallo de red.\nconst createdResponse = await api(\"v1/reports\", {\n  method: \"POST\",\n  headers: { \"Idempotency-Key\": crypto.randomUUID() },\n  body: JSON.stringify({\"language\":\"es\",\"genre\":\"informe\",\"text\":\"En el día de hoy se realizará la revisión de la solicitud.\",\"external_id\":\"document-123\"})\n});\nconst created = await createdResponse.json();\nconst deadline = Date.now() + 180_000;\nlet retryAfter = Number(createdResponse.headers.get(\"Retry-After\") || 2);\nlet report;\n\n// Consulta el mismo informe respetando Retry-After y con un límite total.\nwhile (Date.now() < deadline) {\n  await delay(retryAfter * 1_000);\n  const statusResponse = await api(created.status_url);\n  retryAfter = Number(statusResponse.headers.get(\"Retry-After\") || 2);\n  if (statusResponse.status === 429) continue;\n  report = await statusResponse.json();\n  if (report.status === \"completed\") break;\n  if (report.status === \"failed\") {\n    throw new Error(report.error?.message || \"Analysis failed\");\n  }\n  if (![\"queued\", \"running\"].includes(report.status)) {\n    throw new Error(`Unknown report status: ${report.status}`);\n  }\n}\nif (report?.status !== \"completed\") {\n  throw new Error(\"Report polling timed out\");\n}\n\n// Procesa el resultado únicamente después de completed.\nfor (const item of report.result.measurements) {\n  console.log(item.metric_id, item.value, item.unit);\n}\nfor (const [index, item] of report.result.suggestions.entries()) {\n  console.log(index, item.metric_id, item.occurrences.length);\n}"},"sdk":{"python":"# Instala el cliente oficial: pip install artext-client\n# Ejecuta este código en servidor; nunca expongas la clave en el navegador.\nimport os\n\nfrom artext_client import ArtextClient\n\nclient = ArtextClient(\n    api_key=os.environ[\"ARTEXT_API_KEY\"],\n    base_url=\"http://wuned.tail9cc3dd.ts.net\",\n)\n\n# analyze crea el informe, espera respetando Retry-After y devuelve el resultado.\n# Un análisis fallido lanza una excepción con el detalle del error.\nreport = client.analyze(\"En el día de hoy se realizará la revisión de la solicitud.\", language=\"es\")\n\nprint(report.result.measurement(\"word-count\").value)\nfor suggestion in report.result.suggestions:\n    print(suggestion.metric_id, suggestion.summary)\n    for occurrence in suggestion.occurrences:\n        print(\"  \", occurrence.text, \"->\", occurrence.replacement)\n","javascript":"// Instala el cliente oficial: npm install artext-client\n// Ejecuta este código en servidor; nunca expongas la clave en el navegador.\nimport { ArtextClient } from \"artext-client\";\n\nconst client = new ArtextClient({\n  apiKey: process.env.ARTEXT_API_KEY,\n  baseUrl: \"http://wuned.tail9cc3dd.ts.net\",\n});\n\n// analyze crea el informe, espera respetando Retry-After y devuelve el resultado.\n// Un análisis fallido lanza una excepción con el detalle del error.\nconst report = await client.analyze({ text: \"En el día de hoy se realizará la revisión de la solicitud.\", language: \"es\" });\n\nfor (const suggestion of report.result.suggestions) {\n  console.log(suggestion.metric_id, suggestion.summary);\n  for (const occurrence of suggestion.occurrences) {\n    console.log(\"  \", occurrence.text, \"->\", occurrence.replacement);\n  }\n}\n"},"request":{"language":"es","genre":"informe","text":"En el día de hoy se realizará la revisión de la solicitud.","external_id":"document-123"},"responses":{"queued":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"queued","status_url":"/v1/reports/rep_7f12a4c8","events_url":"/v1/reports/rep_7f12a4c8/events"},"completed":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"completed","position_in_queue":null,"result":{"schema_version":"1.0","offset_unit":"unicode_code_points","language":"es","genre":{"id":"informe","title":"Informe"},"measurements":[{"metric_id":"word-count","value":12,"unit":"words","implementation_version":"1.0"}],"suggestions":[{"id":"sug_1","metric_id":"redundant-expressions","implementation_version":"1.0","metric_title":"Eliminación de expresiones redundantes","category":"lexical","severity":"warning","summary":"Conviene formular el fragmento de manera más directa.","explanation":null,"occurrences":[{"id":"occ_1","text":"En el día de hoy","paragraph_index":0,"sentence_index":0,"start":0,"end":16,"replacement":null,"alternatives":[],"edits":[{"start":0,"end":16,"text":""}]}],"recommendations":["Elimina la expresión si el contexto lo permite."]}]},"error":null},"failed":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"failed","position_in_queue":null,"result":null,"error":{"code":"analysis_failed","message":"The analysis could not be completed."}}}}}