{
  "name": "json-to-csv",
  "summary": "Renders JSON objects as raw CSV with no spreadsheet formula protection; columns are inferred or given, nested values are JSON-encoded.",
  "rationale": "Correct CSV output is quoting and escaping to RFC-4180, not a join on commas, and the column set has to be reconciled across rows with different keys, fiddly enough that paying for it beats hand-rolling it per call. Headers and string values preserve formula prefixes (=, +, -, @), tabs and carriage returns without added apostrophes or trimming. CSV quoting escapes delimiters, quotes and newlines but does not neutralize spreadsheet formulas. Use spreadsheet-specific import controls for untrusted data. CRLF separates records.",
  "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": "Absolute http(s) URL of a UTF-8 JSON document (a leading BOM is allowed) that is an array of objects or a single object, up to 2048 characters; redirects are followed."
      },
      "columns": {
        "description": "Selects columns in exactly the supplied order; a missing key produces an empty field, except an absent __proto__ column renders {}. When omitted, visits only rows retained by max_rows, in input order, appending each unseen key once. Within each row, JavaScript enumerates canonical array-index keys (0 through 4294967294) numerically ascending, then other string keys in insertion order. Names such as 01, -1 and 4294967295 are ordinary string keys. New keys from later rows are appended, not globally sorted; textual JSON key order is not preserved for array-index keys.",
        "minItems": 1,
        "maxItems": 1000,
        "type": "array",
        "items": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255
        }
      },
      "max_rows": {
        "default": 10000,
        "description": "Maximum rows rendered, 1 to 100000, default 10000; later rows are dropped and truncated is set, and inferred columns come only from the kept rows.",
        "type": "integer",
        "minimum": 1,
        "maximum": 100000
      }
    },
    "required": [
      "url"
    ],
    "additionalProperties": false
  },
  "input_example": {
    "url": "https://grist.tools/samples/documents/garden-harvest.json"
  },
  "output_example": {
    "source_url": "https://grist.tools/samples/documents/garden-harvest.json",
    "columns": [
      "week",
      "crop",
      "kg",
      "plots",
      "note"
    ],
    "csv": "week,crop,kg,plots,note\r\n1,radish,2.5,\"[\"\"A1\"\",\"\"B2\"\"]\",\r\n1,lettuce,4,,\"first cut, outer leaves only\"\r\n2,peas,3.25,\"[\"\"A2\"\"]\",",
    "row_count": 3,
    "truncated": false
  },
  "errors": [
    "invalid_input",
    "blocked_target",
    "unreachable_target",
    "upstream_timeout",
    "too_large",
    "unprocessable",
    "internal"
  ]
}