> ## Documentation Index
> Fetch the complete documentation index at: https://docs.withleaf.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Field Boundary Upload

> Upload field boundary files (shapefiles, GeoJSON, KML/KMZ) and track the status of each upload and its entries.

Use the upload service to create many field boundaries at once from shapefiles, GeoJSON, or KML/KMZ files. This page covers the upload endpoints, supported formats, status values, and the follow-up calls you use to inspect results.

For conceptual background, see [Uploading Boundaries](/fields/uploading-boundaries).

## Base URL

```
https://api.withleaf.io/services/uploadservice/api
```

## Endpoints

| Endpoint                                    | Method                                                           | Path                         |
| ------------------------------------------- | ---------------------------------------------------------------- | ---------------------------- |
| [Upload a field file](#upload-a-field-file) | <span style={{fontWeight: 'bold', color: '#e5a00d'}}>POST</span> | `/upload`                    |
| [Get all uploads](#get-all-uploads)         | <span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span>  | `/upload`                    |
| [Get an upload](#get-an-upload)             | <span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span>  | `/upload/{uploadId}`         |
| [Get upload entries](#get-upload-entries)   | <span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span>  | `/upload/{uploadId}/entries` |

***

## Supported formats

You can upload field boundaries in the following formats:

* **Shapefile** — packaged as a `.zip` containing `.shp`, `.shx`, `.dbf`, and optionally `.prj`
* **GeoJSON** — `.json` or `.geojson`
* **KML / KMZ** — `.kml` or `.kmz`

<Note>Leaf reprojects all uploaded geometries to WGS 84 (EPSG:4326).</Note>

## Upload limits

| Constraint                | Limit |
| ------------------------- | ----- |
| Maximum file size         | 3 GB  |
| Maximum fields per upload | 100   |

## Upload and entry statuses

Each upload progresses through the following statuses:

| Upload status | Description                                                                                                     |
| ------------- | --------------------------------------------------------------------------------------------------------------- |
| `RECEIVED`    | The file has been received and is queued for processing.                                                        |
| `PROCESSED`   | All files in the upload have finished processing, and at least one recognized file created fields successfully. |
| `FAILED`      | The upload did not generate any field boundaries successfully.                                                  |

Individual entries within an upload have their own statuses:

| Entry status         | Description                                                    |
| -------------------- | -------------------------------------------------------------- |
| `PROCESSING`         | The entry is being converted and validated.                    |
| `CONVERTED`          | The geometry has been converted and is pending field creation. |
| `FINISHED`           | The field has been created from this entry.                    |
| `FAILED`             | The entry could not be processed.                              |
| `PARTIALLY_FINISHED` | Some geometries in the entry succeeded while others failed.    |

***

## Upload a field file

<span style={{fontWeight: 'bold', color: '#eab308'}}>POST</span> `/upload`

Uploads a field boundary file for the specified Leaf user. The file is processed asynchronously. Use the [Get an upload](#get-an-upload) or [Get upload entries](#get-upload-entries) endpoints to track progress.

### Parameters

| Parameter    | Type            | Description                                          |
| ------------ | --------------- | ---------------------------------------------------- |
| `leafUserId` | query (UUID)    | **Required.** The Leaf user who owns the fields.     |
| `farmId`     | query (integer) | Optional farm to associate with the uploaded fields. |

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    -H 'Authorization: Bearer YOUR_TOKEN' \
    -F 'file=@/path/to/boundaries.zip' \
    'https://api.withleaf.io/services/uploadservice/api/upload?leafUserId=UUID'
  ```

  ```python Python theme={null}
  import requests

  TOKEN = "YOUR_TOKEN"
  endpoint = "https://api.withleaf.io/services/uploadservice/api/upload"
  headers = {"Authorization": f"Bearer {TOKEN}"}
  params = {"leafUserId": "UUID"}

  with open("/path/to/boundaries.zip", "rb") as f:
      response = requests.post(endpoint, headers=headers, params=params, files={"file": f})
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const axios = require('axios')
  const FormData = require('form-data')
  const fs = require('fs')

  const TOKEN = 'YOUR_TOKEN'
  const endpoint = 'https://api.withleaf.io/services/uploadservice/api/upload'

  const form = new FormData()
  form.append('file', fs.createReadStream('/path/to/boundaries.zip'))

  axios.post(endpoint, form, {
    headers: { Authorization: `Bearer ${TOKEN}`, ...form.getHeaders() },
    params: { leafUserId: 'UUID' }
  })
    .then(res => console.log(res.data))
    .catch(console.error)
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
  "leafUserId": "7494c90e-28b8-4bb2-9ede-95c1cc894349",
  "originalFileUrl": "https://storage.withleaf.io/upload/f1e2d3c4-b5a6-7890-abcd-ef1234567890.zip",
  "fileName": "boundaries.zip",
  "status": "RECEIVED",
  "createdTime": "2024-01-15T19:48:51.017Z"
}
```

<Warning>If the `.zip` does not contain the required shapefile components (`.shp`, `.shx`, `.dbf`), the upload fails with status `FAILED`.</Warning>

***

## Get all uploads

<span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span> `/upload`

Returns a paginated list of field boundary uploads.

### Parameters

| Parameter    | Type          | Description                           |
| ------------ | ------------- | ------------------------------------- |
| `leafUserId` | string (UUID) | Filter by Leaf user.                  |
| `status`     | string        | `RECEIVED`, `PROCESSED`, or `FAILED`. |
| `page`       | integer       | Page number (default `0`).            |
| `size`       | integer       | Page size (default `20`).             |

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET \
    -H 'Authorization: Bearer YOUR_TOKEN' \
    'https://api.withleaf.io/services/uploadservice/api/upload?leafUserId=UUID'
  ```

  ```python Python theme={null}
  import requests

  TOKEN = "YOUR_TOKEN"
  endpoint = "https://api.withleaf.io/services/uploadservice/api/upload"
  headers = {"Authorization": f"Bearer {TOKEN}"}
  params = {"leafUserId": "UUID"}

  response = requests.get(endpoint, headers=headers, params=params)
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const axios = require('axios')
  const TOKEN = 'YOUR_TOKEN'

  const endpoint = 'https://api.withleaf.io/services/uploadservice/api/upload'
  const headers = { Authorization: `Bearer ${TOKEN}` }
  const params = { leafUserId: 'UUID' }

  axios.get(endpoint, { headers, params })
    .then(res => console.log(res.data))
    .catch(console.error)
  ```
</CodeGroup>

### Response

```json theme={null}
[
  {
    "id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
    "leafUserId": "7494c90e-28b8-4bb2-9ede-95c1cc894349",
    "originalFileUrl": "https://storage.withleaf.io/upload/f1e2d3c4-b5a6-7890-abcd-ef1234567890.zip",
    "fileName": "boundaries.zip",
    "status": "PROCESSED",
    "createdTime": "2024-01-15T19:48:51.017Z"
  }
]
```

***

## Get an upload

<span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span> `/upload/{uploadId}`

Returns a single field boundary upload by its ID.

### Parameters

| Parameter  | Type        | Description    |
| ---------- | ----------- | -------------- |
| `uploadId` | path (UUID) | The upload ID. |

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET \
    -H 'Authorization: Bearer YOUR_TOKEN' \
    'https://api.withleaf.io/services/uploadservice/api/upload/{uploadId}'
  ```

  ```python Python theme={null}
  import requests

  TOKEN = "YOUR_TOKEN"
  endpoint = "https://api.withleaf.io/services/uploadservice/api/upload/{uploadId}"
  headers = {"Authorization": f"Bearer {TOKEN}"}

  response = requests.get(endpoint, headers=headers)
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const axios = require('axios')
  const TOKEN = 'YOUR_TOKEN'

  const endpoint = 'https://api.withleaf.io/services/uploadservice/api/upload/{uploadId}'
  const headers = { Authorization: `Bearer ${TOKEN}` }

  axios.get(endpoint, { headers })
    .then(res => console.log(res.data))
    .catch(console.error)
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
  "leafUserId": "7494c90e-28b8-4bb2-9ede-95c1cc894349",
  "originalFileUrl": "https://storage.withleaf.io/upload/f1e2d3c4-b5a6-7890-abcd-ef1234567890.zip",
  "fileName": "boundaries.zip",
  "status": "PROCESSED",
  "createdTime": "2024-01-15T19:48:51.017Z"
}
```

***

## Get upload entries

<span style={{fontWeight: 'bold', color: '#16a34a'}}>GET</span> `/upload/{uploadId}/entries`

Returns the individual entries for each recognized file extracted from an upload. Each entry tracks its own processing status and lists the Leaf field IDs created from that recognized file.

### Parameters

| Parameter  | Type        | Description                |
| ---------- | ----------- | -------------------------- |
| `uploadId` | path (UUID) | The upload ID.             |
| `page`     | integer     | Page number (default `0`). |
| `size`     | integer     | Page size (default `20`).  |

### Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET \
    -H 'Authorization: Bearer YOUR_TOKEN' \
    'https://api.withleaf.io/services/uploadservice/api/upload/{uploadId}/entries'
  ```

  ```python Python theme={null}
  import requests

  TOKEN = "YOUR_TOKEN"
  endpoint = "https://api.withleaf.io/services/uploadservice/api/upload/{uploadId}/entries"
  headers = {"Authorization": f"Bearer {TOKEN}"}

  response = requests.get(endpoint, headers=headers)
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const axios = require('axios')
  const TOKEN = 'YOUR_TOKEN'

  const endpoint = 'https://api.withleaf.io/services/uploadservice/api/upload/{uploadId}/entries'
  const headers = { Authorization: `Bearer ${TOKEN}` }

  axios.get(endpoint, { headers })
    .then(res => console.log(res.data))
    .catch(console.error)
  ```
</CodeGroup>

### Response

```json theme={null}
[
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "uploadId": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
    "leafUserId": "7494c90e-28b8-4bb2-9ede-95c1cc894349",
    "fieldId": [
      "0071484f-4a75-4190-9fd0-f5995d241c2c",
      "9c2d4d24-9f31-4c0b-9b4b-7a5df4d0d5b6"
    ],
    "converterFormat": "GEOJSON",
    "originalFileUrl": "https://storage.withleaf.io/upload/entries/a1b2c3d4.geojson",
    "status": "FINISHED",
    "createFieldErrorDetails": [],
    "createdTime": "2024-01-15T19:48:51.017Z",
    "processedTime": "2024-01-15T19:49:20.104Z"
  }
]
```

<Tip>Poll the entries endpoint to track each recognized file inside the upload. Use the `fieldId` array to fetch the created fields from the Fields API, and inspect `createFieldErrorDetails` when a file finishes with `FAILED` or `PARTIALLY_FINISHED`.</Tip>
