# Import a spreadsheet, mapping it for you

Imports a file onto a pack page WITHOUT being told which column is which — send the sheet as it came: `headers` (its first row) and `rows` (every other row, as arrays of text in header order). Reach for this whenever somebody hands you a spreadsheet, a CSV or a pasted table for an existing page; reach for `post_definitions_key_import` instead only when you already know the mapping and have rows keyed by column. The service folds each header onto the page's own columns, reads what every column actually holds, works out which headers identify a record so a re-import updates instead of duplicating, and creates the columns the page is missing. The answer carries the `plan` it used — one entry per header saying what it decided, why, and how sure it was — beside the ordinary import result, so you can tell the person what went where. SEND `dryRun: true` FIRST when the file is unfamiliar or large: nothing is written, the same plan and per-row outcomes come back, and you can read the mapping out before committing. Same ceiling as every import — 5000 rows, refused whole with `too_many_rows` — and the same undo: `post_definitions_key_imports_runId_undo` rolls the whole run back using the `runId` in the result.

Access class: `def:read`.

## Endpoint

`POST /v1/definitions/{key}/import/auto`

## Parameters

| Name | In | Type | Required |
| --- | --- | --- | --- |
| key | path | string | Yes |

## Request body

Content type: `application/json`.

```json
{
  "body": {
    "boardId": "",
    "dryRun": true,
    "duplicates": "string",
    "fileName": "string",
    "headers": [
      "string"
    ],
    "name": "string",
    "purpose": "string",
    "rows": [
      [
        "string"
      ]
    ],
    "unknownChoice": "string",
    "useModel": true
  },
  "params": {
    ":key": "string"
  },
  "query": {}
}
```

## Responses

### 201

the action's answer

Content type: `application/json`.

```json
{
  "data": {
    "import": {
      "columnsCreated": [
        {
          "id": "",
          "kind": "string",
          "label": "string"
        }
      ],
      "counts": {
        "created": 1,
        "failed": 1,
        "skipped": 1,
        "updated": 1
      },
      "definition": {
        "access": "string",
        "columns": [
          "string"
        ],
        "createdAt": "string",
        "icon": "string",
        "id": "",
        "key": "string",
        "name": "string",
        "object": "string",
        "pack": "string",
        "position": 1,
        "recordsEnabled": true,
        "statuses": [
          "string"
        ],
        "tagsEnabled": true,
        "templateKey": "string",
        "updatedAt": "string",
        "views": [
          "string"
        ]
      },
      "optionsAdded": [
        {
          "columnId": "",
          "label": "string",
          "values": "string"
        }
      ],
      "results": [
        {
          "error": "string",
          "id": "",
          "outcome": "string",
          "row": "string"
        }
      ],
      "runId": ""
    },
    "plan": {
      "columns": [
        {
          "decidedBy": "string",
          "decision": "string",
          "profile": "string",
          "proposed": "string"
        }
      ],
      "identity": [
        "string"
      ],
      "modelProblem": "string",
      "notes": "string",
      "source": "string",
      "statusFolds": [
        {
          "from": "string",
          "header": "string",
          "into": "string",
          "reason": "string"
        }
      ],
      "surface": "string"
    }
  }
}
```

### default

a refusal: `{"error": "<what a person needs to read>"}`. 401 no credential, 402 the plan does not include this, 403 the seat does not, 404 the thing does not exist or is not yours to see.

Content type: `application/json`.

```json
{
  "error": "string"
}
```
