{
  "name": "csv-to-json",
  "summary": "Parses a CSV/TSV document into JSON rows, keyed by a header row or as arrays; the delimiter is auto-detected.",
  "rationale": "A correct CSV parser is more than a split on commas (quoted fields, embedded newlines and escaped quotes all bite), and doing it over a URL an agent fetches itself walks past every SSRF control.",
  "family": "documents",
  "price_usd": "0.005",
  "free": false,
  "timeout_ms": 30000,
  "max_timeout_seconds": 51,
  "input_schema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
      "url": {
        "type": "string",
        "maxLength": 2048,
        "format": "uri",
        "description": "UTF-8 CSV/TSV URL. Missing charset means UTF-8; only utf-8 and utf8 declarations are accepted (case-insensitive, optionally quoted). Unsupported or invalid declarations and NUL bytes return 415; malformed UTF-8 returns 422. A UTF-8 BOM is accepted. Encoding is checked across the whole body."
      },
      "delimiter": {
        "description": "A single field delimiter character such as \",\" or a tab; omitted, it is auto-detected from the first rows among comma, tab, pipe, semicolon and the ASCII record and unit separators, and a line break, quote or BOM given here falls back to a comma.",
        "type": "string",
        "minLength": 1,
        "maxLength": 1
      },
      "header": {
        "default": true,
        "description": "Use the first nonempty record as object keys. Names are trimmed; blank names become column_<position>. Names are made unique left to right using the next unused _2, _3 suffix, including collisions with literal suffixed names. Special names remain data keys. Short rows pad with empty strings; extra fields return 422 csv_extra_fields. With false, preserve actual row widths as string arrays.",
        "type": "boolean"
      },
      "max_rows": {
        "default": 10000,
        "description": "Maximum returned nonempty data rows. One additional nonempty record sets truncated; its width is budgeted but not checked against the header. A truncated success does not validate the entire CSV structure.",
        "type": "integer",
        "minimum": 1,
        "maximum": 100000
      }
    },
    "required": [
      "url"
    ],
    "additionalProperties": false,
    "description": "All values remain strings. Records whose parsed fields concatenate to whitespace only are skipped, including blank lines, delimiter-only and quoted-empty records. They do not establish headers, consume max_rows or alone set truncated; structural budgets still apply."
  },
  "input_example": {
    "url": "https://grist.tools/samples/documents/garden-plots.csv",
    "header": true
  },
  "output_example": {
    "source_url": "https://grist.tools/samples/documents/garden-plots.csv",
    "delimiter": ",",
    "columns": [
      "plot",
      "holder",
      "crop",
      "area_m2",
      "notes"
    ],
    "rows": [
      {
        "plot": "A1",
        "holder": "Maple Group",
        "crop": "tomatoes",
        "area_m2": "12",
        "notes": "Stakes needed, north side"
      },
      {
        "plot": "A2",
        "holder": "Oak Group",
        "crop": "beans",
        "area_m2": "08",
        "notes": ""
      },
      {
        "plot": "B1",
        "holder": "Willow Group",
        "crop": "herbs, mixed",
        "area_m2": "6",
        "notes": "Shares water with \"B2\""
      },
      {
        "plot": "B2",
        "holder": "Birch Group",
        "crop": "squash",
        "area_m2": "10.50",
        "notes": "Compost added in April"
      }
    ],
    "row_count": 4,
    "truncated": false
  },
  "errors": [
    "invalid_input",
    "blocked_target",
    "unreachable_target",
    "upstream_timeout",
    "unsupported_content_type",
    "too_large",
    "unprocessable",
    "internal"
  ]
}