> ## 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 Operations Quickstart

> Create a Leaf user, connect a data provider, and retrieve standardized field operation data from planting, harvest, application, and tillage activities.

This tutorial takes you from a fresh Leaf account to viewing standardized field operation data. You'll authenticate, create a Leaf user, connect a provider, and query the resulting field operations.

## Before you start

* A Leaf account with API credentials (email and password). [Register here](https://withleaf.io/account/get-a-demo) if you don't have one.
* At least one provider account (John Deere, Climate FieldView, CNHi, etc.) with farm data, or test data from Leaf's sample user.
* cURL, Python 3, or Node.js installed.

## Step 1: Get your Leaf token

Authenticate with your Leaf credentials to get a bearer token. This token is required for all subsequent API calls.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.withleaf.io/api/authenticate" \
    -H "Content-Type: application/json" \
    -d '{
      "username": "your-email@example.com",
      "password": "your-password"
    }'
  ```

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

  response = requests.post(
      "https://api.withleaf.io/api/authenticate",
      json={
          "username": "your-email@example.com",
          "password": "your-password"
      }
  )
  token = response.json()["id_token"]
  print(token)
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.withleaf.io/api/authenticate", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      username: "your-email@example.com",
      password: "your-password",
    }),
  });
  const { id_token } = await response.json();
  console.log(id_token);
  ```
</CodeGroup>

Save the `id_token` from the response. You'll pass it as a `Bearer` token in the `Authorization` header on every request.

## Step 2: Create a Leaf user

A Leaf user represents a single data owner (typically a grower or consultant). All provider credentials and data are organized under Leaf users.

<Note>
  Your account includes a sample Leaf user with pre-loaded data. You can skip this step if you just want to explore the sample data. Query `GET /users` to find it.
</Note>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.withleaf.io/services/usermanagement/api/users" \
    -H "Authorization: Bearer YOUR_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Jane Farmer",
      "email": "jane@example.com"
    }'
  ```

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

  headers = {"Authorization": f"Bearer {token}"}

  response = requests.post(
      "https://api.withleaf.io/services/usermanagement/api/users",
      headers=headers,
      json={"name": "Jane Farmer", "email": "jane@example.com"}
  )
  leaf_user = response.json()
  leaf_user_id = leaf_user["id"]
  print(f"Leaf user created: {leaf_user_id}")
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch(
    "https://api.withleaf.io/services/usermanagement/api/users",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${id_token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ name: "Jane Farmer", email: "jane@example.com" }),
    }
  );
  const leafUser = await res.json();
  console.log("Leaf user created:", leafUser.id);
  ```
</CodeGroup>

Save the `id` from the response. This is your `leafUserId`.

## Step 3: Connect a provider

The fastest way to connect a provider is with Magic Link. It generates a shareable URL that walks the grower through OAuth without any front-end code on your side.

First, configure your provider application credentials using the [provider setup guides](/providers/overview).

Then create a Magic Link:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.withleaf.io/services/widgets/api/magic-link/users/YOUR_LEAF_USER_ID/provider" \
    -H "Authorization: Bearer YOUR_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "expiresIn": 900
    }'
  ```

  ```python Python theme={null}
  response = requests.post(
      f"https://api.withleaf.io/services/widgets/api/magic-link/users/{leaf_user_id}/provider",
      headers=headers,
      json={
          "expiresIn": 900
      }
  )
  magic_link = response.json()["link"]
  print(f"Send this to the grower: {magic_link}")
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch(
    `https://api.withleaf.io/services/widgets/api/magic-link/users/${leafUser.id}/provider`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${id_token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        expiresIn: 900,
      }),
    }
  );
  const { link } = await res.json();
  console.log("Send this to the grower:", link);
  ```
</CodeGroup>

Send the returned `link` to the grower. They authenticate with their provider account, and Leaf stores the credentials automatically.

To restrict which providers appear, add `"allowedProviders": ["JohnDeere", "ClimateFieldView"]` to the request body.

### Confirm the connection worked

After the grower completes the Magic Link flow, verify that the provider now appears for the Leaf user:

```bash cURL theme={null}
curl "https://api.withleaf.io/services/integrations/api/resources?leafUserId=YOUR_LEAF_USER_ID" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

If this worked, the response includes a summary for the connected provider. If the provider does not appear yet, wait a few minutes and try again.

<Tip>
  For direct API credential attachment (without Magic Link), see the provider-specific tutorials: [John Deere](/guides/tutorials/connect-john-deere), [Climate FieldView](/guides/tutorials/connect-climate-fieldview), [CNHi](/guides/tutorials/connect-cnhi), [Trimble](/guides/tutorials/connect-trimble), [AgLeader](/guides/tutorials/connect-agleader), [Stara](/guides/tutorials/connect-stara).
</Tip>

## Step 4: Wait for data processing

Once a provider is connected, Leaf begins syncing machine files. The sync-to-operations pipeline works like this:

1. **Machine files** are fetched from the provider and converted to Leaf's standard canonical format (available as GeoJSON or GeoParquet).
2. **Field operations** are created by merging machine files that overlap in time and field boundary.

You can set up [alerts](/alerts/overview) to get notified when processing completes instead of polling.

## Step 5: Query your field operations

Once processing finishes, query the operations for your Leaf user.

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.withleaf.io/services/operations/api/operations?leafUserId=YOUR_LEAF_USER_ID" \
    -H "Authorization: Bearer YOUR_TOKEN"
  ```

  ```python Python theme={null}
  response = requests.get(
      "https://api.withleaf.io/services/operations/api/operations",
      headers=headers,
      params={"leafUserId": leaf_user_id}
  )
  operations = response.json()
  for op in operations:
      print(f"{op['id']} - {op['operationType']} - {op['startTime']}")
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch(
    `https://api.withleaf.io/services/operations/api/operations?leafUserId=${leafUser.id}`,
    { headers: { Authorization: `Bearer ${id_token}` } }
  );
  const operations = await res.json();
  operations.forEach((op) =>
    console.log(`${op.id} - ${op.operationType} - ${op.startTime}`)
  );
  ```
</CodeGroup>

Each operation has an `operationType` of `planted`, `applied`, `harvested`, or `tillage`.

## Step 6: Get operation details

For any operation, you can fetch the summary (aggregated stats) and the standard GeoJSON (point-level data).

**Summary:**

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.withleaf.io/services/operations/api/operations/OPERATION_ID/summary" \
    -H "Authorization: Bearer YOUR_TOKEN"
  ```

  ```python Python theme={null}
  op_id = operations[0]["id"]
  summary = requests.get(
      f"https://api.withleaf.io/services/operations/api/operations/{op_id}/summary",
      headers=headers
  ).json()
  print(summary)
  ```

  ```javascript JavaScript theme={null}
  const opId = operations[0].id;
  const summary = await fetch(
    `https://api.withleaf.io/services/operations/api/operations/${opId}/summary`,
    { headers: { Authorization: `Bearer ${id_token}` } }
  ).then((r) => r.json());
  console.log(summary);
  ```
</CodeGroup>

**Standard GeoJSON:**

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.withleaf.io/services/operations/api/operations/OPERATION_ID/standardGeojson" \
    -H "Authorization: Bearer YOUR_TOKEN"
  ```

  ```python Python theme={null}
  geojson = requests.get(
      f"https://api.withleaf.io/services/operations/api/operations/{op_id}/standardGeojson",
      headers=headers
  ).json()
  print(f"Features: {len(geojson['features'])}")
  ```

  ```javascript JavaScript theme={null}
  const geojson = await fetch(
    `https://api.withleaf.io/services/operations/api/operations/${opId}/standardGeojson`,
    { headers: { Authorization: `Bearer ${id_token}` } }
  ).then((r) => r.json());
  console.log("Features:", geojson.features.length);
  ```
</CodeGroup>

The standard GeoJSON contains point-level data with normalized property names like `yieldVolume`, `seedRate`, `appliedRate`, etc. You can filter by `operationType`, `startTime`, `endTime`, and other parameters.

## What you built

You now have a working pipeline that:

* Authenticates with the Leaf API
* Creates Leaf users to represent growers
* Connects data providers via Magic Link
* Retrieves standardized field operations across providers

From here, you'll likely want to:

* Set up [alerts](/alerts/overview) to react to new data automatically
* Explore [machine file conversion](/machine-data/file-conversion) for lower-level file access
* Review [configuration](/configuration/overview) to tune merge behavior and data cleaning
* Try [manual file upload](/guides/tutorials/manual-file-upload) for thumb drive data
