{"openapi":"3.1.0","info":{"title":"arText Reports API","description":"Submit text, retrieve the asynchronous report and use structured linguistic suggestions in your own products.","version":"1.0.0"},"paths":{"/v1/reports":{"post":{"tags":["Reports"],"summary":"Create an analysis report","description":"Validates and queues text. The 202 response identifies status and event resources; it does not mean analysis is complete.","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":"en","domain_slug":"plain-language","text_type_slug":"legal-administrative-text-addressed-to-citizens","text":"The application will be reviewed at the present time.","external_id":"document-123"}}}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportAccepted"},"examples":{"queued":{"summary":"Report accepted and queued","value":{"report_id":"rep_7f12a4c8","external_id":"document-123","status":"queued","status_url":"/v1/reports/rep_7f12a4c8","events_url":"/v1/reports/rep_7f12a4c8/events"}}}}},"headers":{"Location":{"description":"Canonical status resource for the accepted report.","schema":{"type":"string","example":"/v1/reports/rep_7f12a4c8"}},"Retry-After":{"description":"Minimum number of seconds before the first status poll.","schema":{"type":"integer","minimum":1,"example":2}},"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"409":{"description":"Idempotency-Key was reused with a different payload.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"413":{"description":"Text exceeds the configured word limit.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"422":{"description":"Request validation failed, including an unknown domain_slug/text_type_slug pair.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"503":{"description":"The analysis queue is unavailable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}}},"security":[{"ApiKey":[]}]}},"/v1/reports/{report_id}":{"get":{"tags":["Reports"],"summary":"Retrieve status and results","description":"Returns current state. Honor Retry-After while queued/running; completed and failed are terminal.","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":"en","genre":{"domain":{"slug":"plain-language","name":"Plain language"},"text_type":{"slug":"legal-administrative-text-addressed-to-citizens","name":"Legal-administrative text addressed to citizens"}},"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":"Removal of redundant expressions","category":"lexical","severity":"warning","summary":"Consider expressing this fragment more directly.","explanation":null,"occurrences":[{"id":"occ_1","text":"at the present time","paragraph_index":0,"sentence_index":0,"start":0,"end":19,"replacement":null,"alternatives":[],"edits":[{"start":0,"end":19,"text":""}]}],"recommendations":["Remove the expression when the context allows it."]}]},"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":"Stream transitions over SSE","description":"Optional SSE channel for the initial state, changes and heartbeats. It closes on a terminal state; polling remains the supported fallback.","operationId":"stream_public_report_events_v1_reports__report_id__events_get","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"},"description":"Unique report identifier returned by POST /v1/reports."}],"responses":{"200":{"description":"SSE status stream.","content":{"text/event-stream":{"schema":{"type":"string"},"examples":{"transitions":{"summary":"A stream closes after completed or failed","value":"event: status\\ndata: {\"report_id\":\"rep_7f12a4c8\",\"status\":\"running\",\"updated_at\":\"2026-07-15T10:00:02Z\"}\\n\\nevent: status\\ndata: {\"report_id\":\"rep_7f12a4c8\",\"status\":\"completed\",\"updated_at\":\"2026-07-15T10:00:06Z\"}\\n\\n"}}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Unauthorized","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Not Found","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"429":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Too Many Requests","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"description":"Service Unavailable","headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}},"Retry-After":{"description":"Seconds to wait before a retry can be attempted.","schema":{"type":"integer","minimum":1,"example":2}}}},"422":{"description":"Validation Error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/metrics":{"get":{"tags":["Metrics"],"summary":"List metric implementations","description":"Returns measurement and suggestion implementations, including inactive variants and localized documentation. Narrowing the request to a genre reports which metrics that genre actually evaluates.","operationId":"list_metrics_v1_metrics_get","parameters":[{"name":"analysis_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Analysis Language"},"description":"Language implementation to return: es, ca or en."},{"name":"documentation_language","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Documentation Language"},"description":"Language used for titles and documentation: es, ca or en."},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(measurement|suggestion)$"},{"type":"null"}],"title":"Kind"},"description":"Filter by measurement or suggestion."},{"name":"enabled","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"description":"Filter by the current implementation status."},{"name":"domain_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional genre filter. domain_slug and text_type_slug go together and must name a genre listed by GET /v1/text-types; each metric then reports whether that genre evaluates it and with which thresholds.","title":"Domain Slug"},"description":"Text domain of a genre, as listed by GET /v1/text-types."},{"name":"text_type_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional genre filter. domain_slug and text_type_slug go together and must name a genre listed by GET /v1/text-types; each metric then reports whether that genre evaluates it and with which thresholds.","title":"Text Type Slug"},"description":"Text type (genre) inside domain_slug, as listed by GET /v1/text-types."}],"responses":{"200":{"description":"Metric implementations and their documentation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetricCatalog"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"422":{"description":"Incomplete or unknown genre filter.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/metrics/{metric_id}":{"get":{"tags":["Metrics"],"summary":"Get one metric implementation","description":"Returns the status, category and localized documentation for one language variant, optionally narrowed to a genre.","operationId":"get_metric_v1_metrics__metric_id__get","parameters":[{"name":"metric_id","in":"path","required":true,"schema":{"type":"string","title":"Metric Id"},"description":"Stable conceptual metric identifier."},{"name":"analysis_language","in":"query","required":true,"schema":{"type":"string","title":"Analysis Language"},"description":"Language implementation to return: es, ca or en."},{"name":"documentation_language","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Documentation Language"},"description":"Language used for titles and documentation: es, ca or en."},{"name":"domain_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional genre filter. domain_slug and text_type_slug go together and must name a genre listed by GET /v1/text-types; each metric then reports whether that genre evaluates it and with which thresholds.","title":"Domain Slug"},"description":"Text domain of a genre, as listed by GET /v1/text-types."},{"name":"text_type_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional genre filter. domain_slug and text_type_slug go together and must name a genre listed by GET /v1/text-types; each metric then reports whether that genre evaluates it and with which thresholds.","title":"Text Type Slug"},"description":"Text type (genre) inside domain_slug, as listed by GET /v1/text-types."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Metric"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"404":{"description":"Metric implementation not found.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"422":{"description":"Incomplete or unknown genre filter.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/text-types":{"get":{"tags":["Text types"],"summary":"List text domains and genres","description":"Returns the domains (ámbitos) and the genres (text types) available for a language. The slug pair of a genre is what POST /v1/reports expects.","operationId":"list_public_text_types_v1_text_types_get","parameters":[{"name":"language","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Language"},"description":"Catalog language: es, ca or en."}],"responses":{"200":{"description":"Editorial catalog for the requested language.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTextTypeCatalog"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"422":{"description":"Unsupported language.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}},"/v1/text-types/{domain_slug}/{text_type_slug}":{"get":{"tags":["Text types"],"summary":"Get one genre","description":"Returns the sections and contents recommended for a genre, plus the exact report_defaults to forward to POST /v1/reports.","operationId":"get_public_text_type_v1_text_types__domain_slug___text_type_slug__get","parameters":[{"name":"domain_slug","in":"path","required":true,"schema":{"type":"string","title":"Domain Slug"},"description":"Text domain of a genre, as listed by GET /v1/text-types."},{"name":"text_type_slug","in":"path","required":true,"schema":{"type":"string","title":"Text Type Slug"},"description":"Text type (genre) inside domain_slug, as listed by GET /v1/text-types."},{"name":"language","in":"query","required":false,"schema":{"type":"string","default":"es","title":"Language"},"description":"Catalog language: es, ca or en."}],"responses":{"200":{"description":"Genre detail and its report defaults.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTextTypeDetail"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"401":{"description":"Missing, invalid, inactive or revoked API key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"404":{"description":"Genre not found for this language.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}},"422":{"description":"Unsupported language.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ApiProblem"}}},"headers":{"X-Request-ID":{"description":"Identifier to retain for support and tracing.","schema":{"type":"string","example":"req_4adf8f2c"}}}}},"security":[{"ApiKey":[]}]}}},"components":{"schemas":{"ApiProblem":{"properties":{"type":{"type":"string","title":"Type","description":"URI reference identifying the problem type."},"title":{"type":"string","title":"Title","description":"Short, stable problem title."},"status":{"type":"integer","title":"Status","description":"HTTP status code."},"detail":{"type":"string","title":"Detail","description":"Human-readable explanation for this occurrence."},"instance":{"type":"string","title":"Instance","description":"Request path for this occurrence."},"code":{"type":"string","title":"Code","description":"Stable machine-readable arText error code."},"request_id":{"type":"string","title":"Request Id","description":"Identifier to retain for support and tracing."},"retry_after_seconds":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Retry After Seconds"},"errors":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Errors"}},"type":"object","required":["type","title","status","detail","instance","code","request_id"],"title":"ApiProblem"},"Genre":{"properties":{"domain":{"$ref":"#/components/schemas/TextDomain"},"text_type":{"$ref":"#/components/schemas/TextType"}},"type":"object","required":["domain","text_type"],"title":"Genre","description":"Genre the report was analyzed with.\n\nIt names both coordinates because a text type slug is only unique inside\nits domain, and the pair is what determined the rules, the thresholds and\nthe glossaries used. Resubmitting these two slugs reproduces the analysis."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Measurement":{"properties":{"metric_id":{"type":"string","title":"Metric Id","description":"Stable metric identifier documented by GET /v1/metrics/{metric_id}."},"value":{"anyOf":[{"type":"integer"},{"type":"number"}],"title":"Value","description":"Calculated numeric value."},"unit":{"type":"string","title":"Unit","description":"Semantic unit defined by the metric catalog."},"implementation_version":{"type":"string","title":"Implementation Version","description":"Calculator version used for this report."}},"type":"object","required":["metric_id","value","unit","implementation_version"],"title":"Measurement"},"Metric":{"properties":{"id":{"type":"string","title":"Id"},"kind":{"type":"string","enum":["measurement","suggestion"],"title":"Kind"},"analysis_language":{"type":"string","title":"Analysis Language"},"enabled":{"type":"boolean","title":"Enabled","description":"Whether reports for analysis_language currently evaluate this variant."},"genre_scope":{"anyOf":[{"$ref":"#/components/schemas/MetricGenreScope"},{"type":"null"}],"description":"Genre-specific status, present only when the request selects a genre."},"available":{"type":"boolean","title":"Available","description":"False when the variant can never produce output for analysis_language with the current linguistic runtime; see availability_note.","default":true},"availability_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Availability Note","description":"Localized explanation present only when available is false."},"implementation_version":{"type":"string","title":"Implementation Version"},"category":{"type":"string","title":"Category"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit"},"documentation_language":{"type":"string","title":"Documentation Language","description":"Language actually used by the returned documentation."},"documentation":{"$ref":"#/components/schemas/MetricDocumentation"},"references":{"items":{"type":"string"},"type":"array","title":"References"},"example":{"additionalProperties":true,"type":"object","title":"Example","description":"Example conforming to the public report contract."}},"type":"object","required":["id","kind","analysis_language","enabled","implementation_version","category","documentation_language","documentation"],"title":"Metric"},"MetricCatalog":{"properties":{"genre":{"anyOf":[{"$ref":"#/components/schemas/Genre"},{"type":"null"}],"description":"Genre the genre_scope of every metric refers to, when one was requested."},"metrics":{"items":{"$ref":"#/components/schemas/Metric"},"type":"array","title":"Metrics"}},"type":"object","required":["metrics"],"title":"MetricCatalog"},"MetricDocumentation":{"properties":{"title":{"type":"string","title":"Title","default":""},"summary":{"type":"string","title":"Summary","default":""},"methodology":{"type":"string","title":"Methodology","default":""},"limitations":{"type":"string","title":"Limitations","default":""},"interpretation":{"type":"string","title":"Interpretation","default":""}},"type":"object","title":"MetricDocumentation"},"MetricGenreScope":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Whether reports for this genre evaluate the metric."},"params":{"additionalProperties":true,"type":"object","title":"Params","description":"Thresholds this genre configures for the metric; empty means the defaults."}},"type":"object","required":["enabled"],"title":"MetricGenreScope","description":"How a metric behaves for one genre.\n\nPresent only when the request narrows the catalog with domain_slug and\ntext_type_slug. `enabled` is the intersection of the language variant and\nthe genre configuration: a variant enabled for the language still does not\nrun unless the genre opts into it."},"Occurrence":{"properties":{"id":{"type":"string","title":"Id","description":"Identifier unique within its parent suggestion."},"text":{"type":"string","title":"Text","description":"Fragment identified by the metric."},"paragraph_index":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Paragraph Index","description":"Zero-based paragraph index when available."},"sentence_index":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Sentence Index","description":"Zero-based sentence index when available."},"start":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Start","description":"Inclusive offset in the original input text."},"end":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"End","description":"Exclusive offset in the original input text."},"replacement":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Replacement","description":"Preferred direct replacement, or null when none is available."},"alternatives":{"items":{"type":"string"},"type":"array","title":"Alternatives","description":"Additional reviewed replacement candidates, excluding replacement."},"edits":{"items":{"$ref":"#/components/schemas/TextEdit"},"type":"array","title":"Edits","description":"Validated edits for the preferred deterministic change, ordered from the highest offset to the lowest."}},"type":"object","required":["id","text"],"title":"Occurrence"},"PublicPhraseology":{"properties":{"value":{"type":"string","title":"Value","description":"Suggested wording for the content it belongs to."}},"type":"object","required":["value"],"title":"PublicPhraseology"},"PublicTextContent":{"properties":{"name":{"type":"string","title":"Name","description":"Content the section is expected to cover."},"occurrence":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Occurrence","description":"How many times the content usually appears in the section, when the catalog states it."},"phraseology":{"items":{"$ref":"#/components/schemas/PublicPhraseology"},"type":"array","title":"Phraseology","description":"Wordings the editorial team proposes for this content."}},"type":"object","required":["name"],"title":"PublicTextContent"},"PublicTextDomain":{"properties":{"slug":{"type":"string","title":"Slug","description":"Domain identifier. Send it as domain_slug in POST /v1/reports."},"name":{"type":"string","title":"Name","description":"Domain name in the requested language."},"clear_language":{"type":"boolean","title":"Clear Language","description":"True when the whole domain is plain-language.","default":false},"text_types":{"items":{"$ref":"#/components/schemas/PublicTextType"},"type":"array","title":"Text Types","description":"Genres available inside this domain."}},"type":"object","required":["slug","name"],"title":"PublicTextDomain"},"PublicTextSection":{"properties":{"name":{"type":"string","title":"Name","description":"Section of the document, in reading order."},"contents":{"items":{"$ref":"#/components/schemas/PublicTextContent"},"type":"array","title":"Contents"}},"type":"object","required":["name"],"title":"PublicTextSection"},"PublicTextType":{"properties":{"slug":{"type":"string","title":"Slug","description":"Genre identifier. Send it as text_type_slug in POST /v1/reports."},"name":{"type":"string","title":"Name","description":"Genre name in the requested language."},"clear_language":{"type":"boolean","title":"Clear Language","description":"True when the genre is written in plain language for the general public.","default":false}},"type":"object","required":["slug","name"],"title":"PublicTextType"},"PublicTextTypeCatalog":{"properties":{"language":{"type":"string","title":"Language","description":"Language the catalog is returned in."},"domains":{"items":{"$ref":"#/components/schemas/PublicTextDomain"},"type":"array","title":"Domains"}},"type":"object","required":["language","domains"],"title":"PublicTextTypeCatalog"},"PublicTextTypeDetail":{"properties":{"language":{"type":"string","title":"Language"},"domain":{"$ref":"#/components/schemas/PublicTextDomain","description":"Domain the genre belongs to, without its sibling genres."},"text_type":{"$ref":"#/components/schemas/PublicTextType"},"structure":{"items":{"$ref":"#/components/schemas/PublicTextSection"},"type":"array","title":"Structure","description":"Sections and contents the editorial team recommends for this genre."},"report_defaults":{"$ref":"#/components/schemas/ReportDefaults","description":"Exact payload fragment to forward to POST /v1/reports."}},"type":"object","required":["language","domain","text_type","report_defaults"],"title":"PublicTextTypeDetail"},"Report":{"properties":{"report_id":{"type":"string","title":"Report Id"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id"},"status":{"$ref":"#/components/schemas/ReportStatus"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"UTC acceptance timestamp."},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"UTC last-transition timestamp."},"started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Started At","description":"UTC processing start timestamp."},"finished_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Finished At","description":"UTC terminal timestamp."},"position_in_queue":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position In Queue","description":"Approximate queue position while waiting."},"phase":{"anyOf":[{"$ref":"#/components/schemas/ReportPhase"},{"type":"null"}],"description":"Pipeline stage while the report is running: `analyzing` (linguistic analysis, by far the longest part) or `building` (applying rules). Not a percentage: it marks what the server is doing, not how far along it is."},"estimated_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Seconds","description":"Rough estimate of the analysis duration, derived from the text length when the report was accepted. Indicative only: it excludes queue wait and does not react to server load."},"result":{"anyOf":[{"$ref":"#/components/schemas/ReportResult"},{"type":"null"}],"description":"Analysis result, present only when completed."},"error":{"anyOf":[{"$ref":"#/components/schemas/ReportFailure"},{"type":"null"}],"description":"Safe failure information, present only when failed."}},"type":"object","required":["report_id","status"],"title":"Report"},"ReportAccepted":{"properties":{"report_id":{"type":"string","title":"Report Id","description":"Unique arText report identifier."},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Caller-provided correlation identifier."},"status":{"$ref":"#/components/schemas/ReportStatus","description":"Initial queue status; always queued for a newly accepted report."},"status_url":{"type":"string","title":"Status Url","description":"Relative URL of the report status resource."},"events_url":{"type":"string","title":"Events Url","description":"Relative URL of the optional SSE status stream."},"estimated_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Seconds","description":"Rough estimate of how long the analysis will take, derived from the length of the submitted text. Indicative only: it excludes queue wait and does not react to server load."}},"type":"object","required":["report_id","status","status_url","events_url"],"title":"ReportAccepted"},"ReportDefaults":{"properties":{"language":{"type":"string","title":"Language","description":"Analysis language the slugs below belong to."},"domain_slug":{"type":"string","title":"Domain Slug","description":"Value for the domain_slug field of a report request."},"text_type_slug":{"type":"string","title":"Text Type Slug","description":"Value for the text_type_slug field of a report request."}},"type":"object","required":["language","domain_slug","text_type_slug"],"title":"ReportDefaults","description":"Exact fields to forward to POST /v1/reports for this genre."},"ReportFailure":{"properties":{"code":{"type":"string","const":"analysis_failed","title":"Code","default":"analysis_failed"},"message":{"type":"string","title":"Message","description":"Safe failure description suitable for API clients."}},"type":"object","required":["message"],"title":"ReportFailure"},"ReportPhase":{"type":"string","enum":["analyzing","building"],"title":"ReportPhase","description":"Punto de la tubería mientras el estado es `running`.\n\nNo es un porcentaje y no debe presentarse como tal: `analyzing` se lleva el\n94 % del tiempo (una única llamada a FreeLing, que no informa de su avance),\nasí que una barra basada en esto se quedaría clavada casi todo el rato. Sirve\npara decir qué está pasando y que sigue vivo; para «cuánto queda» está\n`estimated_seconds`."},"ReportRequest":{"properties":{"language":{"type":"string","title":"Language","description":"Language used for analysis: es, ca or en.","default":"es"},"domain_slug":{"type":"string","minLength":1,"title":"Domain Slug","description":"Text domain (ámbito) the genre belongs to, as listed by GET /v1/text-types.","examples":["lenguaje-claro"]},"text_type_slug":{"type":"string","minLength":1,"title":"Text Type Slug","description":"Text type (genre) inside domain_slug, as listed by GET /v1/text-types. The pair selects the rules, thresholds and glossaries applied to the text.","examples":["texto-juridico-administrativo-dirigido-a-la-ciudadania"]},"text":{"type":"string","minLength":1,"title":"Text","description":"Plain text to analyze."},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"Optional caller-owned correlation identifier returned unchanged."}},"additionalProperties":false,"type":"object","required":["domain_slug","text_type_slug","text"],"title":"ReportRequest","description":"Stable input accepted from API integrations."},"ReportResult":{"properties":{"schema_version":{"type":"string","const":"1.0","title":"Schema Version","description":"Version of this public result contract.","default":"1.0"},"offset_unit":{"type":"string","const":"unicode_code_points","title":"Offset Unit","description":"Unit used by every start and end offset in the result.","default":"unicode_code_points"},"language":{"type":"string","title":"Language","description":"Language actually used for analysis."},"genre":{"$ref":"#/components/schemas/Genre"},"measurements":{"items":{"$ref":"#/components/schemas/Measurement"},"type":"array","title":"Measurements"},"suggestions":{"items":{"$ref":"#/components/schemas/Suggestion"},"type":"array","title":"Suggestions"}},"type":"object","required":["language","genre"],"title":"ReportResult"},"ReportStatus":{"type":"string","enum":["queued","running","completed","failed"],"title":"ReportStatus"},"Suggestion":{"properties":{"id":{"type":"string","title":"Id","description":"Identifier unique within this report."},"metric_id":{"type":"string","title":"Metric Id","description":"Stable metric identifier documented by GET /v1/metrics/{metric_id}."},"implementation_version":{"type":"string","title":"Implementation Version","description":"Detector version used for this report."},"metric_title":{"type":"string","title":"Metric Title","description":"Localized human-readable metric title captured for this report."},"category":{"type":"string","title":"Category","description":"Stable linguistic category used by the metric catalog."},"severity":{"type":"string","title":"Severity","description":"Review priority; it is not a confidence score."},"summary":{"type":"string","title":"Summary","description":"Explanation of the issue found in this report."},"explanation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Explanation","description":"Optional extended contextual explanation."},"recommendations":{"items":{"type":"string"},"type":"array","title":"Recommendations","description":"Human-readable review guidance."},"examples":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Examples","description":"Optional worked examples illustrating the recommendation, as plain text with meaningful line breaks. Editorial copy attached to the metric: it never quotes the analysed text and does not vary between reports. Null when the metric's copy has no examples."},"occurrences":{"items":{"$ref":"#/components/schemas/Occurrence"},"type":"array","title":"Occurrences","description":"Concrete appearances of this issue in the submitted text."}},"type":"object","required":["id","metric_id","implementation_version","metric_title","category","severity","summary"],"title":"Suggestion"},"TextDomain":{"properties":{"slug":{"type":"string","title":"Slug","description":"Stable text domain identifier accepted by POST /v1/reports."},"name":{"type":"string","title":"Name","description":"Localized text domain name."}},"type":"object","required":["slug","name"],"title":"TextDomain"},"TextEdit":{"properties":{"start":{"type":"integer","minimum":0.0,"title":"Start","description":"Inclusive offset in the original input text."},"end":{"type":"integer","minimum":0.0,"title":"End","description":"Exclusive offset in the original input text."},"text":{"type":"string","title":"Text","description":"Text to splice into the range. Empty text deletes it; start == end inserts text."}},"type":"object","required":["start","end","text"],"title":"TextEdit"},"TextType":{"properties":{"slug":{"type":"string","title":"Slug","description":"Stable text type identifier accepted by POST /v1/reports."},"name":{"type":"string","title":"Name","description":"Localized text type name."}},"type":"object","required":["slug","name"],"title":"TextType"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","bearerFormat":"art_live_…"}}},"servers":[{"url":"http://uned.tail9cc3dd.ts.net"}],"x-artext-metrics":[{"id":"average-sentence-words","kind":"measurement","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"readability","unit":"words","documentation_language":"en","documentation":{"title":"Average sentence words","summary":"Average number of words per sentence.","methodology":"Divides word-count by sentence-count and rounds to two decimals; returns 0 when no sentences exist.","limitations":"The value depends on analyzer normalization and segmentation; it should be interpreted together with the other metrics and the text genre.","interpretation":"`value` contains the numeric result and `unit` is `words`. Absence means the variant was disabled."},"references":[],"example":{"metric_id":"average-sentence-words","value":24,"unit":"words","implementation_version":"1.0"}},{"id":"maximum-paragraph-words","kind":"measurement","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"readability","unit":"words","documentation_language":"en","documentation":{"title":"Maximum paragraph words","summary":"Word length of the longest paragraph.","methodology":"Takes the maximum paragraph word_count; returns 0 when no paragraph exists.","limitations":"The value depends on analyzer normalization and segmentation; it should be interpreted together with the other metrics and the text genre.","interpretation":"`value` contains the numeric result and `unit` is `words`. Absence means the variant was disabled."},"references":[],"example":{"metric_id":"maximum-paragraph-words","value":24,"unit":"words","implementation_version":"1.0"}},{"id":"maximum-sentence-words","kind":"measurement","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"readability","unit":"words","documentation_language":"en","documentation":{"title":"Maximum sentence words","summary":"Word length of the longest sentence.","methodology":"Takes the maximum sentence word_count; returns 0 when no sentence exists.","limitations":"The value depends on analyzer normalization and segmentation; it should be interpreted together with the other metrics and the text genre.","interpretation":"`value` contains the numeric result and `unit` is `words`. Absence means the variant was disabled."},"references":[],"example":{"metric_id":"maximum-sentence-words","value":24,"unit":"words","implementation_version":"1.0"}},{"id":"paragraph-count","kind":"measurement","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"en","documentation":{"title":"Paragraph count","summary":"Number of non-empty paragraphs detected.","methodology":"Segments paragraph text and counts units containing analyzable content.","limitations":"The value depends on analyzer normalization and segmentation; it should be interpreted together with the other metrics and the text genre.","interpretation":"`value` contains the numeric result and `unit` is `count`. Absence means the variant was disabled."},"references":[],"example":{"metric_id":"paragraph-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"segment-count","kind":"measurement","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"en","documentation":{"title":"Segment count","summary":"Number of recognized intra-sentence segments.","methodology":"Sums the syntactic/discourse segments produced by DiSeg for all sentences.","limitations":"The value depends on analyzer normalization and segmentation; it should be interpreted together with the other metrics and the text genre.","interpretation":"`value` contains the numeric result and `unit` is `count`. Absence means the variant was disabled."},"references":[],"example":{"metric_id":"segment-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"sentence-count","kind":"measurement","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"en","documentation":{"title":"Sentence count","summary":"Number of sentences segmented by the analyzer.","methodology":"Counts sentences recognized by FreeLing/DiSeg, rather than simply counting full stops.","limitations":"The value depends on analyzer normalization and segmentation; it should be interpreted together with the other metrics and the text genre.","interpretation":"`value` contains the numeric result and `unit` is `count`. Absence means the variant was disabled."},"references":[],"example":{"metric_id":"sentence-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"token-count","kind":"measurement","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"en","documentation":{"title":"Token count","summary":"Number of lexical units produced by the analyzer.","methodology":"Counts FreeLing/DiSeg tokens; punctuation and contractions may make this differ from word-count.","limitations":"The value depends on analyzer normalization and segmentation; it should be interpreted together with the other metrics and the text genre.","interpretation":"`value` contains the numeric result and `unit` is `count`. Absence means the variant was disabled."},"references":[],"example":{"metric_id":"token-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"word-count","kind":"measurement","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.0","category":"document","unit":"count","documentation_language":"en","documentation":{"title":"Word count","summary":"Number of words in the normalized text.","methodology":"Normalizes whitespace and counts every non-empty space-separated item.","limitations":"The value depends on analyzer normalization and segmentation; it should be interpreted together with the other metrics and the text genre.","interpretation":"`value` contains the numeric result and `unit` is `count`. Absence means the variant was disabled."},"references":[],"example":{"metric_id":"word-count","value":24,"unit":"count","implementation_version":"1.0"}},{"id":"acronym-consistency","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Use acronyms consistently","summary":"Detects repeated use of an expanded term after its acronym has already been introduced.","methodology":"After identifying an acronym and expansion at first occurrence, checks whether the full expanded term appears again later.","limitations":"This is a review signal, not an automatic correction or a complete assessment of text quality.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"acronym-consistency","implementation_version":"1.2","metric_title":"Use acronyms consistently","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"acronym-introduction","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Introduce acronyms","summary":"Detects the first occurrence of uppercase acronyms that do not appear with an expanded form.","methodology":"Extracts uppercase candidates, ignores stopwords, keeps the first occurrence and searches immediately before or after it for an expanded form.","limitations":"This is a review signal, not an automatic correction or a complete assessment of text quality.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"acronym-introduction","implementation_version":"1.2","metric_title":"Introduce acronyms","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"archaic-verb-forms","kind":"suggestion","analysis_language":"en","enabled":false,"genre_scope":null,"available":false,"availability_note":"Not applicable to this language: the linguistic phenomenon it detects does not exist in its grammar.","implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"en","documentation":{"title":"Remove archaic verb forms","summary":"Detects forms tagged as future subjunctive, generally considered archaic in current usage.","methodology":"Looks for FreeLing tags starting with VSSF, VASF or VMSF, including vaux-cat, while excluding auxiliaries.","limitations":"Results depend on FreeLing/DiSeg tagging and may contain false positives or miss ambiguous constructions.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"archaic-verb-forms","implementation_version":"1.2","metric_title":"Remove archaic verb forms","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"connector-variety","kind":"suggestion","analysis_language":"en","enabled":false,"genre_scope":null,"available":false,"availability_note":"Not available for this language: detection relies on discourse markers (disc_mk) that only the Spanish DiSeg grammar produces.","implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"en","documentation":{"title":"Vary connectors","summary":"Detects discourse connectors repeated above the threshold and offers alternatives expressing the same relation.","methodology":"Counts disc_mk XML nodes, normalizes their forms and flags model connectors whose frequency exceeds n_variacion_conectores. Detection uses the active editorial catalog for the language.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"connector-variety","implementation_version":"1.2","metric_title":"Vary connectors","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"definition-usage","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Use of definitions","summary":"Compares the number of definition markers in the text with the genre's reference value.","methodology":"Counts the text's definition markers and compares them with the genre's reference value; a mode parameter decides whether the notice is raised for too many or too few. Detection uses the active editorial catalog for the language.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"definition-usage","implementation_version":"1.2","metric_title":"Use of definitions","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"difficult-expressions","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Replace difficult expressions","summary":"Finds difficult-to-understand expressions in the catalog and returns alternatives when available.","methodology":"Matches complete expressions directly against the text, case- and accent-insensitively, while respecting word boundaries. Detection uses the active editorial catalog for the language.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"difficult-expressions","implementation_version":"1.2","metric_title":"Replace difficult expressions","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"gerunds","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"en","documentation":{"title":"Review gerunds","summary":"Finds standalone gerund forms worth reviewing, excluding recognized auxiliaries and compound constructions.","methodology":"Uses gerund verb tags (VBG in EN) and auxiliary attributes to exclude already recognized periphrastic forms.","limitations":"Results depend on FreeLing/DiSeg tagging and may contain false positives or miss ambiguous constructions.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"gerunds","implementation_version":"1.2","metric_title":"Review gerunds","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"imprecise-words","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Replace ambiguous words","summary":"Finds imprecise words or sequences listed in the editable catalog and returns their alternatives.","methodology":"Normalizes token lemmas and matches complete sequences from the active catalog; returns the term, alternative and text offsets when available. Detection uses the active editorial catalog for the language.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"imprecise-words","implementation_version":"1.2","metric_title":"Replace ambiguous words","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"list-usage","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"en","documentation":{"title":"Include lists","summary":"Warns, with a single document-level occurrence, when no structured list has been recognized in the text.","methodology":"Returns a single document-level suggestion, with no text offsets, when has_list is false; has_list is derived from text and structured HTML.","limitations":"Results depend on plain-text segmentation and recognizable structural markers; the signal alone does not imply a writing error.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"list-usage","implementation_version":"1.2","metric_title":"Include lists","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-paragraphs","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"en","documentation":{"title":"Review long paragraphs","summary":"Flags paragraphs exceeding the word limit configured for the text genre.","methodology":"Compares each paragraph word_count with the genre's n_palabras_por_parrafo value; only strictly greater values are reported.","limitations":"Results depend on plain-text segmentation and recognizable structural markers; the signal alone does not imply a writing error.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"long-paragraphs","implementation_version":"1.2","metric_title":"Review long paragraphs","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-segmented-sentences","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"en","documentation":{"title":"Split long sentences","summary":"Flags long sentences with several segments and exposes structural points where they might be split.","methodology":"Selects sentences longer than n_division_oraciones words with multiple segments; returns segments, connectors and possible split boundaries.","limitations":"Results depend on plain-text segmentation and recognizable structural markers; the signal alone does not imply a writing error.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"long-segmented-sentences","implementation_version":"1.2","metric_title":"Split long sentences","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-sentences","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"en","documentation":{"title":"Review long sentences","summary":"Flags sentences exceeding the word limit configured for the text genre and, when the sentence can be divided, the points where it might be split.","methodology":"Selects sentences longer than n_division_oraciones words; for those made of several segments it also returns segments, connectors and possible split boundaries.","limitations":"Results depend on plain-text segmentation and recognizable structural markers; the signal alone does not imply a writing error.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"long-sentences","implementation_version":"1.2","metric_title":"Review long sentences","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-unsegmented-sentences","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"en","documentation":{"title":"Review long sentences","summary":"Flags long sentences that DiSeg has not divided into internal segments.","methodology":"Selects sentences longer than n_division_oraciones words and containing at most one DiSeg segment.","limitations":"Results depend on plain-text segmentation and recognizable structural markers; the signal alone does not imply a writing error.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"long-unsegmented-sentences","implementation_version":"1.2","metric_title":"Review long sentences","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"long-words","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Review long words","summary":"Finds long words in the editorial catalog and proposes shorter alternatives when available.","methodology":"Normalizes token lemmas and matches complete sequences from the active catalog; returns the term, alternative and text offsets when available. Detection uses the active editorial catalog for the language.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"long-words","implementation_version":"1.2","metric_title":"Review long words","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"missing-definitions","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Lack of definitions","summary":"Warns when the text contains no definition marker although the genre usually includes definitions.","methodology":"Looks for definition markers (a defining verb next to a context word) and returns a document-level notice when the text contains none. Detection uses the active editorial catalog for the language.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"missing-definitions","implementation_version":"1.2","metric_title":"Lack of definitions","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"negative-formulations","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"en","documentation":{"title":"Rephrase negative ideas","summary":"Finds catalogued negative words or sequences to encourage direct affirmative wording.","methodology":"Normalizes token lemmas and matches complete sequences from the active catalog; returns the term, alternative and text offsets when available. Detection uses the active editorial catalog for the language.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"negative-formulations","implementation_version":"1.2","metric_title":"Rephrase negative ideas","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"paragraph-initial-connectors","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"en","documentation":{"title":"Introduce connectors at the beginning of paragraphs","summary":"Detects paragraphs that do not begin with a recognized discourse connector.","methodology":"Skips empty paragraphs and those with has_initial_connector; returns the remaining paragraphs plus editable connector groups. Detection uses the active editorial catalog for the language.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"paragraph-initial-connectors","implementation_version":"1.2","metric_title":"Introduce connectors at the beginning of paragraphs","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"participles","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"en","documentation":{"title":"Review participles","summary":"Finds standalone participles while excluding recognized compound tenses and passive constructions.","methodology":"Uses participle tags (VBN in EN) and excludes auxiliaries, compound tenses, passive forms and auxiliary lemmas.","limitations":"Results depend on FreeLing/DiSeg tagging and may contain false positives or miss ambiguous constructions.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"participles","implementation_version":"1.2","metric_title":"Review participles","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"passive-voice","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"en","documentation":{"title":"Use of passive voice","summary":"Finds constructions identified by morphosyntactic analysis as possible passive voice.","methodology":"For ES/CA it uses verb_pass or ger_pas nodes, then ser/ésser + participle as fallback. For EN it looks for be + VBN in vb_chunk and then tokens.","limitations":"Results depend on FreeLing/DiSeg tagging and may contain false positives or miss ambiguous constructions.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"passive-voice","implementation_version":"1.2","metric_title":"Use of passive voice","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"redundant-expressions","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Remove redundant expressions","summary":"Finds catalogued redundant expressions so they can be removed or rephrased.","methodology":"Matches complete expressions directly against the text, case- and accent-insensitively, while respecting word boundaries. Detection uses the active editorial catalog for the language.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"redundant-expressions","implementation_version":"1.2","metric_title":"Remove redundant expressions","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"single-sentence-paragraphs","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"discourse","unit":null,"documentation_language":"en","documentation":{"title":"Review sentence-paragraphs","summary":"Finds paragraphs made up of a single sentence so they can be reviewed for excessive density or insufficient development.","methodology":"Selects every non-empty paragraph whose sentence_count is exactly 1.","limitations":"Results depend on plain-text segmentation and recognizable structural markers; the signal alone does not imply a writing error.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"single-sentence-paragraphs","implementation_version":"1.2","metric_title":"Review sentence-paragraphs","category":"discourse","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"subjectivity-markers","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Use of subjectivity markers","summary":"Combines lexical items, sequences, morphological categories and exclamations to find possible subjectivity markers.","methodology":"Combines simple lemma matches, compound sequences, configured morphological categories and exclamation spans, then removes duplicates.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"subjectivity-markers","implementation_version":"1.2","metric_title":"Use of subjectivity markers","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"transparent-terms","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Use more transparent terms","summary":"Detects catalogued difficult terms and proposes more transparent wording.","methodology":"Normalizes token lemmas and matches complete sequences from the active catalog; returns the term, alternative and text offsets when available. Detection uses the active editorial catalog for the language.","limitations":"Coverage depends on the active language catalog; every match still requires contextual review.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"transparent-terms","implementation_version":"1.2","metric_title":"Use more transparent terms","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"verb-person-consistency","kind":"suggestion","analysis_language":"en","enabled":false,"genre_scope":null,"available":false,"availability_note":"Not available for this language: the analyzer's morphological tagset does not encode the required information (person and number).","implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"en","documentation":{"title":"Use verbs consistently","summary":"Warns when first-person singular and plural verb forms coexist in the same document.","methodology":"Extracts person and number from verb tags; returns a result only when both at least one 1S and one 1P form occur.","limitations":"Results depend on FreeLing/DiSeg tagging and may contain false positives or miss ambiguous constructions.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"verb-person-consistency","implementation_version":"1.2","metric_title":"Use verbs consistently","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"verbal-nominalizations","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"morphosyntax","unit":null,"documentation_language":"en","documentation":{"title":"Review verbal nominalizations","summary":"Detects nouns matching productive verbal-nominalization patterns and excludes catalogued exceptions.","methodology":"Filters lowercase nouns ending in -ción/-ciones for ES, -ció/-cions for CA and -tion/-tions for EN; catalogued exceptions are removed.","limitations":"Results depend on FreeLing/DiSeg tagging and may contain false positives or miss ambiguous constructions.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"verbal-nominalizations","implementation_version":"1.2","metric_title":"Review verbal nominalizations","category":"morphosyntax","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}},{"id":"word-repetition","kind":"suggestion","analysis_language":"en","enabled":true,"genre_scope":null,"available":true,"availability_note":null,"implementation_version":"1.2","category":"lexical","unit":null,"documentation_language":"en","documentation":{"title":"Review repeated words","summary":"Flags content words repeated in the text above the threshold configured for the genre.","methodology":"Counts how often each lemma occurs, excluding auxiliary and copular lemmas, and flags noun, adjective, adverb or verb lemmas whose frequency exceeds the genre threshold.","limitations":"This is a review signal, not an automatic correction or a complete assessment of text quality.","interpretation":"Each `occurrences` item identifies an affected span. `start` is inclusive and `end` exclusive, both measured in Unicode code points over the submitted text. `replacement`, `alternatives` and `edits` contain values only when a safe change is available. Absence means no match was found, not necessarily that the text is correct."},"references":[],"example":{"id":"sug_1","metric_id":"word-repetition","implementation_version":"1.2","metric_title":"Review repeated words","category":"lexical","severity":"warning","summary":"Example detection returned by this metric.","explanation":null,"recommendations":["Review the detected fragment in context."],"examples":null,"occurrences":[{"id":"occ_1","text":"example","paragraph_index":0,"sentence_index":0,"start":18,"end":25,"replacement":null,"alternatives":[],"edits":[]}]}}],"x-artext-guides":{"language":"en","examples":{"curl":"# Keep the key out of source code and load it from the environment.\n# Requires curl and jq.\n# export ARTEXT_API_KEY='art_live_...'\nset -euo pipefail\nAPI_BASE='http://uned.tail9cc3dd.ts.net'\nIDEMPOTENCY_KEY=\"report-$(date +%s)-$RANDOM\"\nheaders_file=$(mktemp)\ntrap 'rm -f \"$headers_file\"' EXIT\n\n# Create the report once. Idempotency-Key makes a POST retry safe after a network failure.\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\":\"en\",\"domain_slug\":\"plain-language\",\"text_type_slug\":\"legal-administrative-text-addressed-to-citizens\",\"text\":\"The application will be reviewed at the present time.\",\"external_id\":\"document-123\"}')\n\n# status_url is relative; resolve it against 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# Poll the same report while honoring Retry-After and an overall deadline.\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# Process the result only after 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# Keep the key out of source code and load it from the environment.\nAPI_BASE = 'http://uned.tail9cc3dd.ts.net'\nAPI_KEY = os.environ[\"ARTEXT_API_KEY\"]\nHEADERS = {\"Authorization\": f\"Bearer {API_KEY}\"}\npayload = {'language': 'en', 'domain_slug': 'plain-language', 'text_type_slug': 'legal-administrative-text-addressed-to-citizens', 'text': 'The application will be reviewed at the present time.', 'external_id': 'document-123'}\n\n# Create the report once. Idempotency-Key makes a POST retry safe after a network failure.\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 is relative; resolve it against API_BASE.\nstatus_url = urljoin(f\"{API_BASE}/\", created[\"status_url\"])\nretry_after = int(response.headers.get(\"Retry-After\", \"2\"))\n\n# Poll the same report while honoring Retry-After and an overall deadline.\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# Process the result only after 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+. Run this code on a server; never expose the key in a browser.\nconst API_BASE = \"http://uned.tail9cc3dd.ts.net\";\nconst API_KEY = process.env.ARTEXT_API_KEY;\nif (!API_KEY) throw new Error(\"ARTEXT_API_KEY is required\");\n\nconst headers = {\n  \"Authorization\": `Bearer ${API_KEY}`,\n  \"Content-Type\": \"application/json\"\n};\nconst api = async (path, options = {}) => {\n  const response = await fetch(new URL(path, `${API_BASE}/`), {\n    ...options,\n    headers: { ...headers, ...options.headers },\n    signal: AbortSignal.timeout(30_000)\n  });\n  if (!response.ok && response.status !== 429) {\n    throw new Error(`${response.status}: ${await response.text()}`);\n  }\n  return response;\n};\nconst delay = (milliseconds) =>\n  new Promise((resolve) => setTimeout(resolve, milliseconds));\n\n// Create the report once. Idempotency-Key makes a POST retry safe after a network failure.\nconst createdResponse = await api(\"v1/reports\", {\n  method: \"POST\",\n  headers: { \"Idempotency-Key\": crypto.randomUUID() },\n  body: JSON.stringify({\"language\":\"en\",\"domain_slug\":\"plain-language\",\"text_type_slug\":\"legal-administrative-text-addressed-to-citizens\",\"text\":\"The application will be reviewed at the present time.\",\"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// Poll the same report while honoring Retry-After and an overall deadline.\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// Process the result only after 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":"# Install the official client: pip install artext\n# Run this code on a server; never expose the key in a browser.\nimport os\n\nfrom artext import Artext\n\nclient = Artext(\n    api_key=os.environ[\"ARTEXT_API_KEY\"],\n    base_url=\"http://uned.tail9cc3dd.ts.net\",\n)\n\n# analyze creates the report, waits honoring Retry-After and returns the result.\n# A failed analysis raises an exception carrying the error detail.\nreport = client.analyze(\n    \"The application will be reviewed at the present time.\",\n    language=\"en\",\n    domain_slug=\"plain-language\",\n    text_type_slug=\"legal-administrative-text-addressed-to-citizens\",\n)\n\nprint(report.result.measurement(\"word-count\").value)\nfor suggestion in report.result.suggestions:\n    print(suggestion.metric_id, suggestion.summary)\n    for occurrence in suggestion.occurrences:\n        print(\"  \", occurrence.text, \"->\", occurrence.replacement)\n","javascript":"// Install the official client: npm install artext\n// Run this code on a server; never expose the key in a browser.\nimport { Artext } from \"artext\";\n\nconst client = new Artext({\n  apiKey: process.env.ARTEXT_API_KEY,\n  baseUrl: \"http://uned.tail9cc3dd.ts.net\",\n});\n\n// analyze creates the report, waits honoring Retry-After and returns the result.\n// A failed analysis raises an exception carrying the error detail.\nconst report = await client.analyze({\n  text: \"The application will be reviewed at the present time.\",\n  language: \"en\",\n  domainSlug: \"plain-language\",\n  textTypeSlug: \"legal-administrative-text-addressed-to-citizens\",\n});\n\nfor (const suggestion of report.result.suggestions) {\n  console.log(suggestion.metric_id, suggestion.summary);\n  for (const occurrence of suggestion.occurrences) {\n    console.log(\"  \", occurrence.text, \"->\", occurrence.replacement);\n  }\n}\n"},"request":{"language":"en","domain_slug":"plain-language","text_type_slug":"legal-administrative-text-addressed-to-citizens","text":"The application will be reviewed at the present time.","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":"en","genre":{"domain":{"slug":"plain-language","name":"Plain language"},"text_type":{"slug":"legal-administrative-text-addressed-to-citizens","name":"Legal-administrative text addressed to citizens"}},"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":"Removal of redundant expressions","category":"lexical","severity":"warning","summary":"Consider expressing this fragment more directly.","explanation":null,"occurrences":[{"id":"occ_1","text":"at the present time","paragraph_index":0,"sentence_index":0,"start":0,"end":19,"replacement":null,"alternatives":[],"edits":[{"start":0,"end":19,"text":""}]}],"recommendations":["Remove the expression when the context allows it."]}]},"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."}}}}}