Introduction
This is the Leaf Documentation.
API Reference
This is the Leaf API Reference.
Authorization Signature
The client has an api key and api secret. See /login on how to get these.
General Format
The auth is done via the Authorization HTTP header.
The expected format of this header is:
VERSION KEY=VAL, KEY2=VAL2, KEY3=VAL3
curl -H 'Authorization: VERSION KEY=VAL, KEY2=VAL2, KEY3=VAL3' https://api.leaf.ag/auth/v1/check-signature
req, _ := http.NewRequest("GET", "/check-signature", nil)
req.Header.Set("Authorization", "VERSION KEY=val, KEY2=VAL2, KEY3=VAL3")
GET /check-signature HTTP/1.1
Host: api.leaf.ag
Authorization: VERSION KEY=VAL, KEY2=VAL2, KEY3=VAL3
Expected header format.
Sequence Diagram
AGSIG0
AGSIG0 is going to be used for service to service auth.
AGSIG1
Format
curl -H 'Authorization: AGSIG1 key=$API_KEY, signature=$signature, ts=$timetamp, nonce=$nonce' \
https://api.leaf.ag/auth/v1/check-signature?id=1&guid=2
req, _ := http.NewRequest("GET", "/check-signature?id=1&guid=2", nil)
req.Header.Set("Authorization", fmt.Sprintf("AGSIG1 key=%s, signature=%s, ts=%d, nonce=%s", apiKey, signature, timestamp, nonce))
GET /check-signature?id=1&guid=2 HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<api key>, signature=<request signature>, ts=<timestamp>, nonce=<nonce>
AGSIG1 Format.
The format for AGSIG1 is the following:
AGSIG1 key=<client api key>, signature=<request signature>, ts=<request timestamp (nano)>, nonce=<random string>
Signature
The signature is expected to be hmac / sha256 with the client’s api secret, encoded in hexadecimal.
The signatureString is the concatenation of the following fields: (in that exact order)
- signature version: AGSIG1.
- HTTP method: GET, POST, etc.
- HTTP Path:
/something(without the query string, not url encoded) - QueryString: Query string (without the
?) - Timestamp: Current nano timestamp, same one as
tsin the Authrorization header. - Nonce: Random string, same one as
noncein the Authorization header.
Example
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func main() {
str := "AGSIG1GET/pullid=1&guid=214460642579972822224722613af3bf1dfbb0e177e2edbd0412"
hash := hmac.New(sha256.New, []byte("bde"))
_, _ = hash.Write([]byte(str))
println(hex.EncodeToString(hash.Sum(nil)))
// output: b7ce50891744978ec926b369f0e164bf6a5efa5f7ebe0273aaba7c07f0a4d416
}
AGSIG1 Signature Example (Go). Playground
import jsSHA from "jssha";
let shaObj = new jsSHA("SHA-256", "TEXT");
shaObj.setHMACKey("bde", "TEXT");
shaObj.update("AGSIG1GET/pullid=1&guid=214460642579972822224722613af3bf1dfbb0e177e2edbd0412");
console.log(shaObj.getHMAC("HEX"));
// output: b7ce50891744978ec926b369f0e164bf6a5efa5f7ebe0273aaba7c07f0a4d416
AGSIG1 Signature Example (Javascript)
curl -H 'Authorization: AGSIG1 key=abc, signature=b7ce50891744978ec926b369f0e164bf6a5efa5f7ebe0273aaba7c07f0a4d416, ts=1446064257997282222, nonce=4722613af3bf1dfbb0e177e2edbd0412' \
https://api.leaf.ag/auth/v1/check-signature?id=1&guid=2
req, _ := http.NewRequest("GET", "/check-signature?id=1&guid=2", nil)
req.Header.Set("Authorization", "AGSIG1 key=abc, signature=b7ce50891744978ec926b369f0e164bf6a5efa5f7ebe0273aaba7c07f0a4d416, ts=1446064257997282222, nonce=4722613af3bf1dfbb0e177e2edbd0412")
GET /check-signature?id=1&guid=2 HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=abc, signature=b7ce50891744978ec926b369f0e164bf6a5efa5f7ebe0273aaba7c07f0a4d416, ts=1446064257997282222, nonce=4722613af3bf1dfbb0e177e2edbd0412
AGSIG1 Request example.
API keys for client from auth /login: - API_KEY=abc - API_SECRET=bde
- Method:
GET - Path:
/pull - QueryString:
id=1&guid=2 - Timestamp:
1446064257997282222(October 28, 2015 3:25PM CDT) - Nonce:
4722613af3bf1dfbb0e177e2edbd0412(random)
Signature string:
AGSIG1GET/pullid=1&guid=214460642579972822224722613af3bf1dfbb0e177e2edbd0412
Signature hashed with user’s secret (bde):
b7ce50891744978ec926b369f0e164bf6a5efa5f7ebe0273aaba7c07f0a4d416
AGSIG2
AGSIG2 will add support for body and extra headers to be part of the signature.
Authorization Endpoints
All requests to the auth endpoints must be prefixed by /auth/v1.
Request and response bodies are JSON-encoded.
Register
POST /register HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"email": "email@domain.com",
"first_name": "Bob",
"last_name": "Odenkirk",
"password": "password",
"company_name": "Company, inc",
"private_key": "abcdef"
}
HTTP/1.1 201 CREATED
Connection: close
curl "https://api.leaf.ag/auth/v1/register" \
-X POST \
-d '{
"email": "email@domain.com",
"first_name": "Bob",
"last_name": "Odenkirk",
"password": "password",
"company_name": "Company, inc",
"private_key": "abcdef"
}'
type RegisterRequest struct {
CompanyName string `json:"company_name"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Password string `json:"password"`
PrivateKey string `json:"private_key"`
}
Register request.
Register a new user in the Leaf system.
When provided with a valid email address, a password, and the correct private_key a client is presented with a 201.
HTTP Request
POST /register
| Parameter | Type | Default | Description |
|---|---|---|---|
| string | The user’s email | ||
| first_name | string | The user’s first name | |
| last_name | string | The user’s last name | |
| password | string | The user’s password | |
| company_name | string | The user’s company | |
| private_key | string | The leaf private key (ask Guillaume or Jason) |
HTTP Response
- 201: Successfully registered.
- 400: Bad Request.
- 500: Internal error.
Login
POST /login HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"email": "email@domain.com",
"password": "password"
}
curl "https://api.leaf.ag/auth/v1/login"
-X POST
-d '{
"email": "email@domain.com",
"password": "password"
}'
type AuthRequest struct {
Email string `json:"email"`
Password string `json:"password"`
}
Login request.
HTTP/1.1 202 ACCEPTED
Content-Type: application/json
{
"api_key":"47a88ee3a11c0163e3ca7485a9de950a",
"api_secret":"13e6c0ab4ed938b2cef1905b2f73b7f8",
"user":{
"id":"91476417-45f9-4f2c-9cbf-397d0bd06314",
"timezone":"MST",
"company_id":2,
"email":"email@domain.com",
"role":"manager",
"created_at":"2016-02-25T19:10:27.283485Z",
"updated_at":"2016-02-25T19:10:27.283485Z"
}
}
{
"api_key":"47a88ee3a11c0163e3ca7485a9de950a",
"api_secret":"13e6c0ab4ed938b2cef1905b2f73b7f8",
"user":{
"id":"91476417-45f9-4f2c-9cbf-397d0bd06314",
"timezone":"MST",
"company_id":2,
"email":"email@domain.com",
"role":"manager",
"created_at":"2016-02-25T19:10:27.283485Z",
"updated_at":"2016-02-25T19:10:27.283485Z"
}
}
type AuthResponse struct {
APIKey string `json:"api_key"`
APISecret string `json:"api_secret"`
User *users.User `json:"user"`
}
Login response.
Login as a registered user of the Leaf system.
When provided with a registered email and the correct corresponding password the client is presented with a 202
and a json response with the api key and api secret as api_key and api_secret in the payload.
It is the client’s repsonsibility to store the key/secret pair to sign future requests.
Note that email is case-insensitive.
HTTP Request
POST /login
| Parameter | Type | Default | Description |
|---|---|---|---|
| string | The user’s email | ||
| password | string | The user’s password |
HTTP Response
- 202: Login/Password accepted.
- 400: Bad request.
- 401: Unauthorized.
- 500: Internal error.
| Parameter | Type | Description |
|---|---|---|
| api_key | string | The user’s api key (public). |
| api_secret | string | The user’s api secret (private). |
| user | user | Logged in user object. Including the timezone. |
Logout
POST /logout HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
gurl -X POST -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/auth/v1/logout"
Logout request.
HTTP/1.1 200 OK
Content-Type: application/json
Logout response.
Logout the current user.
HTTP Request
POST /logout
HTTP Response
- 200: Logged out.
- 400: Bad request.
- 401: Unauthorized.
- 500: Internal error.
Check-Signature
GET /check-signature HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
HTTP/1.1 200 OK
Connection: close
# Get $API_KEY and $API_SECRET from /login
gurl -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/auth/v1/check-signature"
import (
"github.com/agrarianlabs/service/lib/signer"
"github.com/agrarianlabs/service/lib/signer/agsig1"
)
//...
client, _ := signer.NewClient("http://api.leaf.ag/auth/v1", apiKey, apiSecret, agsig1.Version)
resp, _ := client.DoRequest("GET", "/check-signature", "", nil, nil)
Check Signature request.
Checks that the signature in the Authorization header is valid.
HTTP Request
GET /check-signature
HTTP Response
- 200: OK
- 400, 401, 500: KO
Confirm
POST /confirm HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"email": "user@domain.com",
"password": "my_new_pa$$word",
"company_name": "my_company_name",
"token": "21c979b214169b4370bc8075c2162f83"
}
curl -v -X POST \
-d '{
"email": "user@domain.com",
"password": "my_new_pa$$word",
"company_name": "my_company_name",
"token": "21c979b214169b4370bc8075c2162f83"
}' \
https://api.leaf.ag/auth/v1/confirm
Confirm an invitation to join a company on Leaf.
Request Body
| Field | Type | Required | Notes |
|---|---|---|---|
| string | yes | A valid RFC 5322 email address | |
| password | string | yes | The user’s new password |
| company_name | string | yes | The name of the company the user wishes to join |
| token | string | yes | A token used to validate the user’s request |
Status Codes
200400- Request body was invalid
Invite
POST /invite HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"email": "user@domain.com"
}
curl -v -X POST \
-d '{
"email": "user@domain.com"
}' \
https://api.leaf.ag/auth/v1/invite
Resend an invitation email.
Request Body
| Field | Type | Required | Notes |
|---|---|---|---|
| string | yes | The email of the user whose invitation you wish to resend. |
Status Codes
200400- Request body was invalid
Health
GET /health HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 200 OK
Connection: close
curl -v "https://api.leaf.ag/auth/v1/health"
Health request.
Checks the health of this service.
HTTP Request
GET /health
HTTP Response
- 200: OK
- 500 (or anything else): KO
Heartbeats
Submit Heartbeats
gurl -X POST -key $API_KEY -secret $API_SECRET \
-d '[{
"user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
"managementzone_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380b22",
"device_id": "0000-0000-0000-0000",
"lat": 30.25,
"long": 97.75,
"heading": 1.2,
"speed": 33.5,
"horizontal_accuracy": 10.0,
"vertical_accuracy": 0.034,
"gps_timestamp": 1447625945,
"timetamp": 1447625946,
"beacons": [
{
"proximity_uuid": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
"major": 1,
"minor": 3,
"distance": 11.2
}
],
"paired_beacons": [
{
"proximity_uuid": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22",
"major": 1,
"minor": 3,
"distance": 11.2
}
]}]' \
"https://api.leaf.ag/heartbeats/v1/heartbeat"
POST /heartbeat HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
[{
"user_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
"managementzone_id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380b22",
"device_id": "0000-0000-0000-0000",
"lat": 30.25,
"long": 97.75,
"heading": 1.2,
"speed": 33.5,
"horizontal_accuracy": 10.0,
"vertical_accuracy": 0.034,
"gps_timestamp": 1447625945,
"timetamp": 1447625946,
"beacons": [
{
"proximity_uuid": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
"major": 1,
"minor": 3,
"distance": 11.2
}
],
"paired_beacons": [
{
"proximity_uuid": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22",
"major": 1,
"minor": 3,
"distance": 11.2
}
]
}]
HTTP/1.1 200 OK
Connection: close
Submit Heartbeats request.
Create heartbeats in the system.
All requests sent to the service will be recorded regardless if the POST payload contains the expect post parameters.
HTTP Request
POST /heartbeat
Parameters
Heartbeats are required to be sent in batch. (json list of heartbeat objects)
| Parameter | Type | Description |
|---|---|---|
| device_id | string | The device id. For iOS, it will be the serial number. |
| user_id | string | The operator/user’s uuid. |
| managementzone_id | string | The managementzone’s uuid. |
| lat | float64 | Latitude Location. The units of lat is degrees. |
| long | float64 | Longitude Location. The units of long is degrees. |
| heading | float64 | Heading of the device. The units of heading is degrees. |
| speed | float64 | Speed of the device. The units of speed is meters/second. |
| horizontal_accuracy | float64 | Horizontal accuracy (in feet) of the GPS measurement. |
| vertical_accuracy | float64 | Vertical accuracy (in feet) of the GPS measurement. |
| gps_timestamp | int | UTC timestamp when the GPS data was observed. |
| timestamp | int | UTC timestamp when the client sent the heartbeats. |
| beacons | []beacon | Nearby beacon list. |
| paired_beacons | []beacon | Paired beacon list. |
Beacon
| Parameter | Type | Description |
|---|---|---|
| proximity_uuid | string | Beacon proximity uuid. |
| major | int | Beacon Major version. |
| minor | int | Beacon Minor version. |
| distance | float64 | Beacon distance from device. The units of distance is meters. |
Health
curl "https://api.leaf.ag/heartbeats/v1/health"
Checks the health of this service.
HTTP Request
GET /health
Users
All users endpoints must be prefixed with /resources/v1.
Eventually, when breaking changes are deployed we will create new versions and update this document accordingly.
All requests and responses must be JSON-encoded.
Add Users
POST /users HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"email": "user@domain.com",
"role": "manager",
"prefix_name": "Sir",
"first_name": "David",
"middle_name": "John",
"last_name": "Cross",
"suffix_name": "Jr.",
"phone_number": "(602) 867-7193",
"employee_id": "283746345",
"emergency_contact_name": "Mama Cross",
"emergency_contact_phone": "(602) 555-5555",
"emergency_contact_relationship": "mother",
"tags": [
"tag1",
"tag2"
]
}
HTTP/1.1 201 CREATED
Connection: Close
gurl \
-X POST \
-d '{
"email": "user@domain.com",
"role": "manager",
"prefix_name": "Sir",
"first_name": "David",
"middle_name": "John",
"last_name": "Cross",
"suffix_name": "Jr.",
"phone_number": "(602) 867-7193",
"employee_id": "283746345",
"emergency_contact_name": "Mama Cross",
"emergency_contact_phone": "(602) 555-5555",
"emergency_contact_relationship": "mother",
"tags": [
"tag1",
"tag2"
]
}' \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/users"
Add a new users for a company.
- The users is added to the company associated with the key and secret used to authenticate the request.
- If you add a manager, an invitation email will be sent to them that contains a link to a page they will use to set their password.
Request Body
| Field | Type | Required | Notes |
|---|---|---|---|
| role | string | yes | “manager” or “operator” |
| string | only if role is “manager” | A valid RFC 5322 email address | |
| prefix_name | string | no | |
| first_name | string | yes | |
| middle_name | string | no | |
| last_name | string | yes | |
| suffix_name | string | no | |
| phone_number | string | no | |
| employee_id | string | no | |
| emergency_contact_name | string | no | |
| emergency_contact_phone | string | no | |
| emergency_contact_relationship | string | no | |
| description | string | no | |
| hire_date | RFC 3339 timestamp | no | |
| tags | []string | no |
Status Codes
201- Users was created successfully.400- Request body was invalid.
Response Body
JSON-encoded string containing the ID of the new users.
Example: "8EF57207-9D29-4C4F-8954-FCEFFED272A7"
Get Users
GET /users/8EF57207-9D29-4C4F-8954-FCEFFED272A7 HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
HTTP/1.1 200 OK
Connection: Close
{
"id": "8EF57207-9D29-4C4F-8954-FCEFFED272A7",
"company_id": 5,
"company_name": "GloboChem",
"email": "user@domain.com",
"role": "manager",
"prefix_name": "Sir",
"first_name": "David",
"middle_name": "John",
"last_name": "Cross",
"suffix_name": "Jr.",
"phone_number": "(602) 867-7193",
"employee_id": "283746345",
"emergency_contact_name": "Mama Cross",
"emergency_contact_phone": "(602) 555-5555",
"emergency_contact_relationship": "mother",
"tags": [
"tag1",
"tag2"
],
"latest_location":{
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long":51.5034070,
"lat":-0.1275920,
"heading":1,
"speed":19.4,
"created_at":"2016-03-04T18:44:41.914068Z"
}
}
gurl \
-X GET \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/users/8EF57207-9D29-4C4F-8954-FCEFFED272A7"
{
"id": "8EF57207-9D29-4C4F-8954-FCEFFED272A7",
"company_id": 5,
"company_name": "GloboChem",
"email": "user@domain.com",
"role": "manager",
"prefix_name": "Sir",
"first_name": "David",
"middle_name": "John",
"last_name": "Cross",
"suffix_name": "Jr.",
"phone_number": "(602) 867-7193",
"employee_id": "283746345",
"emergency_contact_name": "Mama Cross",
"emergency_contact_phone": "(602) 555-5555",
"emergency_contact_relationship": "mother",
"tags": [
"tag1",
"tag2"
],
"latest_location":{
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long":51.5034070,
"lat":-0.1275920,
"heading":1,
"speed":19.4,
"created_at":"2016-03-04T18:44:41.914068Z"
}
}
Get a users’s profile.
Status Codes
200404- Users with the specified ID does not exist.
Response Body
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | ||
| company_id | string | ||
| company_name | string | ||
| string | no | ||
| prefix_name | string | no | |
| first_name | string | no | |
| middle_name | string | no | |
| last_name | string | no | |
| suffix_name | string | no | |
| phone_number | string | no | |
| employee_id | string | no | |
| emergency_contact_name | string | no | |
| emergency_contact_phone | string | no | |
| emergency_contact_relationship | string | no | |
| description | string | no | |
| hire_date | RFC 3339 timestamp | no | |
| tags | []string | no | |
| latest_location | object | no | see details in example. |
List Users
GET /users HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 200 OK
Content-Type: application/json
Connection: Close
[
{
"id":"fc25e422-75fe-44e5-af38-e1d14ee3d7c0",
"company_id":1,
"email":"leaftest@example.com",
"role":"manager",
"created_at":"2016-01-28T20:21:59.950723Z",
"updated_at":"2016-01-28T20:21:59.950723Z",
"latest_location":{
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long":51.5034070,
"lat":-0.1275920,
"heading":1,
"speed":19.2,
"created_at":"2016-03-04T18:44:41.914068Z"
}
},
{
"id":"cc0ad904-1eb4-4597-8206-d59d34b6a689",
"company_id":1,
"role":"operator",
"prefix_name":"Sir",
"first_name":"David",
"middle_name":"John",
"last_name":"Cross",
"suffix_name":"Jr.",
"phone_number":"(602) 867-7193",
"employee_id":"abc123",
"emergency_contact_name":"Van Hammersly",
"emergency_contact_phone":"(602) 992-4135",
"emergency_contact_relationship":"Mr Show Character",
"description":"Entitilitus sufferer",
"hire_date":"2015-12-19T05:15:45Z",
"created_at":"2016-01-28T20:22:31.894883Z",
"updated_at":"2016-01-28T20:22:31.894883Z"
},
{
"id":"9d8b4db1-3e2e-4fe0-a9a6-50343d8dfc58",
"company_id":1,
"role":"operator",
"first_name":"Pitpat",
"created_at":"2016-01-28T20:22:32.055668Z",
"updated_at":"2016-01-28T20:22:32.060941Z"
},
{
"id":"2fcfe7e3-c65e-419a-8b8e-66d051b69061",
"company_id":1,
"role":"operator",
"description":"National Talk Backwards for Entitilitus Day",
"created_at":"2016-01-28T20:22:32.154424Z",
"updated_at":"2016-01-28T20:22:32.165249Z",
"latest_location":{
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long":59.5034070,
"lat":-10.1275920,
"heading":1,
"speed":14.2,
"created_at":"2016-03-04T18:44:41.914068Z"
}
},
{
"id":"f43b23b2-a56f-4b1e-b082-897910569786",
"company_id":1,
"role":"manager",
"created_at":"2016-01-28T20:22:32.389624Z",
"updated_at":"2016-01-28T20:22:32.408093Z"
}
]
gurl \
-X GET \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/users"
[
{
"id":"fc25e422-75fe-44e5-af38-e1d14ee3d7c0",
"company_id":1,
"email":"leaftest@example.com",
"role":"manager",
"created_at":"2016-01-28T20:21:59.950723Z",
"updated_at":"2016-01-28T20:21:59.950723Z",
"latest_location":{
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long":51.5034070,
"lat":-0.1275920,
"heading":1,
"speed":19.2,
"created_at":"2016-03-04T18:44:41.914068Z"
}
},
{
"id":"cc0ad904-1eb4-4597-8206-d59d34b6a689",
"company_id":1,
"role":"operator",
"prefix_name":"Sir",
"first_name":"David",
"middle_name":"John",
"last_name":"Cross",
"suffix_name":"Jr.",
"phone_number":"(602) 867-7193",
"employee_id":"abc123",
"emergency_contact_name":"Van Hammersly",
"emergency_contact_phone":"(602) 992-4135",
"emergency_contact_relationship":"Mr Show Character",
"description":"Entitilitus sufferer",
"hire_date":"2015-12-19T05:15:45Z",
"created_at":"2016-01-28T20:22:31.894883Z",
"updated_at":"2016-01-28T20:22:31.894883Z"
},
{
"id":"9d8b4db1-3e2e-4fe0-a9a6-50343d8dfc58",
"company_id":1,
"role":"operator",
"first_name":"Pitpat",
"created_at":"2016-01-28T20:22:32.055668Z",
"updated_at":"2016-01-28T20:22:32.060941Z"
},
{
"id":"2fcfe7e3-c65e-419a-8b8e-66d051b69061",
"company_id":1,
"role":"operator",
"description":"National Talk Backwards for Entitilitus Day",
"created_at":"2016-01-28T20:22:32.154424Z",
"updated_at":"2016-01-28T20:22:32.165249Z"
"latest_location":{
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long":59.5034070,
"lat":-10.1275920,
"heading":1,
"speed":14.2,
"created_at":"2016-03-04T18:44:41.914068Z"
}
},
{
"id":"f43b23b2-a56f-4b1e-b082-897910569786",
"company_id":1,
"role":"manager",
"created_at":"2016-01-28T20:22:32.389624Z",
"updated_at":"2016-01-28T20:22:32.408093Z"
}
]
List users for a company.
Status Codes
200
Response Body
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | ||
| company_id | string | ||
| company_name | string | ||
| string | |||
| prefix_name | string | no | |
| first_name | string | no | |
| middle_name | string | no | |
| last_name | string | no | |
| suffix_name | string | no | |
| phone_number | string | no | |
| employee_id | string | no | |
| emergency_contact_name | string | no | |
| emergency_contact_phone | string | no | |
| emergency_contact_relationship | string | no | |
| description | string | no | |
| hire_date | RFC 3339 timestamp | no | |
| latest_location | object | no | see details in example. |
Edit users
PUT /users/8EF57207-9D29-4C4F-8954-FCEFFED272A7 HTTP/1.1
Host: api.leaf.ag
{
"email": "ronnie.dobbs@globochem.com"
}
HTTP/1.1 200 OK
Content-Type: application/json
Connection: Close
gurl \
-X PUT \
-key $API_KEY \
-secret $API_SECRET \
-d '{"email":"ronnie.dobbs@globochem.com"}'
"https://api.leaf.ag/resources/v1/users/8EF57207-9D29-4C4F-8954-FCEFFED272A7"
Edit a users’s profile.
- If editing a user’s role from manager to operator, the user cannot login to system any more.
- If editing a user’s role from operator to manager, the user will receive an invitation email.
Request Body
| Field | Type | Required | Notes |
|---|---|---|---|
| role | string | no | “manager” or “operator” |
| string | only if role is “manager” | A valid RFC 5322 email address | |
| prefix_name | string | no | |
| first_name | string | yes | |
| middle_name | string | no | |
| last_name | string | yes | |
| suffix_name | string | no | |
| phone_number | string | no | |
| employee_id | string | no | |
| emergency_contact_name | string | no | |
| emergency_contact_phone | string | no | |
| emergency_contact_relationship | string | no | |
| description | string | no | |
| hire_date | RFC 3339 timestamp | no | |
| tags | []string | no |
Response Body
202: Accepted.404: Users with the specified ID does not exist.500: Internal error.
Equipment
All equipment endpoints must be prefixed with /resources/v1.
Eventually, when breaking changes are deployed we will create new versions and update this document accordingly.
All requests and responses must be JSON-encoded.
Add Equipment
Add a new equipment in the Leaf system.
The available categories of equipment are selfpropelled, implement, truck and misc.
POST /equipment HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"category": "selfpropelled",
"name": "mySprayer",
"type": "sprayer",
"physical_width": 12.34,
"physical_width_units": "feet",
"pass_width": 12.45,
"pass_width_units": "feet",
"make": "make",
"model": "model",
"year": 2016,
"purpose": "Harvesting",
"machine_hours": 1000
}
HTTP/1.1 201 CREATED
Connection: Close
# Adding a selfpropelled equipment.
gurl \
-X POST \
-d '{
"category": "selfpropelled",
"name": "myTractor",
"type": "tractor",
"make": "make",
"model": "model",
"year": 2016,
"beacon": {
"serial": "4A05",
"proximity_uuid":"A911267C-9A96-4B88-B66A-060821AD75E3",
"major":1,
"minor":1,
"distance":0
}
}' \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/equipment"
# Adding an implement.
gurl \
-X POST \
-d '{
"category": "implement",
"name": "myImplement",
"physical_width": 1.0,
"physical_width_units": "feet",
"pass_width": 1.0,
"pass_width_units": "feet",
"purpose": "Harvesting"
}' \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/equipment"
# Adding a truck.
gurl \
-X POST \
-d '{
"category": "truck",
"name": "myTruck",
"tags": ["tag1", "tag2"]
}' \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/equipment"
# Adding a misc.
gurl \
-X POST \
-d '{
"category": "misc",
"name": "MyMisc",
"type": "notselfpropelled",
"fields": [
{"name":"field1", "value":"value1"},
{"name":"field2", "value":"123"}
]
}' \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/equipment
HTTP Request
POST /equipment
Post parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| category | string | Yes | The equipment’s category. The available categories are selfpropelled, implement, truck and misc. |
| name | string | Yes | The equipment’s name. |
| type | string | No | The equipment’s type. |
| make | string | No | The equipment’s make. |
| model | string | No | The equipment’s model. |
| year | int | No | The equipment’s year. |
| physical_width | float64 | No | The equipment’s physical width. |
| physical_width_units | string | No | The equipment’s physical width units, the value is “feet”. |
| pass_width | float64 | No | The equipment’s pass width. |
| pass_width_units | string | No | The equipment’s pass width units, the value is “feet”. |
| beacon | object | No | The equipment’s beacon. See shell example about how to use beacon. |
| vin_sn | string | No | The equipment’s VIN/SN. |
| purpose | string | No | The equipment’s purpose. |
| brand | string | No | The equipment’s brand. |
| description | string | No | The equipment’s description. |
| cost | float64 | No | The equipment’s cost. |
| mileage | int | No | The equipment’s mileage. |
| machine_hours | int | No | The equipment’s machine hours. |
| tags | []string | No | The equipment’s tags, optional for all categories of equipment. |
| fields | []Field | No | The equipment’s optional fields, optional for all categories of equipment. See shell example about how to use Fields |
HTTP Response
- 201: Equipment created.
- 400: Bad request.
- 500: Internal error.
Response Body
JSON-encoded string containing the ID of the new equipment.
Example: "8EF57207-9D29-4C4F-8954-FCEFFED272A7"
Category field
| No. | Type |
|---|---|
| 1 | selfpropelled |
| 2 | implement |
| 3 | truck |
| 4 | misc |
Type field
| No. | Type | Description |
|---|---|---|
| 1 | sprayer | available for selfpropelled category |
| 2 | swather | available for selfpropelled category |
| 3 | combine | available for selfpropelled category |
| 4 | chopper | available for selfpropelled category |
| 5 | tractor | available for selfpropelled category |
| 6 | selfpropelled | available for misc category |
| 7 | notselfpropelled | available for misc category |
Purpose field
| No. | Value |
|---|---|
| 1 | harvesting |
| 2 | spreading |
| 3 | spraying |
| 4 | planting |
| 5 | tilling |
| 6 | transporting |
| 7 | cultivating |
| 8 | seeding |
List Equipment
List existed equipment of the Leaf system by company ID.
GET /equipment HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id":"b4b30b79-28db-4de9-835c-7849fa25f834",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"myMisc2",
"category":"misc",
"type":"notselfpropelled",
"vin_sn":"vin",
"make":"make",
"model":"model",
"purpose":"Harvesting",
"created_at":"2016-01-28T20:09:50.917443Z",
"updated_at":"2016-01-28T20:09:50.917443Z"
},
{
"id":"09e544d6-cd9b-4a65-9c24-a94d32daaa8e",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"myMisc1",
"category":"misc",
"type":"selfpropelled",
"make":"make",
"model":"model",
"purpose":"Harvesting",
"created_at":"2016-01-28T20:09:43.824828Z",
"updated_at":"2016-01-28T20:09:43.824828Z"
},
{
"id":"c19a1da6-6d60-4b41-a69a-37dea7f6b76f",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"myTruck",
"category":"truck",
"vin_sn":"vin",
"make":"make",
"model":"model",
"year":2016,
"mileage": 1000,
"created_at":"2016-01-28T20:09:35.412842Z",
"updated_at":"2016-01-28T20:09:35.412842Z"
},
{
"id":"27874e3d-6135-4041-84e8-7a3e1ec492d6",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"myImplement",
"category":"implement",
"vin_sn":"sn",
"make":"make",
"model":"model",
"purpose":"Harvesting",
"pass_width":12.45,
"pass_width_units":"feet",
"physical_width":12.34,
"physical_width_units":"feet",
"cost":100000,
"year":2016,
"description":"this is a selfpropelled equipment",
"beacon":{
"serial": "4A05",
"proximity_uuid":"5554faa0-1234-4ffd-883a-2d9baadbf873",
"major":11,
"minor":11,
"distance":0
},
"latest_location": {
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long": 97.75,
"lat": 30.25,
"heading": 1.2,
"speed": 33.5,
"created_at": "2016-03-04T19:53:05.996918Z"
},
"tags":["tag1","tag2"],
"created_at":"2016-01-28T20:09:20.132192Z",
"updated_at":"2016-01-28T20:09:20.132192Z"
},
{
"id":"b35b1d4f-79ae-4502-b2fc-83e9c65fe266",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"myTractor",
"category":"selfpropelled",
"type":"tractor",
"make":"make",
"model":"model",
"year":2016,
"tags":["tag1","tag2"],
"created_at":"2016-01-28T20:09:13.626533Z",
"updated_at":"2016-01-28T20:09:13.626533Z"
},
{
"id":"e510a1b4-034e-4359-a9a2-b754694193e7",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"mySprayer",
"category":"selfpropelled",
"type":"sprayer",
"vin_sn":"vin sn",
"brand":"my brand",
"make":"make",
"model":"model",
"purpose":"Harvesting",
"pass_width":12.45,
"pass_width_units":"feet",
"physical_width":12.34,
"physical_width_units":"feet",
"cost":100000,
"year":2016,
"description":"this is a selfpropelled equipment",
"beacon":{
"serial": "4A05",
"proximity_uuid":"5554faa0-1234-4ffd-883a-2d9baadbf874",
"major":13,
"minor":13,
"distance":0
},
"latest_location": {
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long":65.75,
"lat": 30.25,
"heading": 1.2,
"speed": 33.5,
"created_at": "2016-03-04T19:53:05.996918Z"
},
"machine_hours":1000,
"tags":["tag1","tag2"],
"created_at":"2016-01-28T20:09:06.256025Z",
"updated_at":"2016-01-28T20:09:06.256025Z"
}
]
The request:
gurl \
-X GET \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/equipment"
The response:
[
{
"id":"b4b30b79-28db-4de9-835c-7849fa25f834",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"myMisc2",
"category":"misc",
"type":"notselfpropelled",
"vin_sn":"vin",
"make":"make",
"model":"model",
"purpose":"Harvesting",
"created_at":"2016-01-28T20:09:50.917443Z",
"updated_at":"2016-01-28T20:09:50.917443Z"
},
{
"id":"09e544d6-cd9b-4a65-9c24-a94d32daaa8e",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"myMisc1",
"category":"misc",
"type":"selfpropelled",
"make":"make",
"model":"model",
"purpose":"Harvesting",
"created_at":"2016-01-28T20:09:43.824828Z",
"updated_at":"2016-01-28T20:09:43.824828Z"
},
{
"id":"c19a1da6-6d60-4b41-a69a-37dea7f6b76f",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"myTruck",
"category":"truck",
"vin_sn":"vin",
"make":"make",
"model":"model",
"year":2016,
"mileage":1000,
"created_at":"2016-01-28T20:09:35.412842Z",
"updated_at":"2016-01-28T20:09:35.412842Z"
},
{
"id":"27874e3d-6135-4041-84e8-7a3e1ec492d6",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"myImplement",
"category":"implement",
"vin_sn":"sn",
"make":"make",
"model":"model",
"purpose":"Harvesting",
"pass_width":12.45,
"pass_width_units":"feet",
"physical_width":12.34,
"physical_width_units":"feet",
"cost":100000,
"year":2016,
"description":"this is a selfpropelled equipment",
"beacon":{
"serial": "4A05",
"proximity_uuid":"5554faa0-1234-4ffd-883a-2d9baadbf873",
"major":11,
"minor":11,
"distance":0
},
"latest_location": {
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long": 97.75,
"lat": 30.25,
"heading": 1.2,
"speed": 33.5,
"created_at": "2016-03-04T19:53:05.996918Z"
},
"tags":["tag1","tag2"],
"created_at":"2016-01-28T20:09:20.132192Z",
"updated_at":"2016-01-28T20:09:20.132192Z"
},
{
"id":"b35b1d4f-79ae-4502-b2fc-83e9c65fe266",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"myTractor",
"category":"selfpropelled",
"type":"tractor",
"make":"make",
"model":"model",
"year":2016,
"tags":["tag1","tag2"],
"created_at":"2016-01-28T20:09:13.626533Z",
"updated_at":"2016-01-28T20:09:13.626533Z"
},
{
"id":"e510a1b4-034e-4359-a9a2-b754694193e7",
"user_id":"d2fb0f59-1d3c-40d2-9cb8-bcf2dcf41e3e",
"company_id":1,
"name":"mySprayer",
"category":"selfpropelled",
"type":"sprayer",
"vin_sn":"vin sn",
"brand":"my brand",
"make":"make",
"model":"model",
"purpose":"Harvesting",
"pass_width":12.45,
"pass_width_units":"feet",
"physical_width":12.34,
"physical_width_units":"feet",
"cost":100000,
"year":2016,
"description":"this is a selfpropelled equipment",
"beacon":{
"serial": "4BC6",
"proximity_uuid":"5554faa0-1234-4ffd-883a-2d9baadbf873",
"major":11,
"minor":11,
"distance":0
},
"latest_location": {
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long":65.75,
"lat": 30.25,
"heading": 1.2,
"speed": 33.5,
"created_at": "2016-03-04T19:53:05.996918Z"
},
"machine_hours":1000,
"tags":["tag1","tag2"],
"created_at":"2016-01-28T20:09:06.256025Z",
"updated_at":"2016-01-28T20:09:06.256025Z"
}
]
HTTP Request
GET /equipment
HTTP Response
- 200: Ok.
- 400: Bad request.
- 500: Internal error.
Get Equipment
Get the detailed equipment by equipment ID.
GET /equipment/d24725b1-d021-4622-ac7a-10134689a4c1 HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "d24725b1-d021-4622-ac7a-10134689a4c1",
"category": "selfpropelled",
"name": "mySprayer",
"type": "sprayer",
"width": 12.34,
"units": 56.0,
"brand": "brand",
"make": "make",
"model": "model",
"purpose": "Harvesting",
"machine_hours":1000,
"beacon":{
"serial": "4A05",
"proximity_uuid":"5554faa0-1234-4ffd-883a-2d9baadbf873",
"major":11,
"minor":11,
"distance":0
},
"latest_location": {
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long":65.75,
"lat": 30.25,
"heading": 1.2,
"speed": 33.5,
"created_at": "2016-03-04T19:53:05.996918Z"
}
}
gurl \
-X GET \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/equipment/d24725b1-d021-4622-ac7a-10134689a4c1"
{
"id": "d24725b1-d021-4622-ac7a-10134689a4c1",
"category": "selfpropelled",
"name": "mySprayer",
"type": "sprayer",
"width": 12.34,
"units": 56.0,
"brand": "brand",
"make": "make",
"model": "model",
"purpose": "Harvesting",
"machine_hours":1000,
"beacon":{
"serial": "4A05",
"proximity_uuid":"5554faa0-1234-4ffd-883a-2d9baadbf873",
"major":11,
"minor":11,
"distance":0
},
"latest_location": {
"device_id":"e82fb2a0-6141-466d-b11e-1f794cbc7b16",
"long":65.75,
"lat": 30.25,
"heading": 1.2,
"speed": 33.5,
"created_at": "2016-03-04T19:53:05.996918Z"
}
}
HTTP Request
GET /equipment/:id
HTTP Response
- 200: OK.
- 400: Bad request.
- 500: Internal error.
Edit equipment
Edit equipment by equipment ID
PUT /equipment/d24725b1-d021-4622-ac7a-10134689a4c1 HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"name": "mySprayer_update",
"width": 12.345,
"units": 56.789,
"brand": "brand_update",
"make": "make_update",
"model": "model_update"
}
HTTP/1.1 202 Accepted
Connection: Close
gurl \
-X PUT \
-d '{
"name": "mySprayer_update",
"width": 12.345,
"units": 56.789,
"brand": "brand_update",
"make": "make_update",
"model": "model_update"
}' \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/equipment/d24725b1-d021-4622-ac7a-10134689a4c1"
HTTP Request
PUT /equipment/:id
HTTP Response
- 202: Accepted.
- 500: Internal error.
Delete equipment
Delete equipment by equipment ID
DELETE /equipment/d24725b1-d021-4622-ac7a-10134689a4c1 HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 204 NOCONTENT
Connection: Close
The request of deleting an equipment:
gurl \
-X DELETE \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/equipment/d24725b1-d021-4622-ac7a-10134689a4c1"
HTTP Request
DELETE /equipment/:id
HTTP Response
- 204: NoContent.
- 500: Internal error.
List Catalog
Listing a catalog of all categories, types and purposes for equipment.
GET /equipment_catalog HTTP/1.1
HOST: api.leaf.ag
HTTP/1.1 200 OK
Content-Type: application/json
{
"misc": [
{
"type": "selfpropelled",
"purposes": [
"Harvesting",
"Spreading",
"Spraying",
"Planting",
"Tilling",
"Transporting",
"Cultivating",
"Seeding"
]
},
{
"type": "notselfpropelled"
}
]
}
The request:
gurl \
-X GET \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/equipment_catalog
The response:
{
...
"misc": [
{
"type": "selfpropelled",
"purposes": [
"Harvesting",
"Spreading",
"Spraying",
"Planting",
"Tilling",
"Transporting",
"Cultivating",
"Seeding"
]
}, {
"type": "notselfpropelled"
}
]
}
HTTP Request
GET /equipment_catalog
HTTP Response
- 200: Ok.
- 400: Bad request.
- 500: Internal error.
ManagementZones
All managementzone endpoints must be prefixed with /resources/v1
Eventually, when breaking changes are deployed we will create new versions and update this document accordingly.
All requests and responses must be JSON-encoded.
Another point that clients need to remember is that all coordinates are specified as [lng, lat].
This is the way that the default coordinate system of GeoJSON and PostGIS work.
For the coordinations of polygon in all endpoints, we require it to be an array of coordinate arrays. For polygons with multiple rings, the first must be the exterior ring and any others must be interior rings or holes.
Here we have two examples about how to provide coordinates for polygon.

The boundary of polygon is [[[5,2],[4,8],[9,10],[13,5],[10,1]]]

The exterior ring is [[5,2],[4,8],[9,10],[13,5],[10,1]]
The interior ring is [[6,5],[7,7],[10,5],[8,3]]
The boundary of polygon is [ [[5,2],[4,8],[9,10],[13,5],[10,1]], [[6,5],[7,7],[10,5],[8,3]] ]
For the coordinations, we require them to be a pair of longitude and latitude. It is a standard GIS formating for POSTGIS. You can find more infomation about How POSTGIS handle GIS data here.
Add ManagementZone
The available categories of managementzone are farm, field, keylocation.
The request of POST a farm:
POST /managementzones HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"category": "farm",
"name": "myFarm",
"area": 1000.0,
"area_units": "acres",
"tags": ["tag1", "tag2"]
}
The request of POST a field:
POST /managementzones HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"category":"field",
"name":"peach field",
"area":100.0,
"area_units":"acres",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"boundary_type": "polygon",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"lease_date": "2016-01-02T15:04:05-07:00",
"owned_date": "2016-01-02T15:04:05-07:00",
"farm_id": "fbc1a684-da0e-42c8-a888-027fe820a729",
"tags": ["tag1", "tag2"]
}
The request of POST a keylocation:
POST /managementzones HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"category":"keylocation",
"name":"keylocation",
"location_type":"shops",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"boundary_type":"polygon",
"farm_ids":["fbc1a684-da0e-42c8-a888-027fe820a729"],
"field_ids":["aff286ff-a079-4918-b185-e9800de11e51"]
}
The response of POST a management zone:
HTTP/1.1 201 CREATED
Connection: Close
The request of POST a farm:
gurl \
-X POST \
-d '{
"category": "farm",
"name": "myFarm",
"area": 1000.0,
"area_units": "acres"
}' \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/managementzones
The request of POST a field:
gurl \
-X POST \
-d '{
"category":"field",
"name":"peach field",
"area":100.0,
"area_units":"acres",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"boundary_type": "polygon",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"lease_date": "2016-01-02T15:04:05-07:00",
"owned_date": "2016-01-02T15:04:05-07:00",
"farm_id": "fbc1a684-da0e-42c8-a888-027fe820a729",
"tags": ["tag1", "tag2"]
}' \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/managementzones
The request of POST a keylocation:
gurl \
-X POST \
-d '{
"category":"keylocation",
"name":"keylocation",
"location_type":"shops",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"boundary_type":"polygon",
"farm_ids":["fbc1a684-da0e-42c8-a888-027fe820a729"],
"field_ids":["aff286ff-a079-4918-b185-e9800de11e51"]
}' \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/managementzones
HTTP Request
POST /managementzones
Post parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| category | string | Yes | The managementzone’s category. The available categories are farm, field, keylocation. |
| name | string | Yes | The managementzone’s name. |
| tags | []string | No | The managementzone’s tags. |
| area | string | Depends | The managementzone’s area. This field is required if catagory is field. |
| area_units | string | Depends | The managementzone’s area_units. the value is “acres”. This field is required if category is field. |
| boundary_type | string | Depends | The managementzone’s boundary type. The availble types of boundary are polygon, point, circle. This field is required if category is field or keylocation. |
| boundary | [][][]float64 | Depends | The managementzone’s polygon coordinates. The units of coordinates is degree. This field is required if boundary_type is polygon and category is field or keylocation. |
| pivot | []float64 | Depends | The managementzone’s circle pivot coordinates. The units of coordinates is degrees. This field is required if boundary_type is circle and category is field or keylocation. |
| radius | float64 | Depends | The managementzone’s circle radius. This field is required if boundary_type is circle and category is field or keylocation. |
| radius_units | string | Depends | The managementzone’s circle radius units. The value is “feet”. This field is required if boundary_type is circle and category is field or keylocation. |
| point | []float64 | Depends | The managementzone’s point coordinates. The units of coordinates is degrees. This field is required if boundary_type is point and category is field or keylocation. |
| soil_type | string | No | The managementzone’s soil type. The available types of soil are sandy, sandy loam, loam, clay loam, clay. This field is being used only when category is field. |
| irrigation_type | string | No | The managementzone’s irrigation type. The available types of irrigation are pivot, flood, drip, none, furrow, wheelline, handline. This field is being used only when category is field. |
| lease_date | date | No | The managementzone’s lease date. This field is being used only when category is field. |
| lease_length | int | No | The managementzone’s lease length in years. The value is years. This field is being used only when category is field. |
| owned_date | date | No | The managementzone’s bought or owned date. This field is being used only when category is field. |
| farm_id | string | No | The uuid of farm, point to a related farm. This field is being used when category is field. |
| farm_ids | []string | No | The uuid of farms, point to a group of related farms. This field is begin used when category is keylocation. |
| field_ids | []string | No | The uuid of field, point to a group of related fields. This field is being used when category is keylocation. |
| location_type | string | Depends | The managementzone’s location type. This field is required if category is keylocation. The avaible types of location are obstable/hazard, equipment yards, entry points, crop storage, scale, office, shops, pumps, canals, weirs, corrals, livestock, service providers, lagoons, silage pits, water storage. |
HTTP Response
- 201: Management Zone created.
- 400: Bad request.
- 500: Internal error.
Response Body
JSON-encoded string containing the ID of the new management zone.
Example: "8EF57207-9D29-4C4F-8954-FCEFFED272A7"
List ManagementZone
List basic information of all existing management zones of the Leaf system by company ID.
Use “Get MangementZone” to get detailed infomation of a single management zone.
GET /managementzones HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": "fbc1a684-da0e-42c8-a888-027fe820a729",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "farm",
"name": "peach farm",
"area": 100,
"area_units": "acres",
"field_ids": [
"b3793211-c2b6-413a-93b9-1563ab1c2cae",
"ebeb6896-6b45-452b-b4b3-fefcc67477b9"
]
}
{
"id": "ebeb6896-6b45-452b-b4b3-fefcc67477b9",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "field",
"name": "peach field",
"area": 100,
"area_units": "acres",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"owned_date": "2016-01-02T15:04:05Z",
"boundary_type": "polygon",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"farm_id": "fbc1a684-da0e-42c8-a888-027fe820a729"
},
{
"id": "b3793211-c2b6-413a-93b9-1563ab1c2cae",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "field",
"name": "peach field",
"area": 100,
"area_units": "acres",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"owned_date": "2016-01-02T15:04:05Z",
"boundary_type": "polygon",
"boundary": [[[-121.569857,32.884522],[-121.569946,32.883295],[-121.571533,32.883259],[-121.571508,32.884604]]],
"farm_id": "fbc1a684-da0e-42c8-a888-027fe820a729"
},
{
"id": "50533bf3-ba38-41b5-b914-f40bc320cf4e",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "keylocation",
"name": "keylocation",
"boundary_type": "polygon",
"boundary": [[[-131.569857,32.884522],[-131.569946,32.883295],[-131.571533,32.883259],[-131.571508,32.884604]]],
"field_ids": [
"aff286ff-a079-4918-b185-e9800de11e51"
],
"farm_ids": [
"fbc1a684-da0e-42c8-a888-027fe820a729"
]
}
]
The request:
gurl \
-X GET \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/managementzones
The response:
[
{
"id": "fbc1a684-da0e-42c8-a888-027fe820a729",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "farm",
"name": "peach farm",
"area": 100,
"area_units": "acres",
"field_ids": [
"b3793211-c2b6-413a-93b9-1563ab1c2cae",
"ebeb6896-6b45-452b-b4b3-fefcc67477b9"
]
}
{
"id": "ebeb6896-6b45-452b-b4b3-fefcc67477b9",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "field",
"name": "peach field",
"area": 100,
"area_units": "acres",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"owned_date": "2016-01-02T15:04:05Z",
"boundary_type": "polygon",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"farm_id": "fbc1a684-da0e-42c8-a888-027fe820a729"
},
{
"id": "b3793211-c2b6-413a-93b9-1563ab1c2cae",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "field",
"name": "peach field",
"area": 100,
"area_units": "acres",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"owned_date": "2016-01-02T15:04:05Z",
"boundary_type": "polygon",
"boundary": [[[-121.569857,32.884522],[-121.569946,32.883295],[-121.571533,32.883259],[-121.571508,32.884604]]],
"farm_id": "fbc1a684-da0e-42c8-a888-027fe820a729"
},
{
"id": "50533bf3-ba38-41b5-b914-f40bc320cf4e",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "keylocation",
"name": "keylocation",
"boundary_type": "polygon",
"boundary": [[[-131.569857,32.884522],[-131.569946,32.883295],[-131.571533,32.883259],[-131.571508,32.884604]]],
"field_ids": [
"aff286ff-a079-4918-b185-e9800de11e51"
],
"farm_ids": [
"fbc1a684-da0e-42c8-a888-027fe820a729"
]
}
]
HTTP Request
GET /managementzones
HTTP Response
- 200: Ok.
- 400: Bad request.
- 500: Internal error.
Response payload
| Parameter | Type | Description |
|---|---|---|
| id | string | The current managementzone’s ID. |
| user_id | string | The user’s ID. |
| company_id | integer | The company’s ID. |
| category | string | The current managementzone’s category. The avaiable categories are farm, field, keylocation. |
| name | string | The current managementzone’s name. |
| area | float64 | The current managementzone’s area. |
| area_units | string | The current managementzone’s area units. The value is “acres”. |
| boundary_type | string | The current managementzone’s boundary type. The availble types of boundary are polygon, point, circle. This field is being used when category is field or keylocation. |
| boundary | [][][]float64 | The current managementzone’s polygon coordinates. The units of coordinatesis degree. This field is being used when category is field or keylocation and boundary_type is polygon. |
| pivot | []float64 | The current managementzone’s pivot coordinates. The units of coordinatesis degree. This field is being used when category is field or keylocation and boundary_type is circle. |
| radius | float64 | The current managementzone’s radius. This field is being used when category is field or keylocation and boundary_type is circle. |
| radius_units | string | The current managementzone’s radius units. The value is “acres”. This field is being used when category is field or keylocation and boundary_type is circle. |
| point | []float64 | The current managementzone’s point coordinates. The units of coordinatesis degree. This field is being used when category is field or keylocation and boundary_type is point. |
| soil_type | string | The current managementzone’s soil type. The available types of soil are sandy, sandy loam, loam, clay loam, clay. This field is being used when category is field. |
| irrigation_type | string | The current managementzone’s irrigation type. The available types of irrigation are pivot, flood, drip, none, furrow, wheelline, handline. This field is being used when category is field. |
| lease_date | date | The current managementzone’s lease date. The managementzone’s lease date. This field is being used when category is field. |
| lease_length | int | The current managementzone’s lease length. The valus is years. The managementzone’s lease length in years. This field is being used when category is field. |
| owned_date | date | The current managementzone’s owned date. The managementzone’s bought or owned date. This field is being used when category is field. |
| farm_id | string | The uuid of farm, point to a related farm. This field is being used when category is field. |
| farm_ids | []string | The uuid of farms, point to a group of related farms. This field is begin used when category is keylocation. |
| field_ids | []string | The uuid of field, point to a group of related fields. This field is being used when category is keylocation. |
| location_type | string | The current managementzone’s location type. This field is required if category is keylocation. The avaible types of location are obstable/hazard, equipment yards, entry points, crop storage, scale, office, shops, pumps, canals, weirs, corrals, livestock, service providers, lagoons, silage pits, water storage. |
Get ManagementZone
Get the detailed management zone by management zone ID.
GET /managementzones/d24725b1-d021-4622-ac7a-10134689a4c1 HTTP/1.1
Host: api.leaf.ag
The response of GET a farm.
HTTP/1.1 200 OK
Content-Type: application/json
{
"id":"74a1795d-b09f-455d-b287-85dd879418ce",
"user_id":"c2c883b1-6ee5-4d21-a476-42006e85d9f1",
"company_id":14,
"category":"farm",
"name":"peach farm",
"area":100,
"area_units":"acres",
"fields":[
{
"mangementzone_id":"eb44ecb2-4e06-4f9a-aaab-5105675b4bed",
"user_id":"c2c883b1-6ee5-4d21-a476-42006e85d9f1",
"company_id":14,
"category":"field",
"name":"peach field",
"area":100,
"area_units":"acres",
"soil_type":"clay",
"irrigation_type":"pivot",
"lease_length":4,
"lease_date":"2016-01-02T15:04:05Z",
"owned_date":"2016-01-02T15:04:05Z",
"boundary_type":"polygon",
"boundary":[[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"farm_id":"74a1795d-b09f-455d-b287-85dd879418ce"
}
],
"tags":["tag1","tag2"],
"created_at":"2016-01-25T15:56:17.608299Z",
"updated_at":"2016-01-25T15:56:17.608299Z"
}
The response of GET a field.
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "eb44ecb2-4e06-4f9a-aaab-5105675b4bed",
"user_id": "c2c883b1-6ee5-4d21-a476-42006e85d9f1",
"company_id": 14,
"category": "field",
"name": "peach field",
"area": 100,
"area_units": "acres",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"lease_date": "2016-01-02T15:04:05Z",
"owned_date": "2016-01-02T15:04:05Z",
"boundary_type": "polygon",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"farm_id": "74a1795d-b09f-455d-b287-85dd879418ce",
"tags": ["tag1","tag2"]
}
The response of GET a keylocation.
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "50533bf3-ba38-41b5-b914-f40bc320cf4e",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "keylocation",
"name": "keylocation",
"lease_date": "0001-01-01T00:00:00Z",
"owned_date": "0001-01-01T00:00:00Z",
"location_type": "shops",
"boundary_type": "polygon",
"boundary": [[[-121.569857,32.884522],[-121.569946,32.883295],[-121.571533,32.883259],[-121.571508,32.884604]]],
"fields": [
{
"id": "aff286ff-a079-4918-b185-e9800de11e51",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "field",
"name": "peach field",
"area": 100,
"area_units": "acres",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"lease_date": "2016-01-02T15:04:05Z",
"owned_date": "2016-01-02T15:04:05Z",
"boundary_type": "polygon",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"farm_id": "2545123c-fbe5-4e2e-9d25-1d2bd55dc25d"
}
],
"farms": [
{
"id": "fbc1a684-da0e-42c8-a888-027fe820a729",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "farm",
"name": "peach farm",
"area": 100,
"area_units": "acres"
}
]
}
The request:
gurl \
-X GET \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/managementzones/d24725b1-d021-4622-ac7a-10134689a4c1
The response of GET a farm:
{
"id":"74a1795d-b09f-455d-b287-85dd879418ce",
"user_id":"c2c883b1-6ee5-4d21-a476-42006e85d9f1",
"company_id":14,
"category":"farm",
"name":"peach farm",
"area":100,
"area_units":"acres",
"fields":[
{
"id":"eb44ecb2-4e06-4f9a-aaab-5105675b4bed",
"user_id":"c2c883b1-6ee5-4d21-a476-42006e85d9f1",
"company_id":14,
"category":"field",
"name":"peach field",
"area":100,
"area_units":"acres",
"soil_type":"clay",
"irrigation_type":"pivot",
"lease_length":4,
"lease_date":"2016-01-02T15:04:05Z",
"owned_date":"2016-01-02T15:04:05Z",
"boundary_type":"polygon",
"boundary":[[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"farm_id":"74a1795d-b09f-455d-b287-85dd879418ce"
}
],
"tags":["tag1","tag2"],
"created_at":"2016-01-25T15:56:17.608299Z",
"updated_at":"2016-01-25T15:56:17.608299Z"
}
The response of GET a field:
{
"id": "eb44ecb2-4e06-4f9a-aaab-5105675b4bed",
"user_id": "c2c883b1-6ee5-4d21-a476-42006e85d9f1",
"company_id": 14,
"category": "field",
"name": "peach field",
"area": 100,
"area_units": "acres",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"lease_date": "2016-01-02T15:04:05Z",
"owned_date": "2016-01-02T15:04:05Z",
"boundary_type": "polygon",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"farm_id": "74a1795d-b09f-455d-b287-85dd879418ce",
"tags": ["tag1","tag2"]
}
The response of GET a keylocation:
{
"id": "50533bf3-ba38-41b5-b914-f40bc320cf4e",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "keylocation",
"name": "keylocation",
"lease_date": "0001-01-01T00:00:00Z",
"owned_date": "0001-01-01T00:00:00Z",
"location_type": "shops",
"boundary_type": "polygon",
"boundary": [[[-121.569857,32.884522],[-121.569946,32.883295],[-121.571533,32.883259],[-121.571508,32.884604]]],
"fields": [
{
"id": "aff286ff-a079-4918-b185-e9800de11e51",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "field",
"name": "peach field",
"area": 100,
"area_units": "acres",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"lease_date": "2016-01-02T15:04:05Z",
"owned_date": "2016-01-02T15:04:05Z",
"boundary_type": "polygon",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"farm_id": "2545123c-fbe5-4e2e-9d25-1d2bd55dc25d"
}
],
"farms": [
{
"id": "fbc1a684-da0e-42c8-a888-027fe820a729",
"user_id": "7de7190f-571e-453a-90f7-966b3b4b1614",
"company_id": 1,
"category": "farm",
"name": "peach farm",
"area": 100,
"area_units": "acres"
}
]
}
HTTP Request
GET /managementzones/:id
HTTP Response
- 200: OK.
- 400: Bad request.
- 500: Internal error.
Response Payload
| Parameter | Type | Description |
|---|---|---|
| id | string | The current managementzone’s ID. |
| user_id | string | The user’s ID. |
| company_id | string | The company’s ID. |
| category | string | The current managementzone’s category. The available categories are farm, field, keylocation. |
| name | string | The current managementzone’s name. |
| tags | []string | The current managementzone’s tags. |
| area | string | The current managementzone’s area. This field is being used when catagory is farm or field. |
| area_units | string | The current managementzone’s area units. The value is “feet”. his field is being used when category is farm or field. |
| boundary_type | string | The current managementzone’s boundary type. This field is being used when category is field or keylocation. |
| boundary | [][][]float64 | The current managementzone’s polygon coordinates. The units of coordinatesis degree. This field is being used when category is field or keylocation and boundary_type is polygon. |
| pivot | []float64 | The current managementzone’s circle pivot coordinates. The units of coordinatesis degree. This field is being used when category is field or keylocation and boundary_type is circle. |
| radius | float64 | The current managementzone’s cicle radius. This field is being used when category is field or keylocation and boundary_type is circle. |
| radius_units | string | The current managementzone’s circle radius units. This field is being used when category is field or keylocation and boundary_type is circle. |
| point | []float64 | The current managementzone’s point coordinates. The units of coordinatesis degree. This field is being used when category is field or keylocation and boundary_type is point. |
| soil_type | string | The current managementzone’s soil type. The available types of soil are sandy, sandy loam, loam, clay loam, clay. This field is being used when category is field. |
| irrigation_type | string | The current managementzone’s irrigation type. The available types of irrigation are pivot, flood, drip, none, furrow, wheelline, handline. This field is being used when category is field. |
| lease_date | date | The current managementzone’s lease date. This field is being used when category is field. |
| lease_length | int | The current managementzone’s lease length in years. The value is years. This field is being used when category is field. |
| owned_date | date | The current managementzone’s bought or owned date. This field is being used when category is field. |
| farm_id | string | The uuid of farm, point to a related farm. This field is being used when category is field. |
| farm_ids | []string | The uuid of farms, point to a group of related farms. This field is being used when category is keylocation. |
| field_ids | []string | The uuid of fields, point to a group of related fields. This field is being used when category is keylocation. |
| location_type | string | The current managementzone’s location type. The avaible types of location are obstable/hazard, equipment yards, entry points, crop storage, scale, office, shops, pumps, canals, weirs, corrals, livestock, service providers, lagoons, silage pits, water storage. This field is being used if category is keylocation. |
Edit ManagementZone
Edit management zone by management zone ID
The request of PUT a farm:
PUT /managementzones/d24725b1-d021-4622-ac7a-10134689a4c1 HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"category":"farm",
"name":"peach farm",
"area":100.0,
"area_units":"acres",
"tags": ["tag1", "tag2"]
}
The request of PUT a field:
PUT /managementzones/d24725b1-d021-4622-ac7a-10134689a4c1 HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"category":"field",
"name":"peach field",
"area":100.0,
"area_units":"acres",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"boundary_type": "polygon",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"lease_date": "2016-01-02T15:04:05-07:00",
"owned_date": "2016-01-02T15:04:05-07:00",
"farm_id": "fbc1a684-da0e-42c8-a888-027fe820a729",
"tags": ["tag1", "tag2"]
}
The request of PUT a keylocation:
PUT /managementzones/d24725b1-d021-4622-ac7a-10134689a4c1 HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"category":"keylocation",
"name":"keylocation",
"location_type":"shops",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"boundary_type":"polygon",
"farm_ids":["fbc1a684-da0e-42c8-a888-027fe820a729"],
"field_ids":["aff286ff-a079-4918-b185-e9800de11e51"]
}
The response of PUT a management zone:
HTTP/1.1 202 Accepted
Connection: Close
The request of PUT a farm:
gurl \
-X PUT \
-d '{
"category":"farm",
"name":"peach farm",
"area":100.0,
"area_units":"acres",
"tags": ["tag1", "tag2"]
}' \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/managementzones/d24725b1-d021-4622-ac7a-10134689a4c1
The request of PUT a field:
gurl \
-X PUT \
-d '{
"category":"field",
"name":"peach field",
"area":100.0,
"area_units":"acres",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"boundary_type": "polygon",
"soil_type": "clay",
"irrigation_type": "pivot",
"lease_length": 4,
"lease_date": "2016-01-02T15:04:05-07:00",
"owned_date": "2016-01-02T15:04:05-07:00",
"farm_id": "fbc1a684-da0e-42c8-a888-027fe820a729",
"tags": ["tag1", "tag2"]
}' \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/managementzones/d24725b1-d021-4622-ac7a-10134689a4c1
The request of PUT a keylocation:
gurl \
-X PUT \
-d '{
"category":"keylocation",
"name":"keylocation",
"location_type":"shops",
"boundary": [[[-111.569857,32.884522],[-111.569946,32.883295],[-111.571533,32.883259],[-111.571508,32.884604]]],
"boundary_type":"polygon",
"farm_ids":["fbc1a684-da0e-42c8-a888-027fe820a729"],
"field_ids":["aff286ff-a079-4918-b185-e9800de11e51"]
}' \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/managementzones/d24725b1-d021-4622-ac7a-10134689a4c1
HTTP Request
PUT /managementzones/:id
PUT parameters
| Parameter | Type | Description |
|---|---|---|
| name | string | The managementzone’s name. |
| tags | []string | The managementzone’s tags. |
| area | string | The managementzone’s area. This field is required if catagory is field. |
| area_units | string | The managementzone’s area_units. The value is “acres”. This field is required if category is field. The available units are acres and hectares. |
| boundary_type | string | The managementzone’s boundary type. The availble types of boundary are polygon, point, circle. This field is required if category is field or keylocation. |
| boundary | [][][]float64 | The managementzone’s polygon coordinates. The units of coordinatesis degree. This field is required if boundary_type is polygon and category is field or keylocation. |
| pivot | []float64 | The managementzone’s circle pivot cordinates. The units of coordinatesis degree. This field is required if boundary_type is circle and category is field or keylocation. |
| radius | float64 | The managementzone’s circle radius. This field is required if boundary_type is circle and category is field or keylocation. |
| radius_units | string | The managementzone’s circle radius units. The availble types of radius units are meters and feet. This field is required if boundary_type is circle and category is field or keylocation. |
| point | []float64 | The managementzone’s point coordinates. The units of coordinatesis degree. This field is required if boundary_type is point and category is field or keylocation. |
| soil_type | string | The managementzone’s soil type. The available types of soil are sandy, sandy loam, loam, clay loam, clay. This field is being used only when category is field. |
| irrigation_type | string | The managementzone’s irrigation type. The available types of irrigation are pivot, flood, drip, none, furrow, wheelline, handline. This field is being used only when category is field. |
| lease_date | date | The managementzone’s lease date. This field is being used only when category is field. |
| lease_length | int | The managementzone’s lease length in years. The value is years. This field is being used only when category is field. |
| owned_date | date | The managementzone’s bought or owned date. This field is being used only when category is field. |
| farm_id | string | The uuid of farm, point to a related farm. This field is being used when category is field. |
| farm_ids | []string | The uuid of farms, point to a group of related farms. This field is begin used when category is keylocation. |
| field_ids | []string | The uuid of field, point to a group of related fields. This field is being used when category is keylocation. |
| location_type | string | The managementzone’s location type. This field is required if category is keylocation. The avaible types of location are obstable/hazard, equipment yards, entry points, crop storage, scale, office, shops, pumps, canals, weirs, corrals, livestock, service providers, lagoons, silage pits, water storage. |
HTTP Response
- 202: Accepted.
- 500: Internal error.
Delete ManagementZone
Delete management zone by management zone ID
DELETE /managementzones/d24725b1-d021-4622-ac7a-10134689a4c1 HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 204 NoContent
Connection: Close
gurl \
-X DELETE \
-key $API_KEY \
-secret $API_SECRET \
https://api.leaf.ag/resources/v1/managementzones/d24725b1-d021-4622-ac7a-10134689a4c1
HTTP Request
DELETE /managementzones/:id
HTTP Response
- 204: NoContent.
- 500: Internal error.
Health
Checks the health of this service.
GET /health HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 200 OK
Connection: Close
The request:
gurl \
-X GET \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/health"
HTTP Request
GET /health
HTTP Response:
- 200: OK.
- 500: Internal error.
Crops
All crops endpoints must be prefixed with /resources/v1.
All requests and responses are JSON-encoded.
Create Crop
gurl -X POST -key $API_KEY -secret $API_SECRET \
-d '{
"name": "test crop 2",
"crop": "test_crop_2",
"heat_unit": "celsius",
"projected_yield": "mucho",
"bag_info": {
"cost": 1.1,
"seed_density": 2.2,
"weight": 3.3
},
"commodities_index": "NTW",
"market_price": 32.2,
"water_requirements": "a lot",
"chemicals_used": "nitrate,water",
"seed_info": {
"depth": 21.1,
"spacing": 42.2,
"population": 84.4
},
"ideal_soil": "ground",
"vendor": "john",
"type": "feed"
}' \
"https://api.leaf.ag/resources/v1/crops"
POST /crops HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
{
"name": "test crop 2",
"crop": "test_crop_2",
"heat_unit": "celsius",
"projected_yield": "mucho",
"bag_info": {
"cost": 1.1,
"seed_density": 2.2,
"weight": 3.3
},
"commodities_index": "NTW",
"market_price": 32.2,
"water_requirements": "a lot",
"chemicals_used": "nitrate,water",
"seed_info": {
"depth": 21.1,
"spacing": 42.2,
"population": 84.4
},
"ideal_soil": "ground",
"vendor": "john",
"type": "feed"
}
HTTP/1.1 201 CREATED
Connection: Close
Create Crops resource.
Create crops resource in the system.
HTTP Request
POST /crops
Parameters
Crops
| Parameter | Type | Required | Notes |
|---|---|---|---|
| name | string | yes | |
| crop | string | yes | |
| heat_unit | string | no | |
| projected_yield | string | no | |
| bag_info | baginfo object | no | see baginfo object for details |
| commodities_index | string | no | |
| market_price | float | no | |
| water_requirements | string | no | |
| chemicals_used | string | no | |
| seed_info | seedinfo object | no | see seedinfo object for details |
| ideal_soil | string | no | |
| type | string | yes |
BagInfo
| Parameter | Type | Required | Notes |
|---|---|---|---|
| cost | float | no | |
| seed_density | float | no | |
| weight | float | no |
SeedInfo
| Parameter | Type | Required | Notes |
|---|---|---|---|
| depth | float | no | |
| spacing | float | no | |
| population | float | no |
HTTP Response
- 201: Created
- 401: Unauthorized
- 500: Internal error
Notes
All notes endpoints must be prefixed with /resources/v1.
All requests and responses are JSON-encoded.
Create Notes
gurl -X POST -key $API_KEY -secret $API_SECRET \
-d '{
"target_resource_id": "75ee5bf5-1f72-46be-938a-b8610bf9bd70",
"target_resource_type": "users",
"content": "hello!"
}' "https://api.leaf.ag/resources/v1/notes"
POST /notes HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
{
"target_resource_id": "75ee5bf5-1f72-46be-938a-b8610bf9bd70",
"target_resource_type": "users",
"content": "hello!"
}
HTTP/1.1 201 CREATED
Content-Type: application/json
Connection: Close
"2248bf63-b101-4fc8-b372-5042733f714a"
Create Notes resource.
Create Notes resource in the system.
HTTP Request
POST /notes
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | no | Identifier of the note. |
| content | string | yes | Content of the note. |
| target_resource_type | string | yes | Type of resource linked to the note. |
| target_resource_id | string | yes | Resource Identifier linked to the note. |
| origin_resource_type | string | no | Always “users” for in the notes context. |
| origin_resource_id | string | no | user_id for the notes context. |
HTTP Response
- 201: Created
- 401: Unauthorized
- 500: Internal error
Get Notes
gurl -X GET -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/resources/v1/notes/2248bf63-b101-4fc8-b372-5042733f714a"
GET /notes/2248bf63-b101-4fc8-b372-5042733f714a HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
HTTP/1.1 200 OK
Content-Type: application/json
Connection: Close
{
"id": "2248bf63-b101-4fc8-b372-5042733f714a",
"target_resource_id": "75ee5bf5-1f72-46be-938a-b8610bf9bd70",
"target_resource_type": "users",
"origin_resource_id": "3de5ba2c-df30-48f0-833b-5ee54bc25c88",
"origin_resource_type": "users",
"content": "hello!"
}
Fetch Notes resource.
Get a Note resource from the system.
HTTP Request
GET /notes/:id
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | yes | Identifier of the note. |
| content | string | yes | Content of the note. |
| target_resource_type | string | yes | Type of resource linked to the note. |
| target_resource_id | string | yes | Resource Identifier linked to the note. |
| origin_resource_type | string | yes | Always “users” for in the notes context. |
| origin_resource_id | string | yes | user_id for the notes context. |
| created_at | RFC3339 timestamp | yes | The time the note was created. |
| updated_at | RFC3339 timestamp | no | The time the note was last updated. |
HTTP Response
- 200: OK
- 401: Unauthorized
- 500: Internal error
Update Notes
gurl -X PUT -key $API_KEY -secret $API_SECRET \
-d '{
"target_resource_id": "75ee5bf5-1f72-46be-938a-b86100000000",
"target_resource_type": "users",
"content": "hello world!"
}' "https://api.leaf.ag/resources/v1/note/2248bf63-b101-4fc8-b372-5042733f714a"
PUT /notes/2248bf63-b101-4fc8-b372-5042733f714a HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
{
"target_resource_id": "75ee5bf5-1f72-46be-938a-b86100000000",
"target_resource_type": "users",
"content": "hello world!"
}
HTTP/1.1 202 ACCEPTED
Connection: Close
Update Notes resource.
Update Notes resource in the system.
HTTP Request
PUT /notes/:id
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| content | string | no | Content of the note. |
| target_resource_type | string | no | Type of resource linked to the note. |
| target_resource_id | string | no | Resource Identifier linked to the note. |
| origin_resource_type | string | N/A | It is not possible at the moment to update the origin type. |
| origin_resource_id | string | N/A | It is not possible at the moment to update the origin id. |
HTTP Response
- 202: Accepted
- 401: Unauthorized
- 500: Internal error
Delete Notes
gurl -X DELETE -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/resources/v1/notes/75ee5bf5-1f72-46be-938a-b86100000000"
POST /notes/75ee5bf5-1f72-46be-938a-b86100000000 HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
HTTP/1.1 204 NO CONTENT
Connection: Close
Delete Notes resource.
Delete Notes resource in the system.
HTTP Request
DELETE /notes/:id
HTTP Response
- 204: Deleted
- 401: Unauthorized
- 500: Internal error
List Notes
# List all notes
gurl -X GET -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/resources/v1/notes"
# List a targeted note (similar to get by id)
gurl -X GET -key $API_KEY -secret $API_SECRET \
-d '{
"target_resource_id": "2248bf63-b101-4fc8-b372-5042733f714a",
"target_resource_type": "users"
}' "https://api.leaf.ag/resources/v1/notes"
# List all notes for all equipments.
gurl -X GET -key $API_KEY -secret $API_SECRET \
-d '{
"target_resource_type": "equipments"
}' "https://api.leaf.ag/resources/v1/notes"
GET /notes HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
{
"target_resource_id": "75ee5bf5-1f72-46be-938a-b8610bf9bd70",
"target_resource_type": "users"
}
HTTP/1.1 200 OK
Content-Type: application/json
Connection: Close
[{
"id": "2248bf63-b101-4fc8-b372-5042733f714a",
"target_resource_id": "75ee5bf5-1f72-46be-938a-b8610bf9bd70",
"target_resource_type": "users",
"origin_resource_id": "3de5ba2c-df30-48f0-833b-5ee54bc25c88",
"origin_resource_type": "users",
"content": "hello!"
}]
List Notes resource.
List Notes resource in the system.
NOTE: As we don’t have a filtering system yet, we use the payload on a HTTP GET to filter.
HTTP Request
GET /notes
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | no | Filter on the note identifier (similar to get by id). |
| target_resource_type | string | no | Filter on the type of resource linked to the note. |
| target_resource_id | string | no | Filter on the resource Identifier linked to the note. |
| origin_resource_type | string | no | Filter on the origin resource type. (Always “users” for in the notes context.) |
| origin_resource_id | string | no | Filter on the origin resource Identifier. (user_id for the notes context.) |
| created_at | RFC3339 timestamp | yes | The time the note was created. |
| updated_at | RFC3339 timestamp | no | The time the note was last updated. |
HTTP Response
- 200: OK
- 401: Unauthorized
- 500: Internal error
Activity
All activity endpoints must be prefixed with /activities.
All requests and responses are JSON-encoded.
All activities must have a corresponding (real) operation, accessible with the operation’s ID.
Create Activity
gurl -X POST -key $API_KEY -secret $API_SECRET \
-d '{
"device_id":"75ee5bf5-1f72-46be-938a-b8610bf9bd70",
"type":"farming",
"start":"2016-06-09T18:44:53Z",
"end":"2016-06-09T21:37:01Z",
"managementzone_id":"c6f8374a-4e91-487a-8c3d-696591bf4ffc",
"operator_id":"76093839-2da0-48ec-96c4-c34ab0867d10",
"selfpropelled_id":"6c2c879d-efc2-4de6-98b8-986b60b55659",
"implement_id":"9c5c897d-efc2-4de6-98b8-986b60b55659",
"purpose":"harvesting",
"traveled_distance":1.2,
"operation_id":"8ba0df9d-5598-4b9c-a63c-9f80ef40a776"
}' https://api.leaf.ag/resources/v1/activities
POST /activities HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
{
"device_id":"75ee5bf5-1f72-46be-938a-b8610bf9bd70",
"type":"farming",
"start":"2016-06-09T18:44:53Z",
"end":"2016-06-09T21:37:01Z",
"managementzone_id":"c6f8374a-4e91-487a-8c3d-696591bf4ffc",
"operator_id":"76093839-2da0-48ec-96c4-c34ab0867d10",
"selfpropelled_id":"6c2c879d-efc2-4de6-98b8-986b60b55659",
"implement_id":"9c5c897d-efc2-4de6-98b8-986b60b55659",
"purpose":"harvesting",
"traveled_distance":1.2,
"operation_id":"8ba0df9d-5598-4b9c-a63c-9f80ef40a776"
}
HTTP/1.1 201 CREATED
Content-Type: application/json
Connection: Close
"2248bf63-b101-4fc8-b372-5042733f714a"
Create activity resource.
Create activity in the system.
HTTP Request
POST /activities
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| device_id | uuid | no | Identifier of the device that sent the heartbeats which triggered the activity. |
| type | string | no | Type of activity (stationary, farming, travel). |
| start | string | yes | The start time of the activity (formatted as RFC3339). |
| end | string | yes | The end time of the activity (formatted as RFC3339). |
| managementzone_id | uuid | yes | The management zones associated with the activity. |
| operator_id | uuid | yes | The user ID of the operator that performed the activity. |
| selfpropelled_id | uuid | yes | The selfpropelled that was involved with the activity. |
| implement_id | uuid | yes | The equipment that was involved with the activity. |
| purpose | string | yes | The purpose of the activity (harvesting, planting, etc). |
| traveled_distance | float | no | The distance traveled in miles during the activity. |
| operation_id | string | yes | The associated operation id. |
| algorithm_name | string | yes | we have two algorithm_name avaiable until now (fenster and LeafObserver). |
| algorithm_version | string | yes | we have one algorithm_version avaiable until now (v0). |
HTTP Response
- 201: Created
- 401: Unauthorized
- 500: Internal error
Get Activity
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/activities/2248bf63-b101-4fc8-b372-5042733f714a
GET /activities/2248bf63-b101-4fc8-b372-5042733f714a HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
HTTP/1.1 200 OK
Content-Type: application/json
Connection: Close
{
"device_id":"75ee5bf5-1f72-46be-938a-b8610bf9bd70",
"type":"farming",
"start":"2016-06-09T18:44:53Z",
"end":"2016-06-09T21:37:01Z",
"managementzone_id":"c6f8374a-4e91-487a-8c3d-696591bf4ffc",
"operator_id":"76093839-2da0-48ec-96c4-c34ab0867d10",
"selfpropelled_id":"6c2c879d-efc2-4de6-98b8-986b60b55659",
"implement_id":"9c5c897d-efc2-4de6-98b8-986b60b55659"
"purpose":"harvesting",
"traveled_distance":1.2,
"created_at":"2016-06-09T21:45:20Z"
}
Fetch activity resource.
Get an activity from the system.
HTTP Request
GET /activities/:id
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| device_id | uuid | no | Identifier of the device that sent the heartbeats which triggered the activity. |
| type | string | no | Type of activity (stationary, farming, travel). |
| start | string | yes | The start time of the activity (formatted as RFC3339). |
| end | string | yes | The end time of the activity (formatted as RFC3339). |
| managementzone_id | uuid | yes | The management zones associated with the activity. |
| operator_id | uuid | yes | The user ID of the operator that performed the activity. |
| selfpropelled_id | uuid | yes | The selfpropelled that was involved with the activity. |
| implement_id | uuid | yes | The equipment that was involved with the activity. |
| purpose | string | yes | The purpose of the activity (harvesting, planting, etc). |
| traveled_distance | float | no | The distance traveled in miles during the activity. |
| operation_id | string | yes | The associated operation id. |
| algorithm_name | string | yes | we have two algorithm_name avaiable until now (fenster and LeafObserver). |
| algorithm_version | string | yes | we have one algorithm_version avaiable until now (v0). |
HTTP Response
- 200: OK
- 401: Unauthorized
- 500: Internal error
List Activity
# List all activities
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/activities
# List activities that ended during a two-day period
gurl -X GET -key $API_KEY -secret $API_SECRET -d '{"start":"2016-06-28T16:45:50Z","end":"2016-06-30T16:45:50Z"}' https://api.leaf.ag/resources/v1/activities
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/activities?start_time=2016-06-28T16:45:50Z&end_time=2016-06-30T16:45:50Z
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/activities?start_time=2016-06-28T16:45:50Z&end_time=2016-06-30T16:45:50Z&algorithm_name=historian&algorithm_version=1.7
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/activities?operation_id=8ba0df9d-5598-4b9c-a63c-9f80ef40a776
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/activities?selfpropelled_id=b3176e8c-3bc6-4f4c-8f82-d74bf4d4b7b1
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/activities?implement_id=ced67b14-94bd-48ea-b0c8-a866f3e5e146
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/activities?managementzone_id=80db6847-b70d-4680-9495-a3a9e86c5bda
GET /activities?start=2016-06-10T10:00:00Z&end=2016-06-30T16:45:50Z HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Connection: Close
[
{
"device_id":"75ee5bf5-1f72-46be-938a-b8610bf9bd70",
"type":"farming",
"start":"2016-06-09T18:44:53Z",
"end":"2016-06-09T21:37:01Z",
"managementzone_id":"c6f8374a-4e91-487a-8c3d-696591bf4ffc",
"operator_id":"76093839-2da0-48ec-96c4-c34ab0867d10",
"selfpropelled_id":"6c2c879d-efc2-4de6-98b8-986b60b55659",
"implement_id":"9c5c897d-efc2-4de6-98b8-986b60b55659"
"purpose":"harvesting",
"traveled_distance":1.2,
"created_at":"2016-06-09T21:45:20Z"
}
]
List activity.
List activity in the system.
The activity list can be filtered with query parameters.
The accepted parameters are
| Parameter | Type | Required | Notes |
|---|---|---|---|
| start_time | RFC3339 timestamp | no | Start time for activities |
| end_time | RFC3339 timestamp | no | End time for activities |
| algorithm_name | string | no | Return activities generated by the specified algorithm |
| algorithm_version | string | no | Return activities generated by the specified algorithm version |
| operation_id | string | no | Operation ID for activities |
| selfpropelled_id | uuid | no | Selfpropelled ID for activities |
| implement_id | uuid | no | Implement ID for activities |
| managementzone_id | uuid | no | Managementzone ID for activities |
HTTP Request
GET /activities
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| device_id | uuid | no | Identifier of the device that sent the heartbeats which triggered the activity. |
| type | string | no | Type of activity (stationary, farming, travel). |
| start | string | yes | The start time of the activity (formatted as RFC3339). |
| end | string | yes | The end time of the activity (formatted as RFC3339). |
| managementzone_id | uuid | yes | The management zones associated with the activity. |
| operator_id | uuid | yes | The user ID of the operator that performed the activity. |
| selfpropelled_id | uuid | yes | The selfpropelled that was involved with the activity. |
| implement_id | uuid | yes | The equipment that was involved with the activity. |
| purpose | string | yes | The purpose of the activity (harvesting, planting, etc). |
| traveled_distance | float | no | The distance traveled in miles during the activity. |
| operation_id | string | yes | The associated operation id. |
| algorithm_name | string | yes | we have two algorithm_name avaiable until now (fenster and LeafObserver). |
| algorithm_version | string | yes | we have one algorithm_version avaiable until now (v0). |
HTTP Response
- 200: OK
- 401: Unauthorized
- 500: Internal error
Operation
All operation endpoints must be prefixed with /operations.
All requests and responses are JSON-encoded.
Add Operation
Add a new operation in the Leaf system.
POST /operations HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"name":"test",
"purpose":"tilling",
"managementzone_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"completed_date":"2016-08-01T00:00:00Z",
"active_working_time":"02:00:00",
"total_stationary_time":"00:14:35",
"acres":10.0,
"average_speed":10.0,
"start":"2016-08-01T00:00:00Z",
"end":"2016-08-01T00:00:00Z",
"device_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"selfpropelled_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"implement_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"operator_id":"76093839-2da0-48ec-96c4-c34ab0867d10"
}
HTTP/1.1 201 CREATED
Connection: Close
# Add a new operation.
gurl \
-X POST \
-d '{
"name":"test",
"purpose":"tilling",
"managementzone_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"completed_date":"2016-08-01T00:00:00Z",
"active_working_time":"02:00:00",
"total_stationary_time":"00:14:35",
"acres":10.0,
"average_speed":10.0,
"start":"2016-08-01T00:00:00Z",
"end":"2016-08-01T00:00:00Z",
"device_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"selfpropelled_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"implement_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"operator": { "id": "76093839-2da0-48ec-96c4-c34ab0867d10" }
}' \
-key $API_KEY \
-secret $API_SECRET \
"https://api.leaf.ag/resources/v1/operations"
HTTP Request
POST /operations
Post parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| device_id | uuid | no | Identifier of the device that sent the heartbeats which the source of forming operations. |
| name | string | no | Operation name, the value is from equipment type. |
| purpose | string | yes | Operation purpose, the value is from equipment purpose. |
| start | string | yes | The start time of the operation. (formatted as RFC3339). |
| end | string | yes | The end time of the operation (formatted as RFC3339). |
| completed_date | string | no | The date of operation completed (formatted as RFC3339). |
| active_working_time | string | yes | The active working duration between operation start and end (formatted as HH:MM:SS). |
| total_stationary_time | string | yes | The total stationary time between operation start and end (formatted as HH:MM:SS). |
| managementzone_id | uuid | yes | The management zones associated with the activity. |
| selfpropelled_id | uuid | yes | The selfproplled equipment that was involved with the operation. |
| implement_id | uuid | yes | The implement equipment that was involved with the operation. |
| operator | object | yes | The user that was involved with the activity. |
| acres | float | no | The farming coverage of the operation. |
| average_speed | float | no | The average speed of the operation (the unit is forced to be mph). |
| algorithm_name | string | yes | Operation created by this API will has default ‘leaf’ algorithm name. |
| algorithm_version | string | yes | Operation created by this API will has default 'v0’ algorithm version. |
HTTP Response
- 201: Operation created.
- 400: Bad request.
- 500: Internal error.
Response Body
JSON-encoded string containing the ID of the new operation.
Example: "8EF57207-9D29-4C4F-8954-FCEFFED272A7"
List Operation
# List all operations
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/operations
# List operations that ended during a two-day period
gurl -X GET -key $API_KEY -secret $API_SECRET -d '{"start":2016-06-28T16:45:50Z,"end":2016-06-30T16:45:50Z}' https://api.leaf.ag/resources/v1/operations
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/operations?start_time=2016-06-10T10:00:00Z&end_time=2016-06-30T16:45:50Z
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/operations?start_time=2016-06-28T16:45:50Z&end_time=2016-06-30T16:45:50Z&algorithm_name=historian&algorithm_version=1.7
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/operations?operation_id=8ba0df9d-5598-4b9c-a63c-9f80ef40a776
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/operations?selfpropelled_id=b3176e8c-3bc6-4f4c-8f82-d74bf4d4b7b1
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/operations?implement_id=ced67b14-94bd-48ea-b0c8-a866f3e5e146
gurl -X GET -key $API_KEY -secret $API_SECRET https://api.leaf.ag/resources/v1/operations?managementzone_id=80db6847-b70d-4680-9495-a3a9e86c5bda
GET /operations?start=2016-06-10T10:00:00Z&end=2016-06-30T16:45:50Z HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Connection: Close
[
{
"operation_id": "9dcaa92c-1666-47b5-84ab-0a4c43c3cb28",
"company_id": 2025,
"name": "tilling",
"purpose": "tilling",
"managementzone_id":"1778dafc-6a54-4701-90ec-5182f4286024",
"completed_date": "2016-07-28T14:32:16Z",
"active_working_time": "02:00:00",
"total_stationary_time": "00:14:35",
"operator": {
"user_id": "62e4fa90-5f6b-4795-919d-f1b825706a4e",
"timezone": "MST",
"first_name": "Donald",
"last_name": "Narcia",
},
"acres": 0.0,
"average_speed": 0.0,
"start": "2016-07-28T13:51:00Z",
"end": "2016-07-28T14:32:02Z",
"selfpropelled_id": "e8bca0c0-3603-4660-a111-8835c11da3f3",
"implement_id": "797d18db-c097-40f9-a0db-b0c09b14335e",
"activities": [
{
"activity_id": "16269fbb-8469-4e95-b724-5d1199097692",
"company_id": 2025,
"device_id": "6e8b6f54-7f8b-43a1-ac60-08908c25eb26",
"activity_type": "travel",
"activity_start": "2016-07-28T13:51:00Z",
"activity_end": "2016-07-28T13:53:04Z",
"managementzone_id": "",
"selfpropelled_id":"e8bca0c0-3603-4660-a111-8835c11da3f3",
"implement_id": "797d18db-c097-40f9-a0db-b0c09b14335e",
"operator_id": "62e4fa90-5f6b-4795-919d-f1b825706a4e",
"operation_id": "9dcaa92c-1666-47b5-84ab-0a4c43c3cb28",
"purpose": "",
"traveled_distance": 2.09335033037573,
"created_at": "2016-07-28T13:56:01.102189Z",
"updated_at": "2016-07-28 13:56:01.109299"
},
{
"activity_id": "48674860-6e21-46e2-82fa-383cf6dd06ee",
"company_id": 2025,
"device_id": "6e8b6f54-7f8b-43a1-ac60-08908c25eb26",
"activity_type": "farming",
"activity_start": "2016-07-28T13:51:32Z",
"activity_end": "2016-07-28T14:38:43Z",
"managementzone_id": "1778dafc-6a54-4701-90ec-5182f4286024",
"selfpropelled_id":"e8bca0c0-3603-4660-a111-8835c11da3f3",
"implement_id": "797d18db-c097-40f9-a0db-b0c09b14335e",
"operator_id": "62e4fa90-5f6b-4795-919d-f1b825706a4e",
"operation_id": "9dcaa92c-1666-47b5-84ab-0a4c43c3cb28",
"purpose": "tilling",
"traveled_distance": 2.30242706555077,
"created_at": "2016-07-28T14:41:01.140239Z",
"updated_at": "2016-07-28T14:51:01.141167Z"
},
{
"activity_id": "94a3e30e-ec1b-47c6-9e5e-fb31c7eedb12",
"company_id": 2025,
"device_id": "6e8b6f54-7f8b-43a1-ac60-08908c25eb26",
"activity_type": "stationary",
"activity_start": "2016-07-28T13:54:28Z",
"activity_end": "2016-07-28T13:56:30Z",
"managementzone_id": "1778dafc-6a54-4701-90ec-5182f4286024",
"selfpropelled_id":"e8bca0c0-3603-4660-a111-8835c11da3f3",
"implement_id": "797d18db-c097-40f9-a0db-b0c09b14335e",
"operator_id": "62e4fa90-5f6b-4795-919d-f1b825706a4e",
"operation_id": "9dcaa92c-1666-47b5-84ab-0a4c43c3cb28",
"purpose": "",
"traveled_distance": 0,
"created_at": "2016-07-28T14:01:01.114161Z",
"updated_at": "2016-07-28T14:01:01.118971Z"
},
{
"activity_id": "2f1b4fc0-3fdf-4be4-ba3f-b6744bc6824f",
"company_id": 2025,
"device_id": "6e8b6f54-7f8b-43a1-ac60-08908c25eb26",
"activity_type": "travel",
"activity_start": "2016-07-28T13:58:36Z",
"activity_end": "2016-07-28T14:20:29Z",
"managementzone_id": "1778dafc-6a54-4701-90ec-5182f4286024",
"selfpropelled_id":"e8bca0c0-3603-4660-a111-8835c11da3f3",
"implement_id": "797d18db-c097-40f9-a0db-b0c09b14335e",
"operator_id": "62e4fa90-5f6b-4795-919d-f1b825706a4e",
"operation_id": "9dcaa92c-1666-47b5-84ab-0a4c43c3cb28",
"purpose": "",
"traveled_distance": 1.53554642077883,
"created_at": "2016-07-28T14:01:01.130118Z",
"updated_at": "2016-07-28T14:26:01.12477Z"
},
{
"activity_id": "9facf74f-c41b-4379-a455-066904b7d58c",
"company_id": 2025,
"device_id": "6e8b6f54-7f8b-43a1-ac60-08908c25eb26",
"activity_type": "travel",
"activity_start": "2016-07-28T14:23:33Z",
"activity_end": "2016-07-28T14:32:16Z",
"managementzone_id": "1778dafc-6a54-4701-90ec-5182f4286024",
"selfpropelled_id":"e8bca0c0-3603-4660-a111-8835c11da3f3",
"implement_id": "797d18db-c097-40f9-a0db-b0c09b14335e",
"operator_id": "62e4fa90-5f6b-4795-919d-f1b825706a4e",
"operation_id": "9dcaa92c-1666-47b5-84ab-0a4c43c3cb28",
"purpose": "",
"traveled_distance": 0.467075848274908,
"created_at": "2016-07-28T14:26:01.132218Z",
"updated_at": "2016-07-28T14:36:01.134205Z"
}
]
}
]
List operation.
List operation in the system.
The operation list can be filtered with query parameters.
The accepted parameters are
| Parameter | Type | Required | Notes |
|---|---|---|---|
| start_time | RFC3339 timestamp | no | Start time for operations. |
| end_time | RFC3339 timestamp | no | End time for operations. |
| algorithm | string | no | A list of Pairs of names and version for operation. |
| devices | array of string | no | Device ID for operations. |
The time parameters details
| Timezone | Offset ( Hours Behind UTC ) | Example |
|---|---|---|
| CDT | 5 | 2016-09-01T00:00:00-05:00 |
| CST | 6 | 2016-09-01T00:00:00-06:00 |
| MDT | 6 | 2016-09-01T00:00:00-06:00 |
| MST | 7 | 2016-09-01T00:00:00-07:00 |
| PDT | 7 | 2016-09-01T00:00:00-07:00 |
| PST | 7 | 2016-09-01T00:00:00-07:00 |
| EDT | 4 | 2016-09-01T00:00:00-04:00 |
| EST | 5 | 2016-09-01T00:00:00-05:00 |
HTTP Request
GET /operations
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| device_id | uuid | no | Identifier of the device that sent the heartbeats which the source of forming operations. |
| name | string | no | Operation name, the value is from equipment type. |
| purpose | string | yes | Operation purpose, the value is from equipment purpose. |
| start | string | yes | The start time of the operation. (formatted as RFC3339). |
| end | string | yes | The end time of the operation (formatted as RFC3339). |
| completed_date | string | no | The date of operation completed (formatted as RFC3339). |
| active_working_time | string | yes | The active working duration between operation start and end (formatted as HH:MM:SS). |
| total_stationary_time | string | yes | The total stationary time between operation start and end (formatted as HH:MM:SS). |
| managementzone_id | uuid | yes | The management zones associated with the activity. |
| selfpropelled_id | uuid | yes | The selfproplled equipment that was involved with the operation. |
| implement_id | uuid | yes | The implement equipment that was involved with the operation. |
| operator | object | yes | The user that was involved with the activity. |
| acres | float | no | The farming coverage of the operation. |
| average_speed | float | no | The average speed of the operation (the unit is forced to be mph). |
| algorithm_name | string | yes | Operation created by this API will has default 'leaf’ algorithm name. |
| algorithm_version | string | yes | Operation created by this API will has default 'v0’ algorithm version. |
HTTP Response
- 200: OK
- 401: Unauthorized
- 500: Internal error
Get Operation
Get the detailed operation by operation ID.
GET /operations/1c4c3614-c749-45d5-b6c3-5bd9a71d8934 HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "1c4c3614-c749-45d5-b6c3-5bd9a71d8934",
"company_id": 1,
"name": "test",
"purpose": "tilling",
"managementzone_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"completed_date": "2016-08-01T00:00:00Z",
"active_working_time": "02:00:00",
"total_stationary_time": "00:14:35",
"operator": {
"id": "5f5ba55a-4b6d-490b-a859-89bc6f9fa42d",
"timezone": "MST",
"first_name": "testuser",
"last_name": "testuser"
},
"acres": 10,
"average_speed": 10,
"start": "2016-08-01T00:00:00Z",
"end": "2016-08-01T00:00:00Z",
"device_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"selfpropelled_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"implement_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"activities": [
{
"id": "28221640-ebf7-4e89-b050-8d82c812f939",
"device_id": "56f91903-1316-47bc-b020-fa382228f6a5",
"type": "farming",
"start": "2016-08-01T19:00:00-05:00",
"end": "2016-08-01T19:00:00-05:00",
"managementzone_id":"56f91903-1316-47bc-b020-fa382228f6a5",
"operator_id": "5f5ba55a-4b6d-490b-a859-89bc6f9fa42d",
"selfpropelled_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"implement_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"purpose": "tilling",
"operation_id": "1c4c3614-c749-45d5-b6c3-5bd9a71d8934",
"traveled_distance": 200,
"algorithm_name": "fenster",
"algorithm_version": "v1"
}
]
}
gurl -X GET -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/resources/v1/operations/1c4c3614-c749-45d5-b6c3-5bd9a71d8934"
{
"id": "1c4c3614-c749-45d5-b6c3-5bd9a71d8934",
"company_id": 1,
"name": "test",
"purpose": "tilling",
"managementzone_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"completed_date": "2016-08-01T00:00:00Z",
"active_working_time": "02:00:00",
"total_stationary_time": "00:14:35",
"operator": {
"id": "5f5ba55a-4b6d-490b-a859-89bc6f9fa42d",
"timezone": "MST",
"first_name": "testuser",
"last_name": "testuser"
},
"acres": 10,
"average_speed": 10,
"start": "2016-08-01T00:00:00Z",
"end": "2016-08-01T00:00:00Z",
"device_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"selfpropelled_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"implement_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"activities": [
{
"id": "28221640-ebf7-4e89-b050-8d82c812f939",
"device_id": "56f91903-1316-47bc-b020-fa382228f6a5",
"type": "farming",
"start": "2016-08-01T19:00:00-05:00",
"end": "2016-08-01T19:00:00-05:00",
"managementzone_id":"56f91903-1316-47bc-b020-fa382228f6a5",
"operator_id": "5f5ba55a-4b6d-490b-a859-89bc6f9fa42d",
"selfpropelled_id":"67f91903-1316-47bc-b020-fa382228f6a5",
"implement_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"purpose": "tilling",
"operation_id": "1c4c3614-c749-45d5-b6c3-5bd9a71d8934",
"traveled_distance": 200,
"algorithm_name": "fenster",
"algorithm_version": "v1"
}
]
}
HTTP Request
GET /operations/:id
HTTP Response
- 200: OK.
- 400: Bad request.
- 500: Internal error.
Edit Operation
Edit operation by operation ID
PUT /operations/1c4c3614-c749-45d5-b6c3-5bd9a71d8934 HTTP/1.1
Host: api.leaf.ag
Content-Type: application/json
{
"managementzone_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"completed_date": "2016-08-01T00:00:00Z",
"active_working_time": "02:00:00",
"total_stationary_time": "00:12:35",
"acres": 10.0,
"average_speed": 10.0,
"start": "2016-08-01T00:00:00Z",
"end": "2016-08-01T00:00:00Z",
"device_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"selfpropelled_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"implement_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"operator": { "id": "cd0c6a46-0435-46cb-abfa-4233586914d4" }
}
HTTP/1.1 202 Accepted
Connection: Close
gurl \
-X PUT \
-key $API_KEY \
-secret $API_SECRET \
-d '{
"managementzone_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"completed_date": "2016-08-01T00:00:00Z",
"active_working_time": "02:00:00",
"total_stationary_time": "00:12:35",
"acres": 10.0,
"average_speed": 10.0,
"start": "2016-08-01T00:00:00Z",
"end": "2016-08-01T00:00:00Z",
"device_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"selfpropelled_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"implement_id": "67f91903-1316-47bc-b020-fa382228f6a5",
"operator": {"id": "cd0c6a46-0435-46cb-abfa-4233586914d4"}
}' \
"https://api.leaf.ag/resources/v1/operations/1c4c3614-c749-45d5-b6c3-5bd9a71d8934"
HTTP Request
PUT /operations/:id
HTTP Response
- 202: Accepted.
- 500: Internal error.
Delete operation
Delete operation by operation ID
DELETE /operations/1c4c3614-c749-45d5-b6c3-5bd9a71d8934 HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 204 NOCONTENT
Connection: Close
The request of deleting an operation:
gurl -X DELETE "https://api.leaf.ag/resources/v1/operations/1c4c3614-c749-45d5-b6c3-5bd9a71d8934"
HTTP Request
DELETE /operations/:id
HTTP Response
- 204: NoContent.
- 500: Internal error.
Export
All export endpoints must be prefixed with /resources/v1/export.
All requests and responses are JSON-encoded.
Export Equipment
gurl "https://api.leaf.ag/resources/v1/export/equipment"
GET /export/equipment HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
Export equipment profile from the system.
Export Personnel
gurl "https://api.leaf.ag/resources/v1/export/personnel"
GET /export/personnel HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
Export personnel profile from the system.
Export ManagementZone
gurl "https://api.leaf.ag/resources/v1/export/managementzones"
GET /export/managementzones HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
Export managementzone profile from the system.
Export Operation and Activity
# Export operations with algorithm is leaf.
gurl -X GET -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/resources/v1/export/operations?algorithm=leaf-1.0&simple=true"
# Export operations with algorithms are leaf and historian.
gurl -X GET -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/resources/v1/export/operations?algorithm=leaf-1.0,historian-1.6&simple=true
# Export operations with algorithms are historian, jdlink, opcenter.
gurl -X GET -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/resources/v1/export/operations?algorithm=historian-1.6,jdlink-1.0,opcenter-1.0&simple=true
# Export activities with algorithm is historian.
gurl -X GET -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/resources/v1/export/operations?algorithm=historian-1.7&simple=false
# Export activities with algorithms are historian, jdlink, opcenter.
gurl -X GET -key $API_KEY -secret $API_SECRET "https://api.leaf.ag/resources/v1/export/operations?algorithm=historian-1.7,jdlink-1.0,opcenter-1.0&simple=false
GET /export/operations?algorithm=historian-1.7&simple=true HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
Export activities and operations from the system.
Import Activity
gurl -X POST -d @activities.csv "https://api.leaf.ag/resources/v1/import/activities"
POST /import/activities HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
<payload>
Import activities from CSV file.
Send mail to sales@leaf.ag
curl -X POST -H 'Content-Type: application/json' "https://api.leaf.ag/resources/v1/contact/mail" -d '
{
"role": "Dealer",
"name": "Guillaume",
"email": "guillaume@leaf.ag",
"phone_number": "6028182624",
"state": "Texas",
"job_title": "Crop Manager",
"organization_name": "French Fry Farm",
"comments": "blah blah"
}
'
POST /contact/mail HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
<payload>
Send mail to sales@leaf.ag.
Operations Report
All operations report endpoints must be prefixed with /resources/v1.
All requestes and responsed are JSON-encoded.
List Operations Report
# List all operations
gurl https://api.leaf.ag/resources/v1/operations_report
# List operations that ended during a two-day period
gurl -X GET -d '{"start_time":2016-06-28T16:45:50Z,"end_time":2016-06-30T16:45:50Z}' https://api.leaf.ag/resources/v1/operations_reports
gurl https://api.leaf.ag/resources/v1/operations_reports?start_time=2016-06-10T10:00:00Z&end_time=2016-06-30T16:45:50Z
[
{
"report_operation_id": "0886b2ce-ed82-5aa4-a72c-dcd89583a759",
"company_id": 1,
"company_name": "NAPI",
"company_timezone": "America/Phoenix",
"leaf_operation_id": "9da42d10-5dbb-4ebc-b117-78302f8c7719",
"unallocated_jdlink_operation_id": "",
"device_id": "37ad2999-8b46-4529-b685-6a7634e8b193",
"operation_name": "Disked",
"purpose": "tilling",
"start_time": "2017-04-05T18:04:17-05:00",
"end_time": "2017-04-05T18:51:52-05:00",
"duration": 0.7930555555555555,
"algorithm_name": "historian",
"algorithm_version": "1.7",
"did_user_crud": false,
"farming_duration": 0.7930555555555555,
"stationary_duration": 0,
"travel_active_duration": 0,
"travel_stationary_duration": 0,
"missing_duration": 0,
"farming_distance_travelled": 5.195478030153149,
"travel_distance_travelled": 0,
"farming_covered_acres": 20.15215720786676,
"farming_gross_acres": 20.15215720786676,
"operator_id": "0c35670e-3545-4730-b231-1662d5a0f0e6",
"operator_name": "Ervin Kee",
"operator_role": "operator",
"selfpropelled_id": "7d9c7a58-3efa-4f6f-96bf-053f16ed1df6",
"selfpropelled_name": "24-271",
"selfpropelled_category": "selfpropelled",
"selfpropelled_purpose": "driving",
"selfpropelled_type": "tractor",
"selfpropelled_make": "John Deere",
"selfpropelled_model": "9520R",
"selfpropelled_year": 0,
"selfpropelled_vin_sn": "1RW9520RCFP016985",
"selfpropelled_brand": "",
"selfpropelled_serial": "",
"selfpropelled_pass_width": 0,
"selfpropelled_pass_width_units": "",
"selfpropelled_physical_width": 0,
"selfpropelled_physical_width_units": "",
"selfpropelled_beacon_serial": "Sv5h",
"selfpropelled_beacon_major": 54292,
"selfpropelled_beacon_minor": 62005,
"implement_id": "dab31ba0-3257-4cd5-9fb9-34b8f68d8f99",
"implement_name": "9-118",
"implement_category": "implement",
"implement_purpose": "tilling",
"implement_type": "disk",
"implement_make": "",
"implement_model": "337 Disk",
"implement_year": 0,
"implement_vin_sn": "",
"implement_brand": "",
"implement_serial": "",
"implement_pass_width": 32,
"implement_pass_width_units": "feet",
"implement_physical_width": 32,
"implement_physical_width_units": "feet",
"implement_beacon_serial": "o3Ay",
"implement_beacon_major": 54979,
"implement_beacon_minor": 28024,
"managementzone_id": "21706f7a-13b9-44a8-b469-2f71031d5128",
"managementzone_field_name": null,
"managementzone_field_actual_acres": null,
"managementzone_field_description": null,
"related_farm_id": "",
"related_farm_name": null,
"jd_selfpropelled_id": "280655",
"jd_selfpropelled_name": "24-271, JD 9520R",
"jdl_working_machine_utilization": null,
"jdl_idle_machine_utilization": null,
"jdl_transport_machine_utilization": null,
"jdl_working_fuel_consumed": null,
"jdl_idle_fuel_consumed": null,
"jdl_transport_fuel_consumed": null,
"jdl_total_fuel_consumed": null,
"jdl_working_avg_engine_load": null,
"jdl_idle_avg_engine_load": null,
"jdl_transport_avg_engine_load": null,
"jdl_working_avg_ground_speed": null,
"jdl_idle_avg_ground_speed": null,
"jdl_transport_avg_ground_speed": null,
"jd_operation_id": null,
"jdo_duration": null,
"jdo_operative_duration": null,
"jdo_allocated_percentage": null,
"jdo_operation_type": null,
"jdo_crop_name": null,
"jdo_variety_name": null,
"jdo_product_name": null,
"jdo_target_acres": null,
"jdo_result_acres": null,
"jdo_result_pass_acres": null,
"jdo_operative_result_acres": null,
"jdo_operative_result_pass_acres": null,
"jdo_distance_travelled": null,
"jdo_operative_distance_travelled": null,
"jdo_target_material_value": null,
"jdo_result_material_value": null,
"jdo_material_unit": null,
"jdo_yield_value": null,
"jdo_yield_unit": null,
"jdo_wet_mass_value": null,
"jdo_wet_mass_unit": null,
"jdo_avg_moisture": null,
"jdo_avg_target_rate_value": null,
"jdo_avg_target_rate_unit": null,
"jdo_avg_applied_rate_value": null,
"jdo_avg_applied_rate_unit": null,
"jdo_avg_control_rate_value": null,
"jdo_avg_control_rate_unit": null,
"jdo_swath_width": null,
"created_at": "2016-07-28T14:23:33Z",
"updated_at": "2016-07-28T14:23:33Z",
"deleted_at": null
}
]
GET /operations_report?start=2016-06-10T10:00:00Z&end=2016-06-30T16:45:50Z HTTP/1.1
Host: api.leaf.ag
Authorization: AGSIG1 key=<user api key>, signature=<request signature>, ts=<request timestamp>, nonce=<uniq nonce>
Content-Type: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Connection: Close
[
{
"report_operation_id": "0886b2ce-ed82-5aa4-a72c-dcd89583a759",
"company_id": 1,
"company_name": "NAPI",
"company_timezone": "America/Phoenix",
"leaf_operation_id": "9da42d10-5dbb-4ebc-b117-78302f8c7719",
"unallocated_jdlink_operation_id": "",
"device_id": "37ad2999-8b46-4529-b685-6a7634e8b193",
"operation_name": "Disked",
"purpose": "tilling",
"start_time": "2017-04-05T18:04:17-05:00",
"end_time": "2017-04-05T18:51:52-05:00",
"duration": 0.7930555555555555,
"algorithm_name": "historian",
"algorithm_version": "1.7",
"did_user_crud": false,
"farming_duration": 0.7930555555555555,
"stationary_duration": 0,
"travel_active_duration": 0,
"travel_stationary_duration": 0,
"missing_duration": 0,
"farming_distance_travelled": 5.195478030153149,
"travel_distance_travelled": 0,
"farming_covered_acres": 20.15215720786676,
"farming_gross_acres": 20.15215720786676,
"operator_id": "0c35670e-3545-4730-b231-1662d5a0f0e6",
"operator_name": "Ervin Kee",
"operator_role": "operator",
"selfpropelled_id": "7d9c7a58-3efa-4f6f-96bf-053f16ed1df6",
"selfpropelled_name": "24-271",
"selfpropelled_category": "selfpropelled",
"selfpropelled_purpose": "driving",
"selfpropelled_type": "tractor",
"selfpropelled_make": "John Deere",
"selfpropelled_model": "9520R",
"selfpropelled_year": 0,
"selfpropelled_vin_sn": "1RW9520RCFP016985",
"selfpropelled_brand": "",
"selfpropelled_serial": "",
"selfpropelled_pass_width": 0,
"selfpropelled_pass_width_units": "",
"selfpropelled_physical_width": 0,
"selfpropelled_physical_width_units": "",
"selfpropelled_beacon_serial": "Sv5h",
"selfpropelled_beacon_major": 54292,
"selfpropelled_beacon_minor": 62005,
"implement_id": "dab31ba0-3257-4cd5-9fb9-34b8f68d8f99",
"implement_name": "9-118",
"implement_category": "implement",
"implement_purpose": "tilling",
"implement_type": "disk",
"implement_make": "",
"implement_model": "337 Disk",
"implement_year": 0,
"implement_vin_sn": "",
"implement_brand": "",
"implement_serial": "",
"implement_pass_width": 32,
"implement_pass_width_units": "feet",
"implement_physical_width": 32,
"implement_physical_width_units": "feet",
"implement_beacon_serial": "o3Ay",
"implement_beacon_major": 54979,
"implement_beacon_minor": 28024,
"managementzone_id": "21706f7a-13b9-44a8-b469-2f71031d5128",
"managementzone_field_name": null,
"managementzone_field_actual_acres": null,
"managementzone_field_description": null,
"related_farm_id": "",
"related_farm_name": null,
"jd_selfpropelled_id": "280655",
"jd_selfpropelled_name": "24-271, JD 9520R",
"jdl_working_machine_utilization": null,
"jdl_idle_machine_utilization": null,
"jdl_transport_machine_utilization": null,
"jdl_working_fuel_consumed": null,
"jdl_idle_fuel_consumed": null,
"jdl_transport_fuel_consumed": null,
"jdl_total_fuel_consumed": null,
"jdl_working_avg_engine_load": null,
"jdl_idle_avg_engine_load": null,
"jdl_transport_avg_engine_load": null,
"jdl_working_avg_ground_speed": null,
"jdl_idle_avg_ground_speed": null,
"jdl_transport_avg_ground_speed": null,
"jd_operation_id": null,
"jdo_duration": null,
"jdo_operative_duration": null,
"jdo_allocated_percentage": null,
"jdo_operation_type": null,
"jdo_crop_name": null,
"jdo_variety_name": null,
"jdo_product_name": null,
"jdo_target_acres": null,
"jdo_result_acres": null,
"jdo_result_pass_acres": null,
"jdo_operative_result_acres": null,
"jdo_operative_result_pass_acres": null,
"jdo_distance_travelled": null,
"jdo_operative_distance_travelled": null,
"jdo_target_material_value": null,
"jdo_result_material_value": null,
"jdo_material_unit": null,
"jdo_yield_value": null,
"jdo_yield_unit": null,
"jdo_wet_mass_value": null,
"jdo_wet_mass_unit": null,
"jdo_avg_moisture": null,
"jdo_avg_target_rate_value": null,
"jdo_avg_target_rate_unit": null,
"jdo_avg_applied_rate_value": null,
"jdo_avg_applied_rate_unit": null,
"jdo_avg_control_rate_value": null,
"jdo_avg_control_rate_unit": null,
"jdo_swath_width": null,
"created_at": "2016-07-28T14:23:33Z",
"updated_at": "2016-07-28T14:23:33Z",
"deleted_at": null
}
]
List operation_reports.
List operation reports in the system.
The operations report list can be filtered with query parameters.
The accepted parameters are:
| Parameter | Type | Required | Notes |
|---|---|---|---|
| start_time | RFC3339 timestamp | no | Start time for operations. |
| end_time | RFC3339 timestamp | no | End time for operations. |
HTTP Request
GET /operations_reports
Parameters
| Parameter | Type | Units | Description |
|---|---|---|---|
| company_id | int | Leaf Company ID. | |
| company_name | string | Leaf Company Name. | |
| company_timezone | string | Company Timezone. | |
| report_operation_id | UUID | Operations Report specific ID for operation. | |
| operation_id | UUID | Leaf Operation ID. | |
| unallocated_jdlink_operation_id | UUID | Unallocated Operation ID generated from JDLink data. | |
| device_id | UUID | Unique device identifier. | |
| operation_name | string | Operations Name. Inferred from equipment. | |
| purpose | string | Operation Purpose. Inferred from equipment. | |
| start_time | RFC3339 Time | local tz | Time of the operation start. In company’s time zone. |
| end_time | RFC3339 Time | local tz | Time of the operation end. In company’s time zone. |
| duration | float | hours | Duration of the operation. |
| algorithm_name | string | Algorithm of the operation. “historian” when there is leaf data, “jdlink” or “jdopcenter” otherwise. | |
| algorithm_version | string | Version of the algorithm when “historian”. | |
| did_user_crud | bool | Flag whether or not the entry has been manually edited by a user. | |
| farming_duration | float | hours | Duration of the “farming” activities during the operation. |
| stationary_duration | float | hours | Duration of the “farming” activities during the operation. |
| travel_active_duration | float | hours | Duration of the “travel_active” activities during the operation. |
| travel_stationary_duration | float | hours | Duration of the “travel_stationery” activities during the operation. |
| missing_duration | float | hours | Duration of the “missing” and “travel_missing” activities during the operation. |
| farming_distance_travelled | float | miles | Distance travelled duration for the “farming” activities for the operation. |
| travel_distance_travelled | float | miles | Distance travelled duration for the “travel” activities for the operation. |
| farming_covered_acres | float | acres | Covered Acres (based on pass_width) for “farming” activities for the operation. |
| farming_gross_acres | float | acres | Covered Acres (based on physical_width) for “farming” activities for the operation. |
| operator_id | UUUD | ID of the Leaf user operating the equipment. | |
| operator_name | string | Name of the user operating the equipment. | |
| operator_role | string | Role of the user operating the equipment. (Ex. “operator”, “manager”). | |
| selfpropelled_id | UUID | ID of the operated selfpropelled on the Leaf side. | |
| selfpropelled_name | string | Name of the SP. | |
| selfpropelled_category | string | Category of the SP. | |
| selfpropelled_purpose | string | Purpose of the SP. | |
| selfpropelled_type | string | Type of the SP. | |
| selfpropelled_make | string | Make of the SP. | |
| selfpropelled_model | string | Model of the SP. | |
| selfpropelled_year | int | Year of the SP. | |
| selfpropelled_vin_sn | string | VIN of the SP. | |
| selfpropelled_brand | string | Brand of the SP. | |
| selfpropelled_serial | string | Serial of the SP. | |
| selfpropelled_pass_width | float | variable | Pass Width of the SP. |
| selfpropelled_pass_width_units | string | Units for the SP’s pass width. Should always be “feet”. | |
| selfpropelled_physical_width | float | variable | Physical (“gross”) width of the SP. |
| selfpropelled_physical_width_units | string | Units for the SP’s physical wifth. Should always be “feet.” | |
| selfpropelled_beacon_serial | string | Serial number of the beacon linked to the SP. | |
| selfpropelled_beacon_major | int | Beacon Major number linked to the SP. | |
| selfpropelled_beacon_minor | int | Beacon Minor number linked to the SP. | |
| implement_id | UUID | ID of the operated implement on the Leaf side. | |
| implement_name | string | Name of the Implement. | |
| implement_category | string | Category of the Implement. | |
| implement_purpose | string | Purpose of the Implement. | |
| implement_type | string | Type of the Implement. | |
| implement_make | string | Make of the Implement. | |
| implement_model | string | Model of the Implement. | |
| implement_year | int | Year of the Implement. | |
| implement_vin_sn | string | VIN of the Implement. | |
| implement_brand | string | Brand of the Implement. | |
| implement_serial | string | Serial of the Implement. | |
| implement_pass_width | float | variable | Pass Width of the Implement. |
| implement_pass_width_units | string | Units for the Implement’s pass width. Should always be “feet”. | |
| implement_physical_width | float | variable | Physical (“gross”) width of the Implement. |
| implement_physical_width_units | string | Units for the Implement’s physical wifth. Should always be “feet.” | |
| implement_beacon_serial | string | Serial number of the beacon linked to the Implement. | |
| implement_beacon_major | int | Beacon Major number linked to the Implement. | |
| implement_beacon_minor | int | Beacon Minor number linked to the Implement. | |
| managementzone_id | UUID | ID of the Leaf MZ where the operation took place. | |
| managementzone_field_name | string | Name of the Leaf MZ. | |
| managementzone_field_actual_acres | float | acres | Area of the Leaf MZ. |
| managementzone_field_description | string | Description of the Leaf MZ. | |
| related_farm_id | UUID | ID of the Leaf Farm on which the operated MZ belong. | |
| related_farm_name | string | Name of the Farm for the operated MZ. | |
| jd_selfpropelled_id | string | ID of the John Deere self propelled equipment operated for the operation. | |
| jd_selfpropelled_name | string | Name of the JD equipment. | |
| jdl_working_machine_utilization | float | minutes | “working” Machine Utilization from JDLink for the operation. |
| jdl_idle_machine_utilization | float | minutes | “idle” Machine Utilization from JDLink for the operation. |
| jdl_transport_machine_utilization | float | minutes | “transport” Machine Utilization from JDLink for the operation. |
| jdl_working_fuel_consumed | float | gallons | “working” Fuel Consumed from JDLink for the operation. |
| jdl_idle_fuel_consumed | float | gallons | “idle” Fuel Consumed from JDLink for the operation. |
| jdl_transport_fuel_consumed | float | gallons | “transport” Fuel Consumed from JDLink for the operation. |
| jdl_total_fuel_consumed | float | gallons | Total (sum of “working”, “idle” and “transport”) Fueld Consumed from JDLink for the operation. |
| jdl_working_avg_engine_load | float | % | “working” average engine load from JDLink for the operation. |
| jdl_idle_avg_engine_load | float | % | “idle” average engine load from JDLink for the operation. |
| jdl_transport_avg_engine_load | float | % | “transport” average engine load from JDLink for the operation. |
| jdl_working_avg_ground_speed | float | mph | “working” average ground speed from JDLink for the operation. |
| jdl_idle_avg_ground_speed | float | mph | “idle” average ground speed from JDLink for the operation. |
| jdl_transport_avg_ground_speed | float | mph | “transport” average ground speed from JDLink for the operation. |
| jd_operation_id | string | base64 | ID of the Operation on the John Deere side. |
| jdo_duration | float | hours | Duration of the operation from JD Ops Center. |
| jdo_operative_duration | float | hours | Duration when values exist for VRYIELDVOL for (harvest), or AppliedRate for (application and planting). |
| jdo_allocated_percentage | float | % | Percentage of the John Deere Ops Center operation’s time range allcoated to leaf operation. |
| jdo_operation_type | string | Type of operation reported by JD Ops Center. Ex: “seeding”, “application” or “harvest”. | |
| jdo_crop_name | string | Name of the crop reported by JD Ops Center for the operation. Ex: “CORN_SILAGE”, “OATS”, “CORN_WET”, “COTTON”, “SORGHUM”, “WHEAT_DURUM”, “BARLEY”. | |
| jdo_variety_name | string | Variety of the crop reported by JD Ops Center for the operation. Ex: “FM 2007GLT - 220,000”, “PHY 841”, “DKC-53-45”, “PHY 444 WRF - 230,00”,“PHY 841”, “DKC62-08RIB” | |
| jdo_product_name | string | Product reported by JD Ops Center for the operation. Ex: “1”, “32-0-0”, “QUADRIS”, “UAN Solution 32-0-0”, “Acid Fert.”. | |
| jdo_target_acres | float | acres | Operator entered target area to be covered for the operation. |
| jdo_result_acres | float | acres | Actual area covered for the operation based on Swath width and Distance travelled. |
| jdo_result_pass_acres | float | acres | Actual area covered for the operation based on Pass width and Distance travelled. |
| jdo_operative_result_acres | float | acres | Operative area covered for the operation based on Swath width when values exist for VRYIELDVOL for (harvest), or AppliedRate for (application and planting). |
| jdo_operative_result_pass_acres | float | acres | Operative area covered for the operation based on Pass width when values exist for VRYIELDVOL for (harvest), or AppliedRate for (application and planting). |
| jdo_distance_travelled | float | feet | Sum of the distances reported by John Deere for the operation. |
| jdo_operative_distance_travelled | float | feet | Sum of the distances reported by John Deere for the operation when values exist for VRYIELDVOL for (harvest), or AppliedRate for (application and planting). |
| jdo_target_material_value | float | variable | Operator entered target material to be used for the operation. |
| jdo_result_material_value | float | variable | Actual material used for the operation based on Swath width, distance travelled and average applied rate. |
| jdo_material_unit | string | Units for jdo_result_material_value and jdo_target_material_value. Ex: “gal”, “seeds”. | |
| jdo_yield_value | float | variable | Yield of the operation based on VrYieldVol, Swath width and distance travelled. |
| jdo_yield_unit | string | Units for jdo_yield_value. | |
| jdo_wet_mass_value | float | variable | Wet mass of the operation based on Swath width and distance travelled. |
| jdo_wet_mass_unit | string | Units for jdo_wet_mass_value. Ex: “ton”, “lb”. | |
| jdo_avg_moisture | float | % | Percentage of moisture for the operation time range. |
| jdo_avg_target_rate_value | float | variable | Average target rate entered by the operator based on the operation time range. |
| jdo_avg_target_rate_unit | string | Units for jdo_avg_target_rate_value. Ex: “seeds1ac-1”, “gal1ac-1”, “lb1ac-1”. | |
| jdo_avg_applied_rate_value | float | variable | Average applied rate based on duration. |
| jdo_avg_applied_rate_unit | string | Units for jdo_avg_applied_rate_value. Ex: “seeds1ac-1”, “gal1ac-1”, “lb1ac-1”. | |
| jdo_avg_control_rate_value | float | variable | Average control rate based on duration. |
| jdo_avg_control_rate_unit | string | Units for jdo_avg_control_rate_value. Ex: “seeds1ac-1”, “gal1ac-1”, “lb1ac-1”. | |
| jdo_swath_width | float | feet | Swath width reported by John Deere for the equipment operated during the operation. |
HTTP Response
- 200: OK
- 401: Unauthorized
- 500: Internal error
Get Operation Report
Get the detailed operation report by report operation ID.
GET /operations_report/1c4c3614-c749-45d5-b6c3-5bd9a71d8934 HTTP/1.1
Host: api.leaf.ag
HTTP/1.1 200 OK
Content-Type: application/json
{
"report_operation_id": "0886b2ce-ed82-5aa4-a72c-dcd89583a759",
"company_id": 1,
"company_name": "NAPI",
"company_timezone": "America/Phoenix",
"leaf_operation_id": "9da42d10-5dbb-4ebc-b117-78302f8c7719",
"unallocated_jdlink_operation_id": "",
"device_id": "37ad2999-8b46-4529-b685-6a7634e8b193",
"operation_name": "Disked",
"purpose": "tilling",
"start_time": "2017-04-05T18:04:17-05:00",
"end_time": "2017-04-05T18:51:52-05:00",
"duration": 0.7930555555555555,
"algorithm_name": "historian",
"algorithm_version": "1.7",
"did_user_crud": false,
"farming_duration": 0.7930555555555555,
"stationary_duration": 0,
"travel_active_duration": 0,
"travel_stationary_duration": 0,
"missing_duration": 0,
"farming_distance_travelled": 5.195478030153149,
"travel_distance_travelled": 0,
"farming_covered_acres": 20.15215720786676,
"farming_gross_acres": 20.15215720786676,
"operator_id": "0c35670e-3545-4730-b231-1662d5a0f0e6",
"operator_name": "Ervin Kee",
"operator_role": "operator",
"selfpropelled_id": "7d9c7a58-3efa-4f6f-96bf-053f16ed1df6",
"selfpropelled_name": "24-271",
"selfpropelled_category": "selfpropelled",
"selfpropelled_purpose": "driving",
"selfpropelled_type": "tractor",
"selfpropelled_make": "John Deere",
"selfpropelled_model": "9520R",
"selfpropelled_year": 0,
"selfpropelled_vin_sn": "1RW9520RCFP016985",
"selfpropelled_brand": "",
"selfpropelled_serial": "",
"selfpropelled_pass_width": 0,
"selfpropelled_pass_width_units": "",
"selfpropelled_physical_width": 0,
"selfpropelled_physical_width_units": "",
"selfpropelled_beacon_serial": "Sv5h",
"selfpropelled_beacon_major": 54292,
"selfpropelled_beacon_minor": 62005,
"implement_id": "dab31ba0-3257-4cd5-9fb9-34b8f68d8f99",
"implement_name": "9-118",
"implement_category": "implement",
"implement_purpose": "tilling",
"implement_type": "disk",
"implement_make": "",
"implement_model": "337 Disk",
"implement_year": 0,
"implement_vin_sn": "",
"implement_brand": "",
"implement_serial": "",
"implement_pass_width": 32,
"implement_pass_width_units": "feet",
"implement_physical_width": 32,
"implement_physical_width_units": "feet",
"implement_beacon_serial": "o3Ay",
"implement_beacon_major": 54979,
"implement_beacon_minor": 28024,
"managementzone_id": "21706f7a-13b9-44a8-b469-2f71031d5128",
"managementzone_field_name": null,
"managementzone_field_actual_acres": null,
"managementzone_field_description": null,
"related_farm_id": "",
"related_farm_name": null,
"jd_selfpropelled_id": "280655",
"jd_selfpropelled_name": "24-271, JD 9520R",
"jdl_working_machine_utilization": null,
"jdl_idle_machine_utilization": null,
"jdl_transport_machine_utilization": null,
"jdl_working_fuel_consumed": null,
"jdl_idle_fuel_consumed": null,
"jdl_transport_fuel_consumed": null,
"jdl_total_fuel_consumed": null,
"jdl_working_avg_engine_load": null,
"jdl_idle_avg_engine_load": null,
"jdl_transport_avg_engine_load": null,
"jdl_working_avg_ground_speed": null,
"jdl_idle_avg_ground_speed": null,
"jdl_transport_avg_ground_speed": null,
"jd_operation_id": null,
"jdo_duration": null,
"jdo_operative_duration": null,
"jdo_allocated_percentage": null,
"jdo_operation_type": null,
"jdo_crop_name": null,
"jdo_variety_name": null,
"jdo_product_name": null,
"jdo_target_acres": null,
"jdo_result_acres": null,
"jdo_result_pass_acres": null,
"jdo_operative_result_acres": null,
"jdo_operative_result_pass_acres": null,
"jdo_distance_travelled": null,
"jdo_operative_distance_travelled": null,
"jdo_target_material_value": null,
"jdo_result_material_value": null,
"jdo_material_unit": null,
"jdo_yield_value": null,
"jdo_yield_unit": null,
"jdo_wet_mass_value": null,
"jdo_wet_mass_unit": null,
"jdo_avg_moisture": null,
"jdo_avg_target_rate_value": null,
"jdo_avg_target_rate_unit": null,
"jdo_avg_applied_rate_value": null,
"jdo_avg_applied_rate_unit": null,
"jdo_avg_control_rate_value": null,
"jdo_avg_control_rate_unit": null,
"jdo_swath_width": null,
"created_at": "2016-07-28T14:23:33Z",
"updated_at": "2016-07-28T14:23:33Z",
"deleted_at": null
}
gurl "https://api.leaf.ag/resources/v1/operations_report/1c4c3614-c749-45d5-b6c3-5bd9a71d8934"
{
"report_operation_id": "0886b2ce-ed82-5aa4-a72c-dcd89583a759",
"company_id": 1,
"company_name": "NAPI",
"company_timezone": "America/Phoenix",
"leaf_operation_id": "9da42d10-5dbb-4ebc-b117-78302f8c7719",
"unallocated_jdlink_operation_id": "",
"device_id": "37ad2999-8b46-4529-b685-6a7634e8b193",
"operation_name": "Disked",
"purpose": "tilling",
"start_time": "2017-04-05T18:04:17-05:00",
"end_time": "2017-04-05T18:51:52-05:00",
"duration": 0.7930555555555555,
"algorithm_name": "historian",
"algorithm_version": "1.7",
"did_user_crud": false,
"farming_duration": 0.7930555555555555,
"stationary_duration": 0,
"travel_active_duration": 0,
"travel_stationary_duration": 0,
"missing_duration": 0,
"farming_distance_travelled": 5.195478030153149,
"travel_distance_travelled": 0,
"farming_covered_acres": 20.15215720786676,
"farming_gross_acres": 20.15215720786676,
"operator_id": "0c35670e-3545-4730-b231-1662d5a0f0e6",
"operator_name": "Ervin Kee",
"operator_role": "operator",
"selfpropelled_id": "7d9c7a58-3efa-4f6f-96bf-053f16ed1df6",
"selfpropelled_name": "24-271",
"selfpropelled_category": "selfpropelled",
"selfpropelled_purpose": "driving",
"selfpropelled_type": "tractor",
"selfpropelled_make": "John Deere",
"selfpropelled_model": "9520R",
"selfpropelled_year": 0,
"selfpropelled_vin_sn": "1RW9520RCFP016985",
"selfpropelled_brand": "",
"selfpropelled_serial": "",
"selfpropelled_pass_width": 0,
"selfpropelled_pass_width_units": "",
"selfpropelled_physical_width": 0,
"selfpropelled_physical_width_units": "",
"selfpropelled_beacon_serial": "Sv5h",
"selfpropelled_beacon_major": 54292,
"selfpropelled_beacon_minor": 62005,
"implement_id": "dab31ba0-3257-4cd5-9fb9-34b8f68d8f99",
"implement_name": "9-118",
"implement_category": "implement",
"implement_purpose": "tilling",
"implement_type": "disk",
"implement_make": "",
"implement_model": "337 Disk",
"implement_year": 0,
"implement_vin_sn": "",
"implement_brand": "",
"implement_serial": "",
"implement_pass_width": 32,
"implement_pass_width_units": "feet",
"implement_physical_width": 32,
"implement_physical_width_units": "feet",
"implement_beacon_serial": "o3Ay",
"implement_beacon_major": 54979,
"implement_beacon_minor": 28024,
"managementzone_id": "21706f7a-13b9-44a8-b469-2f71031d5128",
"managementzone_field_name": null,
"managementzone_field_actual_acres": null,
"managementzone_field_description": null,
"related_farm_id": "",
"related_farm_name": null,
"jd_selfpropelled_id": "280655",
"jd_selfpropelled_name": "24-271, JD 9520R",
"jdl_working_machine_utilization": null,
"jdl_idle_machine_utilization": null,
"jdl_transport_machine_utilization": null,
"jdl_working_fuel_consumed": null,
"jdl_idle_fuel_consumed": null,
"jdl_transport_fuel_consumed": null,
"jdl_total_fuel_consumed": null,
"jdl_working_avg_engine_load": null,
"jdl_idle_avg_engine_load": null,
"jdl_transport_avg_engine_load": null,
"jdl_working_avg_ground_speed": null,
"jdl_idle_avg_ground_speed": null,
"jdl_transport_avg_ground_speed": null,
"jd_operation_id": null,
"jdo_duration": null,
"jdo_operative_duration": null,
"jdo_allocated_percentage": null,
"jdo_operation_type": null,
"jdo_crop_name": null,
"jdo_variety_name": null,
"jdo_product_name": null,
"jdo_target_acres": null,
"jdo_result_acres": null,
"jdo_result_pass_acres": null,
"jdo_operative_result_acres": null,
"jdo_operative_result_pass_acres": null,
"jdo_distance_travelled": null,
"jdo_operative_distance_travelled": null,
"jdo_target_material_value": null,
"jdo_result_material_value": null,
"jdo_material_unit": null,
"jdo_yield_value": null,
"jdo_yield_unit": null,
"jdo_wet_mass_value": null,
"jdo_wet_mass_unit": null,
"jdo_avg_moisture": null,
"jdo_avg_target_rate_value": null,
"jdo_avg_target_rate_unit": null,
"jdo_avg_applied_rate_value": null,
"jdo_avg_applied_rate_unit": null,
"jdo_avg_control_rate_value": null,
"jdo_avg_control_rate_unit": null,
"jdo_swath_width": null,
"created_at": "2016-07-28T14:23:33Z",
"updated_at": "2016-07-28T14:23:33Z",
"deleted_at": null
}
HTTP Request
GET /operations_report/:id
HTTP Response
- 200: OK.
- 400: Bad request.
- 500: Internal error.
Streams
Streams is a service dedicated to streaming events.
The service exposes two types of endpoints: WebSocket and HTTP.
Clients who wish to use realtime streaming will use the WebSocket endpoints, whereas clients who wish to use polling will choose HTTP.
Authentication
Subscribe using Websocket with http header auth (regular mode)
gurl -X GET -key $API_KEY -secret $API_SECRET wss://qa-api.leaf.ag/streams/v1/ws/activity
HTTP Request
GET /ws/activity
HTTP Response
JSON stream of Event. (See Event Payload section)
Get an auth token
token=$(gurl -X GET -key $API_KEY -secret $API_SECRET https://qa-api.leaf.ag/streams/v1/token | jq -r .)
echo $token
HTTP Request
GET /token
HTTP Response
Payload: json encoded string representation of the new token.
- 200: Success
- 400: Bad Request
- 401: Invalid credentials
- 500: Internal error.
Subscribe using Websocket with token based auth
token=$(gurl -X GET -key $API_KEY -secret $API_SECRET https://qa-api.leaf.ag/streams/v1/token | jq -r .)
gurl -X GET wss://api.leaf.ag/streams/v1/ws/token/activity/$token
In order to subscribe to a websocket stream without setting the Authorization http header, you need to first
retrieve a token and use it when connecting to the websocket endpoint.
HTTP Request
GET /ws/token/activity/:token
HTTP Response
JSON stream of Event. See Event Response
Create Events
gurl -X POST -key $API_KEY -secret $API_SECRET \
-d '[
{
"event_type": "operator_login",
"user_id": "d640de41-1cd9-4dca-9025-8139cfef7126",
"device_id": "1f4fd6e8-c38e-495d-a084-508a4ff3626b",
"event_time": "2016-04-25T18:02:02Z",
"latitude": -83.2284,
"longitude": 47.3394,
"heading": 1.2,
"speed": 33.5
},
{
"event_type": "operator_logout",
"user_id": "d640de41-1cd9-4dca-9025-8139cfef7126",
"device_id": "1f4fd6e8-c38e-495d-a084-508a4ff3626b",
"event_time": "2016-04-25T18:05:43Z",
"latitude": -83.2284,
"longitude": 43.5336,
"heading": 137.23307,
"speed": 7.3289
},
{
"event_type": "pairing",
"user_id": "d640de41-1cd9-4dca-9025-8139cfef7126",
"managementzone_id": "8b212ec1-0acf-47e9-8144-c05e34f8c558",
"latitude": -28.202939,
"longitude": 33.2389,
"heading": 137.23307,
"speed": 7.3289,
"beacons": [
{
"proximity_uuid": "800e1e57-cae4-4ab2-9e8f-af4e138d7964",
"major": 4637,
"minor": 8776,
"distance": 9.32284
},
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"paired_beacons": [
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"event_time": "2016-04-27T18:11:24Z"
},
{
"event_type": "managementzone_enter",
"user_id": "d640de41-1cd9-4dca-9025-8139cfef7126",
"device_id": "1f4fd6e8-c38e-495d-a084-508a4ff3626b",
"managementzone_id": "8b212ec1-0acf-47e9-8144-c05e34f8c558",
"heading": 137.23307,
"speed": 7.3289,
"beacons": [
{
"proximity_uuid": "800e1e57-cae4-4ab2-9e8f-af4e138d7964",
"major": 4637,
"minor": 8776,
"distance": 9.32284
},
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"paired_beacons": [
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"event_time": "2016-04-27T14:11:24Z"
},
{
"event_type": "managementzone_exit",
"user_id": "d640de41-1cd9-4dca-9025-8139cfef7126",
"managementzone_id": "8b212ec1-0acf-47e9-8144-c05e34f8c558",
"heading": 137.23307,
"speed": 7.3289,
"beacons": [
{
"proximity_uuid": "800e1e57-cae4-4ab2-9e8f-af4e138d7964",
"major": 4637,
"minor": 8776,
"distance": 9.32284
},
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"paired_beacons": [
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077
}
],
"event_time": "2016-04-27T18:11:24Z"
}
]' https://qa-api.leaf.ag/streams/v1/events
HTTP Request
POST /events
Request body should be an array of Event Request objects.
Event Request
This model is only used in the Create Events request.
| Parameters | Type | Required | Description |
|---|---|---|---|
| event_type | string | Yes | The type of the event (see Event Types). |
| event_time | time | Yes | RFC3339 timestamp that specifies when the client observed the event. |
| data | object | No | The event data. The structure of the data depends on the event_type. |
| device_id | string | No | The device id. For iOS, it will be the serial number. |
| user_id | string | No | The ID of the operator who triggered the event. |
| managementzone_id | string | No | The ID of the management zone in which the event occurred. |
| beacons | []Beacon | No | Beacons that were nearby when the event was generated. |
| paired_beacons | []Beacon | No | Beacons that were paired when the event was generated. |
| latitude | float | No | Latitude Location. The units of latitude is degrees. |
| longitude | float | No | Longitude Location. The units of longitude is degrees. |
| heading | float | No | Heading of the device. The units of heading is degrees. |
| speed | float | No | Speed of the device. The units of speed is meters/second. |
HTTP Response
Status Codes
201- Created400- Bad Request401- Unauthorized
Get Events
gurl -X GET -key $API_KEY -secret $API_SECRET https://qa-api.leaf.ag/streams/v1/events
[
{
"event_type": "operator_login",
"user_id": "d640de41-1cd9-4dca-9025-8139cfef7126",
"device_id": "1f4fd6e8-c38e-495d-a084-508a4ff3626b",
"event_time": "2016-04-25T18:02:02Z",
"latitude": -83.2284,
"longitude": 47.3394,
"heading": 1.2,
"speed": 33.5,
"sender": {
"id": "4557f0b8-f230-42c3-ad93-3259bb24c377"
}
},
{
"event_type": "operator_logout",
"user_id": "d640de41-1cd9-4dca-9025-8139cfef7126",
"device_id": "1f4fd6e8-c38e-495d-a084-508a4ff3626b",
"event_time": "2016-04-25T18:05:43Z",
"latitude": -83.2284,
"longitude": 43.5336,
"heading": 137.23307,
"speed": 7.3289,
"sender": {
"id": "4557f0b8-f230-42c3-ad93-3259bb24c377"
}
},
{
"event_type": "pairing",
"user_id": "d640de41-1cd9-4dca-9025-8139cfef7126",
"managementzone_id": "8b212ec1-0acf-47e9-8144-c05e34f8c558",
"latitude": -28.202939,
"longitude": 33.2389,
"heading": 137.23307,
"speed": 7.3289,
"sender": {
"id": "4557f0b8-f230-42c3-ad93-3259bb24c377"
},
"beacons": [
{
"proximity_uuid": "800e1e57-cae4-4ab2-9e8f-af4e138d7964",
"major": 4637,
"minor": 8776,
"distance": 9.32284
},
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"paired_beacons": [
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"event_time": "2016-04-27T18:11:24Z"
},
{
"event_type": "managementzone_enter",
"user_id": "d640de41-1cd9-4dca-9025-8139cfef7126",
"device_id": "1f4fd6e8-c38e-495d-a084-508a4ff3626b",
"managementzone_id": "8b212ec1-0acf-47e9-8144-c05e34f8c558",
"heading": 137.23307,
"speed": 7.3289,
"sender": {
"id": "4557f0b8-f230-42c3-ad93-3259bb24c377"
},
"beacons": [
{
"proximity_uuid": "800e1e57-cae4-4ab2-9e8f-af4e138d7964",
"major": 4637,
"minor": 8776,
"distance": 9.32284
},
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"paired_beacons": [
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"event_time": "2016-04-27T14:11:24Z"
},
{
"event_type": "managementzone_exit",
"user_id": "d640de41-1cd9-4dca-9025-8139cfef7126",
"managementzone_id": "8b212ec1-0acf-47e9-8144-c05e34f8c558",
"heading": 137.23307,
"speed": 7.3289,
"sender": {
"id": "4557f0b8-f230-42c3-ad93-3259bb24c377"
},
"beacons": [
{
"proximity_uuid": "800e1e57-cae4-4ab2-9e8f-af4e138d7964",
"major": 4637,
"minor": 8776,
"distance": 9.32284
},
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"paired_beacons": [
{
"proximity_uuid": "cfb6c6d9-7c0d-4edf-8fc9-775f801afa1b",
"major": 2876,
"minor": 9077,
"distance": 1.2948576
}
],
"event_time": "2016-04-27T18:11:24Z"
},
{
"id": "99ffe889-8702-4cf6-aea4-b77af50dd474",
"user_id": "1bac2df2-790e-4a73-aa53-0bf89463eec3",
"latitude": 37.33233141,
"longitude": -122.0312186,
"heading": 0,
"speed": 0,
"device_id": "54ca6696-2dac-48d2-bfda-a9bcd3c547ef",
"sender": {
"id": "4557f0b8-f230-42c3-ad93-3259bb24c377"
},
"event_type": "location",
"data": {
"equipment_ids": [
"3ebf2a60-05a2-4a5c-ac58-eda735cae99e",
"dc6b5866-9eff-4a28-bb0d-56c3008f28ba"
]
},
"event_time": "2016-04-27T15:57:27Z",
"created_at": "2016-04-27T15:57:27.327205Z"
}
]
HTTP Request
GET /events
Query Parameters
| Paramter | Type | Default | Description |
|---|---|---|---|
| start_date | RFC3339 timestamp | Beginning of time | Return events only if they occurred after this time |
| end_date | RFC3339 timestamp | Now | Return events only if they occurred before this time |
| limit | int | 10 | Return at most limit items |
| include | csv | all | List of event types to include |
| exclude | csv | none | List of event types to exclude |
| beacons | bool | 1 | Set to “0” to disable beacons query |
| skip | int | 0 | Number of events to skip |
Notes
- It is an error to provide both include and exclude parameters.
Valid values for include/exclude parameters
- crud
- managementzone_enter
- managementzone_exit
- location
- operator_login
- operator_logout
- pairing
- turn
- mzoverlay
- farming_activity_start
- farming_activity_end
- action
- pause
- operation_end
HTTP Response
An array of Event Response objects.
Event Response
This model is used for the Get Events response, as well as in the event streams that come from a websocket connection.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Event ID. |
| sender | object | Yes | User that sent the event. See the user model. |
| event_type | string | Yes | Describe the type of event. See Event Types below for valid values. |
| event_time | RFC3339 timestamp | Yes | Time that event was observed by the client. |
| created_at | RFC3339 timestamp | Yes | Time that event was created on the server. |
| data | object | No | Describe event-specific data. See below for the data structure of each event type. |
| device_id | string | No | The device id. For iOS, it will be the serial number. |
| user_id | string | No | The ID of the operator who triggered the event. |
| managementzone_id | string | No | The ID of the management zone in which the event occurred. |
| beacons | []Beacon | No | Beacons that were nearby when the event was generated. |
| paired_beacons | []Beacon | No | Beacons that were paired when the event was generated. |
| lat | float | No | Latitude Location. The units of lat is degrees. |
| long | float | No | Longitude Location. The units of long is degrees. |
| heading | float | No | Heading of the device. The units of heading is degrees. |
| speed | float | No | Speed of the device. The units of speed is meters/second. |
Status Codes
200- OK401- Unauthorized
Events
Event Types
The event types we currently support are:
CRUD Event
CRUD events happen when any resource in the system is created, updated, or deleted.
Notes
- “former_resource” will only be included for “Update” and “Delete” events
- “resource” will not be included for “Delete” events.
Data structure
| Parameter | Type | Description |
|---|---|---|
| resource_type | string | Resource type. Valid values: [“equipment”, “managementzones”, “users”] |
| kind | string | CRUD action. Valid values: [“Insert”, “Update”, “Delete”] |
| resource | string | The JSON encoded new state of the concerned resource. |
| former_resource | string | The JSON encoded old state of the concerned resource. |
gurl -X GET -key $API_KEY -secret $API_SECRET https://qa-api.leaf.ag/streams/v1/events
[
{
"user_id": "999fad7b-b222-4a8c-9d39-96ca4cd9f748",
"event_type": "crud",
"event_time": "2016-04-25T18:09:15Z",
"data": {
"id": "466df65c-7938-4711-b970-d5597dc4b695",
"kind": "Insert",
"resource_type": "equipment",
"resource": "{\"beacon\":{\"major\":2,\"minor\":2,\"uuid\":\"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22\"},\"category\":\"selfpropelled\",\"company_id\":1,\"fields\":[{\"name\":\"field name\",\"value\":\"field value\"}],\"id\":\"466df65c-7938-4711-b970-d5597dc4b695\",\"make\":\"make\",\"model\":\"model\",\"name\":\"myTractor updatee\",\"tags\":[\"tag1\"],\"type\":\"tractor\",\"user_id\":\"999fad7b-b222-4a8c-9d39-96ca4cd9f748\",\"year\":2016}"
}
},
{
"user_id": "999fad7b-b222-4a8c-9d39-96ca4cd9f748",
"event_type": "crud",
"event_time": "2016-04-25T18:09:29Z",
"data": {
"id": "466df65c-7938-4711-b970-d5597dc4b695",
"kind": "Update",
"resource_type": "equipment",
"resource": "{\"beacon\":{\"major\":2,\"minor\":2,\"uuid\":\"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22\"},\"category\":\"selfpropelled\",\"company_id\":1,\"created_at\":\"2016-02-11T04:48:17.30343Z\",\"fields\":[{\"name\":\"field name\",\"value\":\"field value\"},{\"name\":\"field name\",\"value\":\"field value\"}],\"id\":\"466df65c-7938-4711-b970-d5597dc4b695\",\"make\":\"make\",\"model\":\"model\",\"name\":\"myTractor updatee\",\"tags\":[\"tag1\",\"tag1\"],\"type\":\"tractor\",\"updated_at\":\"2016-02-11T04:48:39.985196Z\",\"user_id\":\"999fad7b-b222-4a8c-9d39-96ca4cd9f748\",\"year\":2016}",
"former_resource": "{\"beacon\":{\"major\":2,\"minor\":2,\"uuid\":\"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22\"},\"category\":\"selfpropelled\",\"company_id\":1,\"created_at\":\"2016-02-11T04:48:17.30343Z\",\"fields\":[{\"name\":\"field name\",\"value\":\"field value\"}],\"id\":\"466df65c-7938-4711-b970-d5597dc4b695\",\"make\":\"make\",\"model\":\"model\",\"name\":\"myTractor updatee\",\"tags\":[\"tag1\"],\"type\":\"tractor\",\"updated_at\":\"2016-02-11T04:48:17.30343Z\",\"user_id\":\"999fad7b-b222-4a8c-9d39-96ca4cd9f748\",\"year\":2016}"
}
}
]
CRUD event format.
Location Event
Location events are automatically sent to WebSocket clients when heartbeats are created that contain beacons that are paired with equipment.
| Parameter | Type | Required | Description |
|---|---|---|---|
| user_id | string | no | User ID of the operator. |
| device_id | string | no | Identifier of the device that triggered the event. |
| managementzone_id | string | no | Identifier of the management zone the device was in when it triggered the event. |
| longitude | float64 | yes | Longitude. The units of longitude is degrees. |
| latitude | float64 | yes | Latitude. The units of latitude is degrees. |
| speed | float64 | yes | Speed in meters per second. The units of speed is meters/second. |
| heading | float64 | yes | Heading (0 is due to north, 90 is due to east, etc). The units of speed is degrees. |
| timestamp | int | yes | Time of the GPS data. |
| equipment_ids | []string | no | ID’s of equipment that was present when the location event was triggered. |
gurl -X GET -key $API_KEY -secret $API_SECRET 'https://qa-api.leaf.ag/streams/v1/events?include=location'
[
{
"id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
"user_id": "4ae2a3d6-781e-4599-a66e-e775cb4ce0c5",
"event_type": "location",
"device_id": "22bc394b-78cb-4a4a-b679-abec4348a8a3",
"managementzone_id": "8a41c2f7-3212-4139-9a20-ec5d2971a530",
"longitude": 97.75,
"latitude": 30.25,
"heading": 1.2,
"speed": 33.5,
"timestamp": 0,
"data": {
"equipment_ids": [
"a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
"74786684-bcc7-43ea-98fd-137902b0bb43"
]
},
"event_time": "2016-02-11T03:25:29.034209729Z"
}
]
Location event format.
Operator Login Event
gurl -X POST -key $API_KEY -secret $API_SECRET \
-d '[
{
"event_type": "operator_login",
"user_id": "2acc7975-6f00-4d03-8c77-14f649dea51d",
"event_time": "2016-02-25T15:02:41Z"
}
]' https://qa-api.leaf.ag/streams/v1/events
Operator Login event format.
Operator Logout Event
gurl -X POST -key $API_KEY -secret $API_SECRET \
-d '[
{
"event_type": "operator_logout",
"user_id": "2acc7975-6f00-4d03-8c77-14f649dea51d",
"event_time": "2016-02-25T15:03:04Z"
}
]' https://qa-api.leaf.ag/streams/v1/events
Operator Logout event format.
Pairing Event
gurl -X POST -key $API_KEY -secret $API_SECRET \
-d '[
{
"event_type": "pairing",
"beacons": [
{
"proximity_uuid": "8d442b8c-ce72-4595-a5dc-138f59da2d26",
"major": 1008,
"minor": 7324,
"distance": 21.2
},
{
"proximity_uuid": "3cba88ca-37e8-4463-b402-bd27a714a3de",
"major": 2385,
"minor": 1279,
"distance": 20.18
}
],
"paired_beacons": [
{
"proximity_uuid": "8d442b8c-ce72-4595-a5dc-138f59da2d26",
"major": 1008,
"minor": 7324
},
{
"proximity_uuid": "3cba88ca-37e8-4463-b402-bd27a714a3de",
"major": 2385,
"minor": 1279
}
],
"event_time": "2016-02-25T15:03:04Z"
}
]' https://qa-api.leaf.ag/streams/v1/events
Pairing event format.
Managementzone Enter Event
# Example Request
gurl -X POST -key $API_KEY -secret $API_SECRET \
-d '[
{
"user_id": "7b3db577-6db4-4f5c-89d1-1400e510735a",
"event_type": "managementzone_enter",
"event_time": "2016-03-25T16:31:58Z",
"beacons": [
{
"proximity_uuid": "b745e29a-1768-4660-93ff-109ddfb552f6",
"major": 1002,
"minor": 7327,
"distance": 1.43938
},
{
"proximity_uuid": "f6235a75-0918-47a1-9e83-a7276c4cae95",
"major": 2387,
"minor": 1271,
"distance": 0.23894
}
],
"paired_beacons": [
{
"proximity_uuid": "b745e29a-1768-4660-93ff-109ddfb552f6",
"major": 1002,
"minor": 7327
}
]
}
]' https://qa-api.leaf.ag/streams/v1/events
Managementzone Enter event format.
Managementzone Exit Event
# Example Request
gurl -X POST -key $API_KEY -secret $API_SECRET \
-d '[
{
"event_type": "managementzone_exit",
"event_time": "2016-03-25T16:31:58Z",
"user_id": "7b3db577-6db4-4f5c-89d1-1400e510735a",
"beacons": [
{
"proximity_uuid": "b745e29a-1768-4660-93ff-109ddfb552f6",
"major": 1002,
"minor": 7327,
"distance": 1.43938
},
{
"proximity_uuid": "f6235a75-0918-47a1-9e83-a7276c4cae95",
"major": 2387,
"minor": 1271,
"distance": 0.23894
}
],
"paired_beacons": [
{
"proximity_uuid": "f6235a75-0918-47a1-9e83-a7276c4cae95",
"major": 2387,
"minor": 1271
}
]
}
]' https://qa-api.leaf.ag/streams/v1/events
Managementzone Exit event format.
GetDevices
This API endpoint retrives a list of active devices for a given company.
| Parameter | Type | Option | Description |
|---|---|---|---|
| start_date | RFC3339 timestamp | Beginning of time | Return devices with only if they have data after this time |
| end_date | RFC3339 timestamp | Now | Return devices with only if they have data before this time |
HTTP Request
GET /devices
HTTP Response
[“72b5f1fa-c930-4435-be29-48b5b4773afe”, “1229154a-7f47-407c-bd04-1add5c092fed”]
gurl -key $API_KEY -secret $API_SECRET https://api.leaf.ag/streams/v1/devices
gurl -key $API_KEY -secret $API_SECRET https://api.leaf.ag/streams/v1/devices?start_date=2016-09-09T07:27:20Z&end_date=2016-09-09T17:27:20Z
Errors
The Leaf API uses the following error codes:
| Error Code | Meaning |
|---|---|
| 400 | Bad Request |
| 401 | Unauthorized – Check your token |
| 403 | Forbidden |
| 404 | Not Found |
| 500 | Internal Server Error |
Golang
Why Golang?
Golang (also called Go) started in 2009 as a solution to multi-core application needs.
Strongly Typed / Statically Typed
Having a strongly typed (and statically typed) language makes it easier to have good designs and performant code.
Compiled / Statically Linked
Because Go is compiled, most errors will be caught at compile time instead of runtime. This result in better stability of the product.
On top of being compiled, Go is (can be) statically linked. This means there are no runtime dependencies (no libc, no other shared libraries needed) which make the deployment very easy.
Using Docker, this also results in much smaller images which means faster deployment and faster testing.
Multi-Platform support
Go has a very simple cross compilation system that make it easy to compile for any platform from your local dev environment.
Speed
With a simple benchmark on 3xlarge AWS instance, we reached 4.5M req/sec, which is not even conceivable with most languages out there.
Concurency
Go is natively concurrent, it exposes mechanism like channels in order to make cross-thread communication easy.
Testing
Part of the Go library is the testing “framework” which makes it easy to reliably test the code and generate coverage reports.
Future proof
Go has been rising for the past 3 years and keeps growing. The community is very strong, there are a lot of libraries/drivers/bindings out there for almost any use case. Using Go puts us in a good situation on both product scalability and developer recruitment.
Installation
Non developer
# OSX
brew install golang
# Ubuntu
apt-get install -y golang
Non-developer installation.
If you are not a developer (i.e. just need to install one of the tool or packages), you can install Go using your system’s package manager.
Developer
# Set your environement (should be in your .profile/.bashrc/.zshrc accordingly)
export GOPATH=$HOME/go
export GOROOT=$HOME/goroot
export GOBIN=$GOROOT/bin
export PATH=$GOBIN:$PATH
# Clone the golang repository
git clone https://github.com/golang/go ~/goroot
# Install go1.4
## Copy the cloned repository to temporary goroot
cp -r ~/goroot ~/go1.4
## Checkout go1.4
cd ~/go1.4/src
git checkout go1.4.2
## Install go1.4
GOROOT=~/go1.4 GOBIN=~/go1.4/bin ./make.bash
# Install go1.5
## Checkout go1.5 in final repository
cd ~/goroot/src
git checkout go1.5.3 # (change according to latest go version)
./make.bash
Developer installation.
As a developer, you will most likely need to introspect and interact with the standard libary. It is bad practice to tamper with system installed files which may result in conflicts when updating. A developer should install Go from source.
Documents
Every Go developer should be very familiar with these documents:
A more advanced go developer needs to be familiar with these documents as well:
Finally, here is the semi-official Golang coding style: CodeReviewComments. Note that this document takes precedence over the CodeReviewComments style guide. We are a bit stricter than them.
Update dependencies
As a lot of our dependencies are activelly maintained, the team will endup with different version of them locally. Our vendor system (Godeps for now, native go vendor when 1.6 get stable) fixes those and allow us to not worry about it when running or testing a service. However, when developing, those differences will create conflicts when merging. To avoid this, make sure you are up to date with all your dependencies once in a while.
A simple way to do so is to use go list:
for i in `go list -f '{{range .Imports}}{{. | printf "%s\n"}}{{end}}' github.com/agrarianlabs/... | \grep github | \grep -v agrarianlabs | sort | uniq`; do echo $i; go get -d -u $i/...; done
One liner to update all dependencies in Go.
This commandline is quite simple:
- Lookup all imported package across the whole company’s github with
go list github.com/agrarianlabs/... - Format this to have one import per line (
-ffromgo list) - Filter out stdlib (
\grep github) and the company’s repos (\grep -v agrarianlabs) - Sort and filter out duplicates (
| sort | uniq) - Loop over this list with the for
- At each iteration, update the imported repo (
-u) without reinstalling it (-d)
Go Tools
As we have the tooling, all the following should pass.
goimports
Install: go get golang.org/x/tools/cmd/goimports.
Doc: https://godoc.org/golang.org/x/tools/cmd/goimports. See as well gofmt.
The goimports tool is similar to gofmt but will also handle imports for you. These tools should never complain when running goimports -l (list files with issues).
While the tool is pretty great, it is not perfect.
Formatting Issue
import (
"net"
"sort"
"os"
"github.com/creack/goproxy"
"github.com/creack/ehttp"
"github.com/creack/ehttp/ehttprouter"
)
Bad. Because
goimportsgroups the import statements while keeping the user’s ones, you will often endup in a situation where your import block looks like this.
import (
"net"
"os"
"sort"
"github.com/creack/goproxy"
"github.com/creack/ehttp"
"github.com/creack/ehttp/ehttprouter"
)
Good.
import (
"net"
"os"
"sort"
"code.google.com/p/uuid"
"github.com/creack/goproxy"
"github.com/creack/ehttp"
"github.com/creack/ehttp/ehttprouter"
)
Also good. This is perfectly fine but should be done in a way where it makes sense.
Because goimports groups the import statements while keeping the user’s ones, you will often
end up in a situation where your import block contains extra new lines.
This is an issue that can’t be solved automatically by the tool. The import block should be consistent.
This does not forbid manual grouping, but it should be done only for external imports.
Import issue
The way goimports works is by looking into your $GOPATH for a package matching the functions/types/variables that you are using without having the imported path.
This is very useful for fast development but can easily backfire when you have multiple packages with the same name in your gopath.
You should always double check the import paths that are automatically added by the tool to make sure that it imported the expected packages.
gofmt -s
This tool comes with Go. Doc: https://golang.org/cmd/gofmt/
goimports is very similar to gofmt, however, it does not support the -s.
For this reason, the developer is expected to check the code against both goimports and gofmt -s.
The -s means “simplify”. It will catch code that is not necessary.
Note: While this tool is great, it is not retro compatible with older version of Go.
Example: for _ := range []string{}{} would pass in Go1.3 but would fail in 1.4 as it should be for range []string{}{}.
This is not a big issue since the 1.4 format would not compile in 1.3.
errcheck
Install: go get github.com/kisielk/errcheck. Doc: https://github.com/kisielk/errcheck/blob/master/README.md
errcheck will analyze your code and make sure that there are no function calls returning an error that the developer missed.
It can be a hassle when you don’t want to check the error like when doing defer elem.Close(), but it is expected.
You should always explicitly check your errors. You might discard some errors, but all error handling should still be explicit and documented. Example:
package main
import (
"log"
"os"
)
func main() {
f, err := os.Open("file")
if err != nil {
log.Fatal(err)
}
// The error is explicitly discarded and documented.
defer func() { _ = f.Close() }() // Best effort.
_ = f
}
go vet
go vet comes with golang. Doc: https://golang.org/cmd/vet/
This will check various common errors like wrong usage of formatting function, misuse of the unsafe package, invalid json tags, etc.
golint
Install: go get github.com/golang/lint/golint . Doc: https://github.com/golang/lint/blob/master/README.md
golint will enforce everything that is not enforced within the specifications.
Partial list:
- Enforce comments format
- Make sure the acronyms are used properly
- Make sure there is no C (or other language) syntax
- Enforce the camelCase (from effective go, but not enforced by gofmt)
- Redundant naming
- Much more
gometalinter
Install: go get github.com/alecthomas/gometalinter; gometalinter --install --update. Doc: https://github.com/alecthomas/gometalinter/blob/master/README.md
Note: this tool is not mandatory, but is always a plus when it does not complain.
gometalinter will call all the tools above and more.
Some of the things it does that other tools does not: - Duplicate code check - Complexity check - Bad variable shadowing check - Much more
Coding style
Go provides fantastic tooling and documentation around the styling and on how to write proper Go code. While those are a good start, they do not handle all the cases and are often a bit too permissive. This document aims to have code that can be easily read and understood by anyone by enforcing stricter rules.
Even with all those awesome tools, there are still some things that are not automatically enforced.
Variable declaration / instantiation
The reason behind this section is to have consistent code across the whole code base.
Go provides multiple ways to instantiate a variable: var, var () and :=. While they are all good, they should be used wisely.
var statement
func test() error {
var conf Config
if err := json.Unmarshal(&conf); err != nil {
return err
}
return nil
}
var globalCount int // Note: bad example for production. Should probably be locked.
func main() {
}
The var statement should be used when instantiating a single variable (i.e. not related to others) and when there is no assignement (i.e. use the zero value of the type).
var() block
package main
import "errors"
// Common errors.
var (
ErrNotFound = errors.New("not found")
ErrInvalidFormat = errors.New("invalid format")
ErrUnexpectedEOF = errors.New("unexpected EOF")
)
func main() {
}
Good. the errors are a single group declared together.
package main
import "errors"
// Common errors.
var ErrNotFound = errors.New("not found")
var ErrInvalidFormat = errors.New("invalid format")
var ErrUnexpectedEOF = errors.New("unexpected EOF")
func main() {
}
Bad. The errors declarations should be grouped. When not grouped,
gofmtwill not indent properly.
func main() {
var (
port = flag.String("p", 0, "port to use")
host = flag.String("h", "", "host to use")
)
flag.Parse()
}
Good. the command-line flags are a single logical group.
The var() block statement should be used when declaring/instantiating related variables.
:= operator
if err := fct(); err != nil {
return err
}
Good. The error is properly scoped.
func TestText(t *testing.T) {
text := "abc"
// ...
if expect, got := "abc", text; expect != got {
t.Fatalf("Unexpected text.\nExpect:\t%s\nGot:\t%s", expect, got)
}
}
Good.
textis isolated and we still create anexpectvariable in theifblock.
var i = 0
Bad. Should be either
i := 0orvar i int.
var i int
Good.
i := 0
Good if isolated.
var i int
var j int
Bad. “Related” variables should be declared together.
var (
i int
j int
)
Good. “Related” variables are in the same declaration block.
var i, j int
Acceptable if isolated and without affectation.
i := 1
j := 2
Acceptable if isolated.
var (
unrelatedLock sync.Mutex
i int
j int
)
Bad. As the lock is not relate to the indexes, it should not be in the same
var()block.
var unrelatedLock sync.Mutex
var (
i int
j int
)
Good.
iandjare grouped, other variables are isolated.
var (
i int
j int
)
unrelatedLock := sync.Mutex{}
Also good if there are no other unrelated variables.
i := 0
j := 0
k := 0
Bad. The variabels are grouped. It should be a
var()block.
var (
i int
j int
k int
)
Good. The variables are grouped so we declare it in a
var()block.
var i, j, k int
Acceptable. For simalar and/or grouped declaration, we can use the
,operator.
The main usage of := is to declare and assign a single variable with a non-zero value.
It is also often used when inlining declaration/instantiation inside a scope like an if block.
Even when variables are related, it is acceptable to use :=, but if you have more than 2 statements,
it should probably be moved into a var() block.
Type instantiation
type Test struct{}
Type for following examples.
v := new(Test)
Bad, should use literal instead.
v := &Test{}
v2 := Test{}
Good. Whether it is a pointer or not, with initialization or not, we are using literals.
m := make(map[string]string)
s := make([]string, 0)
Bad, should use literal instead.
m := map[string]string{}
s := []string{}
Good. Even when there are no element to initialize with, we are using literals.
s := make([]string, 16)
s2 := make([]string, 0, 16)
Good when needed, no other choice.
ch := make(chan struct{})
ch2 := make(chan struct{}, 1)
Good. No other choice.
type Test2 struct{
i *int
}
tt := &Test2{i: new(int)}
Acceptable for native types when needed. Not acceptable with custom types, should use literal instead.
Between new, make and literal, it can be confusing how to instantiate a type.
For consistency, we will not be using new nor make unless in a very specific situation:
- pre allocate slice (or map)
- create a channel (language constraint)
- create a native type variable and get its address.
Scope
func test() error {
var conf Config
buf, err := json.Marshal(conf)
if err != nil {
log.Println(err)
}
err = json.Unmarshal(buf, &conf)
if err != nil {
log.Println(err)
}
// If the `Marshal` failed, here, `err` will have the value of `Unmarhsal` even if it didn't fail.
// Scoping it within the `if` with `:=` would isolate the values.
if err = json.Unmarshal(buf, &conf); err != nil {
log.Println(err)
}
// On this example, the error is scoped, however, it still does not help, we should have used `:=` instead of `=`.
}
Example (bad)
The reason behind this section is to avoid unexpected variable assignment. That way, all variable are scopped to the smallest possible block.
Reflect
The reflect package can be a great tool, but it is also the source of all evil.
Unless you don’t have the choice, don’t use reflect.
It can be acceptable to use reflect.DeepEqual in tests when it makes sense.
Do not ever use reflect.DeepEqual if you don’t need it, i.e. don’t do if reflect.DeepEqual(21, 42) {}.
Prefer if 21 == 42 {}.
Defer
var (
l sync.Mutex
m = map[string]string{}
)
func heavilyUsedFunction() {
l.Lock()
defer l.Unlock()
m["hello"] = "world"
}
Bad. Small operation, defering the unlock slows down the function.
var (
l sync.Mutex
m = map[string]string{}
)
func heavilyUsedFunction() {
l.Lock()
m["hello"] = "world"
l.Unlock()
}
Good. Small operation, not defering makes it much faster. NOTE: Calling l.Unlock without a defer would not be executed in case of a panic.
buf := &bytes.Buffer{}
tw := tar.NewWriter(buf)
defer tw.Close()
Bad,
f.Close()returns an error that is not checked.errchecktool will complain. In this case, the Close() would write the footer of the archive so it is very important to check. NOTE: In tests, defers like this are acceptable when you want to discard the error.
f, err := os.Open("file")
if err != nil {
return err
}
defer func() { _ = f.Close() }() // Best effort.
Good. Even when discarding the error, we are explicit about it.
Defer statements are very useful, however, you need to be careful when using them.
You need to keep in mind that a defer induces a closure, which might impact the performances of your code. Also, defering a statement is no excuse not to check the error value returned.
Panic
Don’t panic. Ever.
If you need to handle errors during init, prefer Fatalf as the stack trace would not be meaningful, anywhere else in the code, you should return the error or send it via a channel if it is in a goroutine.
The exception for this is in the init() functions of libraries where you might have to use a different logger that your own. In this case, it is best to use panic() in the init.
Named return parameters
func getUser() (string, string, string, int, error) {
return "Guillaume", "user@domain.com", "Leaf, LLC", 1, nil
}
Bad. The prototype is not explicit enough for documentation.
func getUser() (name, email, company string, uid int, err error) {
name, email, company, uid, err = "Guillaume", "user@domain.com", "Leaf, LLC", 1, nil
return
}
Bad. Don’t use naked returns.
func getUser() (name, email, company string, uid int, err error) {
return "Guillaume", "user@domain.com", "Leaf, LLC", 1, nil
}
Good. The documentation is clear and the return is correct.
func check() (err error) {
return nil
}
Bad. Don’t use named return when not needed. In this case, having
errin the prototype does not brind any value.
func check() error {
return nil
}
Good. The prototype is self-describing, not using named return.
func check() (err error) {
defer func() {
if err != nil {
// TODO: do something
return
}
if e1 := cleanup(); e1 != nil {
err = e1
}
}
}
Good. The named return parameter is not needed for documentation but makes the error check / set in the defer possible.
Named returns are mainly used for documentation purpose. When a prototype is not explicit enough, it might be interesting to add named returns.
An other situation where it is acceptable to use named returns is to handle error in the defer.
Embedded structures
type Test struct {
a int
*Controller
}
Bad. The embedded struct should be the first element.
type Test struct {
*Controller
a int
}
Good.
When using embedded structures, they should be the first thing in the “parent” structure.
JSON tags
If you have a structure that will be marshalled to json, it should be explicitly tagged.
Not doing so and relying on Go for the naming will cause issues and conflicts for retro-compatibility. Even if the json name is the same as the field name, it should be explicitly written.
Same idea applies to any other tags like db, xml, yaml, etc..
Import Dot
Even though the Golang’s CodeReviewComments allows it, it is meant for handling circular dependency issues.
You should not use . import. It makes the code much more difficult to read and understand. Your code should
not have circular dependency issues anyway.
Import Rename
import (
"fmt"
"log"
"gopkg.in/mgo.v2"
)
Bad. The import path does not match the package name.
import (
"fmt"
"log"
mgo "gopkg.in/mgo.v2"
)
Good. We rename the import with the package name. This does not impact the code, but make it possible to use tag lookup.
When importing multiple package with the same name, you have to rename one of them. When doing so, the name should be short, similar to the original one and make sense. It also need to be camelCase if it is longer.
To make it easier for people using tools like gocode on their editor, we also force the import rename of
package name not matching the import path.
Naming conventions
Golang
Golang (also called Go) uses camelCase. golint will validates names. Don’t use snake_case.
Constants should follow the same rules and not be all upper case like in C or other languages.
JSON
When using json, we are using snake_case.
Database
In databases, we are using snake_case as well with plural names for tables.
The primary key, when unique is expected to be the singular version of the table name suffixed with _id.
Example:
- Table:
users - Primary key:
user_id
Format
The database names format (tables and fields) should be snake_case.
Table name
CREATE TABLE users (
)
The table names should be plural.
Primary key
CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
)
The primary key of a table should be the singular name of the table suffixed with _id.
Infrastructure
The goal of the infrastructure is to be scalable and reliable.
In order to accomplish this, we are using tools and services like Docker, Tutum or AWS on the infrastructure side, but what really helps is the concept of micro services.
We are going to develop a serie of micro service with dedicated tasks. Those unit will be updated and scaled individually, which means that even during an upgrade, we will always have a service up and running.
The general idea is to have something like this:

Global Architecture
The infrastructure runs on Amazon Web Services, a.k.a. AWS.
AWS allows us to configure the network in a way that allows public access only via the load balancer (ELB). To do so, we are using the VPC and Security Group from EC2.
The idea being to have something like this:
- ELB is accessible from the public
- Router is accessible only from ELB
- Services are accessible only from the Router.
Using the security group, we grant exta access to specific IPs like the office.
As we want to have testing and staging environments we need to replicate this schema. In order to isolate environements from each other, we are using VPC from EC2.
VPCs
VPCs or Virtual Private Clouds are an AWS unit that groups servers together in a given subnet(s). It allows us to isolate our clusters at a network level.
Because the CI / Build server needs certificate to pull code and push to tutum, it is isolated in its own VPC and can’t communicate with any service directly. This require a dedicated security group.

Security Groups
Security Groups are the “firewall rules” for a given VPC. Because security groups are scoped by VPC, we have some duplicate settings for testing, staging and production.

Add yourself to a group
You need access to the AWS console to add yourself to a group.
From the AWS console, navigate to EC2 then Security Groups. Once there, select the group you are looking for. If at home, use the “untrusted” networks, at the office, we can use the “trusted” ones. This allows us to control and easily cleanup the access list. On the security group you are looking for, select the “Inbound” tab and “Edit” to add your IP to the given port.
Docker / Deployment
Docker
Tutum
Services, Stack, Node, Cluster
Tutum has some top level concepts.
ECS, Elastic Beanstalk, Others.
Before chosing Tutum, we tried several solutions.
ECS: very difficult to use, manual cluster management EB: difficult to use, lot of overhead, lot of AWS services involded, annoying deployment process, specific DSLs to learn. Fleet: not reliable enough RancherOS: forces us to use their discovery system and OS Kubernete: way overcomplicated, too manyy moving parts Mesos: too heavy, does too much, stip learning curve. OpenShift: too heavy, does too much, stip learning curve.
Router / Discovery
Configuration management
For configuration management, at the moment, we are using Zookeeper. The main reason for this choice is the reliability of it and the ease of use from the driver.
We eventually will migrate to Etcd or more likely Consul.
Continuous Integration
We are usnig Jenkins as our CI. NOTE: Jenkins is now deprecated and being replaced by CircleCI.
Because everything runs on Docker, there is no special configuration for it.
We just need Docker, git and make.
Github configuration
In order to hook a Github repository to Jenkins, we need to go to the repo’s settings, Webhooks & services tab. From there we are going to add a new service: "Jenkins (Github plugin)”.
The hook url is: http://jenkins.leaf.ag:8080/github-webhook/

Security Group
As Github needs to have access to Jenkins to trigger the builds, we need to grant Github access. We need to add the following ips on tcp/8080: (doc):
- 192.30.252.0/22
- 2620:112:3000::/44 (not supported)
Service Discovery
Events Topology
The following diagram indicates the types of events that flow through the system, who produces them, and who consumes them.
