> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.tryenvoy.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.tryenvoy.ai/_mcp/server.

# Create a load

POST https://tryenvoy.ai/api/v1/loads
Content-Type: application/json

Create or update a freight load. A load with the same tms_load_id or load_number is updated: 201 when the load was created, 200 when an existing load was updated.

**Authentication:** API key required.

Reference: https://docs.tryenvoy.ai/api-reference/endpoints/loads/create

## Servers

- `https://tryenvoy.ai/api/v1` (Production, default)
- `https://staging.tryenvoy.ai/api/v1` (Staging)

## Request

### Headers

- `Idempotency-Key` (string, optional) — A unique key (up to 255 printable ASCII characters, e.g. a UUID) that makes a retry of this request safe. Envoy stores the response for 24 hours; a retry with the same key and the same request returns it again with `Idempotent-Replayed: true` instead of running twice. The same key with a different request returns 422 `idempotency_key_reused`; while the first request is still running, 409 `idempotency_key_in_progress`. 5xx and 429 responses are not stored.

### Body (application/json)

This endpoint expects a TMSLoadRequest.

- `load_number` (string, required) — Unique load reference number
- `equipment_type` (enum, required) — Equipment type used for transport
  - Allowed values: `VAN`, `REEFER`, `FLATBED`, `STEPDECK`, `RGN`, `CONESTOGA`, `BOX_TRUCK`, `CONTAINER`, `INTERMODAL`, `POWER_ONLY`, `TANKER`, `LOWBOY`, `HOTSHOT`, `AUTO_CARRIER`, `DRY_BULK`, `DRY_TEAM`, `REEFER_TEAM`, `LTL`, `OTHER`, `VAN/REEFER`
- `stops` (list of Stop, required) — Ordered list of stops (pickup and dropoff) - minimum 2 required
- `equipment_type_raw` (string, optional, nullable) — Original equipment-type string from the source TMS (e.g. 'Van/Reefer 53'', 'Dry Van 53FT'). Stored verbatim for audit; not used for matching.
- `mode` (enum, optional, nullable) — Transport mode, when supplied by the source
  - Allowed values: `FTL`, `PTL`, `LTL`, `DED`, `RAIL`, `PASS`
- `tms_load_id` (string, optional, nullable) — Your TMS's ID for the load. Envoy matches updates on it (then on load_number), so keep it stable for the life of the load.
- `tms_status` (string, optional, nullable) — Status supplied by the source TMS; no status is inferred when omitted
- `weight` (double, optional, nullable) — Weight of the load (must be non-negative)
- `distance` (double, optional, nullable) — Number of miles to drive (must be non-negative)
- `commodity` (string, optional, nullable) — Commodity being hauled
- `handling_unit_count` (integer, optional, nullable) — Number of freight handling units
- `handling_unit_type` (string, optional, nullable) — Type of freight handling unit, e.g. pallet or case
- `po_numbers` (list of string, optional, nullable) — List of PO or pickup numbers
- `reference_numbers` (list of ReferenceNumber, optional, nullable) — Typed reference numbers for the load, as sent by the source TMS. po_numbers remains the PO-only projection used by matching/search.
- `posted_rate` (integer, optional, nullable) — Posted rate in USD
- `max_buy_rate` (integer, optional, nullable) — Maximum buy rate in USD
- `suggested_market_rate` (integer, optional, nullable) — Suggested market rate in USD
- `length_feet` (integer, optional, nullable) — Length in feet (must be non-negative)
- `carrier_completed_rate` (double, optional, nullable) — Rate the carrier completed the load at, in USD
- `customer_rate` (double, optional, nullable) — Rate quoted to the customer, in USD
- `margin` (double, optional, nullable) — Margin in USD as reported by the source TMS
- `hold_load` (boolean, optional, default: false) — Whether the source TMS has this load on hold; held loads are hidden from web/extension
- `action` (enum, optional, nullable) — Caller intent for this row (CSV upload). NEW = create (error if it already exists); UPDATE = update (error if it does not exist); INACTIVATE = update + hold_load=true. When omitted, the row is upserted (legacy behavior).
  - Allowed values: `NEW`, `UPDATE`, `INACTIVATE`
- `temperature_setting_minimum` (integer, optional, nullable) — Minimum temperature setting in degrees
- `temperature_setting_maximum` (integer, optional, nullable) — Maximum temperature setting in degrees
- `temperature_run_type` (string, optional, nullable) — Temperature run type (e.g., continuous, cycle)
- `last_reported_state` (string, optional, nullable) — Last reported state for tracking
- `last_reported_city` (string, optional, nullable) — Last reported city for tracking
- `last_reported_long` (double, optional, nullable) — Last reported longitude (-180 to 180, nullable when not moving)
- `last_reported_lat` (double, optional, nullable) — Last reported latitude (-90 to 90, nullable when not moving)
- `tms_carrier_id` (string, optional, nullable) — Your TMS's ID for the carrier hauling the load
- `carrier_name` (string, optional, nullable) — Name of the carrier
- `customer_name` (string, optional, nullable) — Name of the customer (shipper)
- `customer_ref` (string, optional, nullable) — Customer/shipper entity identifier from the TMS (the scheduling feed's customer_tms_id) — not a per-shipment reference number
- `tms_sales_rep_names` (string, optional, nullable) — Sales representative name(s) reported by the source TMS; not an Envoy user assignment
- `carrier_rep` (Contact, optional, nullable) — Carrier Sales Representative contact details
- `driver` (Contact, optional, nullable) — Driver contact details
- `special_instructions` (list of string, optional, nullable) — Special handling instructions provided by the shipper/broker; verbalized to carriers during voice calls
- `notes` (string, optional, nullable) — Internal notes for the load. Shown in Envoy; the AI agent reads them only if your organization turns that on (carrier-facing text belongs in special_instructions).
- `custom_fields` (map from string to any, optional, nullable) — Additional custom fields for source-specific data

## Response

### 200

An existing load was updated

- `id` (string, required) — Envoy load ID
- `operation` (enum, required) — Whether the load was created or updated
  - Allowed values: `created`, `updated`
- `message` (string, required)
- `processed_at` (datetime, required)
- `load_id` (string, required, deprecated) — Deprecated: the same load as `id`, without the `load_` prefix. Use `id`.
- `success` (boolean, optional, default: true)

### 201

Successful Response

- `id` (string, required) — Envoy load ID
- `operation` (enum, required) — Whether the load was created or updated
  - Allowed values: `created`, `updated`
- `message` (string, required)
- `processed_at` (datetime, required)
- `load_id` (string, required, deprecated) — Deprecated: the same load as `id`, without the `load_` prefix. Use `id`.
- `success` (boolean, optional, default: true)

## Errors

### 401 Unauthorized Error

Not authenticated - missing or invalid token/API key

- `any`

### 403 Forbidden Error

Insufficient permissions for this operation

- `any`

### 409 Conflict Error

`idempotency_key_in_progress`: a request with this Idempotency-Key is still running; retry later

- `any`

### 422 Unprocessable Entity Error

Validation Error; or `idempotency_key_reused`: this Idempotency-Key was already used for a different request

- `detail` (list of ValidationError, optional)

### 429 Too Many Requests Error

Rate limit exceeded - see Retry-After header

- `any`

## Types

### Stop

Stop information including location, timing, and contacts

- `location` (Location, required) — Location details of the stop
- `id` (string, optional, nullable) — Envoy's id for the stop (a UUID), set on every stop Envoy returns. Never send your own stop identifier here: put it in pickup_delivery_number or reference_numbers.
- `stop_number` (integer, optional, nullable) — Sequence number of the stop
- `is_pickup` (boolean, optional, nullable) — True if this stop is a pickup
- `is_dropoff` (boolean, optional, nullable) — True if this stop is a dropoff
- `expected_date` (datetime, optional, nullable) — Expected date of activity at this stop
- `contact` (Contact, optional, nullable) — Contact details at the stop (singular, for backward compatibility)
- `type` (enum, optional, nullable) — Type of stop: exactly `Pickup` or `Dropoff` (case-sensitive; other spellings are rejected)
  - Allowed values: `Pickup`, `Dropoff`
- `planned` (PlannedArrival, optional, nullable) — Planned arrival window
- `pickup_delivery_number` (string, optional, nullable) — PO or pickup number; also the place for the source TMS's own stop identifier
- `contacts` (list of Contact, optional, nullable) — List of contacts at this stop
- `actual_stop_times` (ActualStopTimes, optional, nullable) — Actual arrival and departure times
- `carrier_eta` (datetime, optional, nullable) — Carrier's estimated time of arrival
- `reference_numbers` (list of ReferenceNumber, optional, nullable) — Typed reference numbers for this stop, as sent by the source TMS
- `notes` (string, optional, nullable) — Stop notes from the source TMS. DISPLAY ONLY — kept out of the voice/email agent context; carrier-facing text belongs in special_instructions.
- `special_instructions` (list of string, optional, nullable) — Stop-level handling instructions (titled sections), verbalized to carriers

### ReferenceNumber

One typed reference number from a source TMS (stop- or load-level). ``type`` is the source's own label ("Shipper Reference Number", "PO Number", "Shipment Id", ...) kept as display text rather than an enum: every TMS names these differently and customers define their own beyond any standard table, so an enum would silently drop the custom ones. Callers that need typed routing resolve the label at the source boundary (see ``tai/reference_types.py``) and keep the raw string here.

- `value` (string, required) — The reference number itself
- `type` (string, optional, nullable) — Source TMS label for this reference (display text)

### Contact

Contact information for various parties involved in the load

- `type` (enum, optional, nullable) — Role of the contact (for TMS compatibility)
  - Allowed values: `CARRIER`, `DISPATCHER`, `DRIVER`, `TENDERING`, `QUOTING`, `OTHER`
- `first_name` (string, optional, nullable) — First name of the contact
- `last_name` (string, optional, nullable) — Last name of the contact
- `phone` (string, optional, nullable) — Phone number — stored as E.164 when parseable, None otherwise
- `email` (string, optional, nullable) — Email address

### ValidationError

- `loc` (list of ValidationErrorLocItems, required)
- `msg` (string, required)
- `type` (string, required)
- `input` (any, optional)
- `ctx` (ValidationErrorCtx, optional)

### Location

Location information for stops

- `city` (string, required) — City of the location
- `state` (string, required) — State or province code
- `street` (string, optional, nullable) — Street address of the location
- `postal_code` (string, optional, nullable) — Postal or ZIP code
- `country` (string, optional, nullable, default: US) — Country code (ISO-3166-1 alpha-2), defaults to US
- `company_name` (string, optional, nullable) — Company name at this location
- `county` (string, optional, nullable) — County name
- `latitude` (double, optional, nullable) — Latitude coordinate (-90 to 90)
- `longitude` (double, optional, nullable) — Longitude coordinate (-180 to 180)

### PlannedArrival

Planned arrival window for a stop

- `start` (datetime, optional, nullable) — Earliest arrival time
- `end` (datetime, optional, nullable) — Latest arrival time

### ActualStopTimes

Actual stop times for tracking when arrival/departure occurred

- `arrival_datetime` (datetime, optional, nullable) — Actual arrival timestamp
- `departure_datetime` (datetime, optional, nullable) — Actual departure timestamp

### ValidationErrorLocItems

### ValidationErrorCtx

## Examples

### Example 1

**Request**

```json
{
  "load_number": "string",
  "equipment_type": "VAN",
  "stops": [
    {
      "location": {
        "city": "string",
        "state": "string"
      }
    }
  ]
}
```

**Response**

```json
{
  "id": "load_00000000-0000-0000-0000-000000000000",
  "operation": "created",
  "message": "string",
  "processed_at": "2024-01-15T09:30:00Z",
  "load_id": "string",
  "success": true
}
```

**SDK Code**

```python
import requests

url = "https://tryenvoy.ai/api/v1/loads"

payload = {
    "load_number": "string",
    "equipment_type": "VAN",
    "stops": [{ "location": {
                "city": "string",
                "state": "string"
            } }]
}
headers = {"Content-Type": "application/json"}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://tryenvoy.ai/api/v1/loads';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"load_number":"string","equipment_type":"VAN","stops":[{"location":{"city":"string","state":"string"}}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://tryenvoy.ai/api/v1/loads"

	payload := strings.NewReader("{\n  \"load_number\": \"string\",\n  \"equipment_type\": \"VAN\",\n  \"stops\": [\n    {\n      \"location\": {\n        \"city\": \"string\",\n        \"state\": \"string\"\n      }\n    }\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://tryenvoy.ai/api/v1/loads")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"load_number\": \"string\",\n  \"equipment_type\": \"VAN\",\n  \"stops\": [\n    {\n      \"location\": {\n        \"city\": \"string\",\n        \"state\": \"string\"\n      }\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://tryenvoy.ai/api/v1/loads")
  .header("Content-Type", "application/json")
  .body("{\n  \"load_number\": \"string\",\n  \"equipment_type\": \"VAN\",\n  \"stops\": [\n    {\n      \"location\": {\n        \"city\": \"string\",\n        \"state\": \"string\"\n      }\n    }\n  ]\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://tryenvoy.ai/api/v1/loads', [
  'body' => '{
  "load_number": "string",
  "equipment_type": "VAN",
  "stops": [
    {
      "location": {
        "city": "string",
        "state": "string"
      }
    }
  ]
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://tryenvoy.ai/api/v1/loads");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"load_number\": \"string\",\n  \"equipment_type\": \"VAN\",\n  \"stops\": [\n    {\n      \"location\": {\n        \"city\": \"string\",\n        \"state\": \"string\"\n      }\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = [
  "load_number": "string",
  "equipment_type": "VAN",
  "stops": [["location": [
        "city": "string",
        "state": "string"
      ]]]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://tryenvoy.ai/api/v1/loads")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Example 2

**Request**

```json
{
  "load_number": "string",
  "equipment_type": "VAN",
  "stops": [
    {
      "location": {
        "city": "string",
        "state": "string"
      }
    }
  ]
}
```

**Response**

```json
{
  "id": "load_00000000-0000-0000-0000-000000000000",
  "operation": "created",
  "message": "string",
  "processed_at": "2024-01-15T09:30:00Z",
  "load_id": "string",
  "success": true
}
```

**SDK Code**

```python
import requests

url = "https://tryenvoy.ai/api/v1/loads"

payload = {
    "load_number": "string",
    "equipment_type": "VAN",
    "stops": [{ "location": {
                "city": "string",
                "state": "string"
            } }]
}
headers = {"Content-Type": "application/json"}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://tryenvoy.ai/api/v1/loads';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"load_number":"string","equipment_type":"VAN","stops":[{"location":{"city":"string","state":"string"}}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://tryenvoy.ai/api/v1/loads"

	payload := strings.NewReader("{\n  \"load_number\": \"string\",\n  \"equipment_type\": \"VAN\",\n  \"stops\": [\n    {\n      \"location\": {\n        \"city\": \"string\",\n        \"state\": \"string\"\n      }\n    }\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://tryenvoy.ai/api/v1/loads")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"load_number\": \"string\",\n  \"equipment_type\": \"VAN\",\n  \"stops\": [\n    {\n      \"location\": {\n        \"city\": \"string\",\n        \"state\": \"string\"\n      }\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://tryenvoy.ai/api/v1/loads")
  .header("Content-Type", "application/json")
  .body("{\n  \"load_number\": \"string\",\n  \"equipment_type\": \"VAN\",\n  \"stops\": [\n    {\n      \"location\": {\n        \"city\": \"string\",\n        \"state\": \"string\"\n      }\n    }\n  ]\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://tryenvoy.ai/api/v1/loads', [
  'body' => '{
  "load_number": "string",
  "equipment_type": "VAN",
  "stops": [
    {
      "location": {
        "city": "string",
        "state": "string"
      }
    }
  ]
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://tryenvoy.ai/api/v1/loads");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"load_number\": \"string\",\n  \"equipment_type\": \"VAN\",\n  \"stops\": [\n    {\n      \"location\": {\n        \"city\": \"string\",\n        \"state\": \"string\"\n      }\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = [
  "load_number": "string",
  "equipment_type": "VAN",
  "stops": [["location": [
        "city": "string",
        "state": "string"
      ]]]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://tryenvoy.ai/api/v1/loads")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```