Make your first MVMNT API call in about 5 minutes: get a token, create a customer, create two locations, then create an order.
- MVMNT API credentials:
client_idandclient_secret(see Authentication) - A shell with
curl - For code samples:
- JavaScript: Node 18+
- Python: Python 3.9+ and
pip install requests
- Token:
https://api.mvmnt.io/oauth2/token - API:
https://api.mvmnt.io/v1
export MVMNT_CLIENT_ID="YOUR_CLIENT_ID"
export MVMNT_CLIENT_SECRET="YOUR_CLIENT_SECRET"curl -sS -X POST "https://api.mvmnt.io/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=${MVMNT_CLIENT_ID}" \
-d "client_secret=${MVMNT_CLIENT_SECRET}"Expected output (HTTP 200 OK):
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}Copy the access_token into an environment variable:
export MVMNT_TOKEN="PASTE_ACCESS_TOKEN_HERE"// quickstart-token.mjs
const tokenRes = await fetch("https://api.mvmnt.io/oauth2/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.MVMNT_CLIENT_ID,
client_secret: process.env.MVMNT_CLIENT_SECRET,
}),
});
if (!tokenRes.ok) {
console.error(await tokenRes.text());
process.exit(1);
}
const { access_token, token_type, expires_in } = await tokenRes.json();
console.log({ token_type, expires_in, access_token: access_token.slice(0, 24) + "..." });Run:
node quickstart-token.mjs# quickstart_token.py
import os
import requests
res = requests.post(
"https://api.mvmnt.io/oauth2/token",
headers={"Content-Type": "application/x-www-form-urlencoded"},
data={
"grant_type": "client_credentials",
"client_id": os.environ["MVMNT_CLIENT_ID"],
"client_secret": os.environ["MVMNT_CLIENT_SECRET"],
},
timeout=30,
)
res.raise_for_status()
data = res.json()
print({"token_type": data["token_type"], "expires_in": data["expires_in"], "access_token_prefix": data["access_token"][:24] + "..."})Run:
python quickstart_token.pyThis creates a shipper profile you can attach locations and orders to.
curl -sS -X POST "https://api.mvmnt.io/v1/customers" \
-H "Authorization: Bearer ${MVMNT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Industrial Plastics",
"status": "ACTIVE",
"website": "https://acme-industrial-plastics.example",
"phoneNumber": "+1-214-555-0199"
}'Expected output (HTTP 201 Created):
{
"object": "CUSTOMER",
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Industrial Plastics",
"status": "ACTIVE",
"createdAt": "2026-01-22T18:30:00Z",
"updatedAt": "2026-01-22T18:30:00Z",
"deletedAt": null
}Save the id as CUSTOMER_ID for the next step.
// quickstart-customer.mjs
async function getToken() {
const res = await fetch("https://api.mvmnt.io/oauth2/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.MVMNT_CLIENT_ID,
client_secret: process.env.MVMNT_CLIENT_SECRET,
}),
});
if (!res.ok) throw new Error(await res.text());
const json = await res.json();
return json.access_token;
}
const token = await getToken();
const customerRes = await fetch("https://api.mvmnt.io/v1/customers", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Acme Industrial Plastics",
status: "ACTIVE",
website: "https://acme-industrial-plastics.example",
phoneNumber: "+1-214-555-0199",
}),
});
if (!customerRes.ok) {
console.error(await customerRes.text());
process.exit(1);
}
const customer = await customerRes.json();
console.log({ customerId: customer.id, name: customer.name, status: customer.status });Run:
node quickstart-customer.mjs# quickstart_customer.py
import os
import requests
token_res = requests.post(
"https://api.mvmnt.io/oauth2/token",
headers={"Content-Type": "application/x-www-form-urlencoded"},
data={
"grant_type": "client_credentials",
"client_id": os.environ["MVMNT_CLIENT_ID"],
"client_secret": os.environ["MVMNT_CLIENT_SECRET"],
},
timeout=30,
)
token_res.raise_for_status()
token = token_res.json()["access_token"]
customer_res = requests.post(
"https://api.mvmnt.io/v1/customers",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={
"name": "Acme Industrial Plastics",
"status": "ACTIVE",
"website": "https://acme-industrial-plastics.example",
"phoneNumber": "+1-214-555-0199",
},
timeout=30,
)
customer_res.raise_for_status()
customer = customer_res.json()
print({"customerId": customer["id"], "name": customer["name"], "status": customer["status"]})Run:
python quickstart_customer.pyCreate two locations under the customer you just created.
Replace CUSTOMER_ID with the customer id from Step 3.
curl -sS -X POST "https://api.mvmnt.io/v1/locations" \
-H "Authorization: Bearer ${MVMNT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"customerId": "CUSTOMER_ID",
"name": "Acme Plastics DC - Dallas, TX",
"key": "QS-LOC-DAL-001",
"type": "SHIPPER",
"isAppointmentRequired": true,
"notes": "Appointments required. Receiving: Mon-Fri 07:00-15:00",
"internalNotes": "Check in at gate, then door assignment by radio"
}'Expected output (HTTP 201 Created):
{
"object": "LOCATION",
"id": "770e8400-e29b-41d4-a716-446655440000",
"customerId": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Plastics DC - Dallas, TX",
"key": "QS-LOC-DAL-001",
"type": "SHIPPER",
"isAppointmentRequired": true,
"notes": "Appointments required. Receiving: Mon-Fri 07:00-15:00",
"internalNotes": "Check in at gate, then door assignment by radio",
"createdAt": "2026-01-22T18:32:00Z",
"updatedAt": "2026-01-22T18:32:00Z",
"deletedAt": null
}Save the id as PICKUP_LOCATION_ID.
curl -sS -X POST "https://api.mvmnt.io/v1/locations" \
-H "Authorization: Bearer ${MVMNT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"customerId": "CUSTOMER_ID",
"name": "Acme Retail DC - Atlanta, GA",
"key": "QS-LOC-ATL-001",
"type": "RECEIVER",
"isAppointmentRequired": false,
"notes": "No appointment. Deliveries: Mon-Fri 08:00-16:00"
}'Expected output (HTTP 201 Created): same shape as above. Save the id as DELIVERY_LOCATION_ID.
This creates a draft order with two stops and basic freight details.
Replace PICKUP_LOCATION_ID and DELIVERY_LOCATION_ID with your location IDs from Step 4.
curl -sS -X POST "https://api.mvmnt.io/v1/orders" \
-H "Authorization: Bearer ${MVMNT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"key": "QS-ORDER-2026-0001",
"stops": [
{ "sequence": 1, "type": "pickup", "locationId": "PICKUP_LOCATION_ID" },
{ "sequence": 2, "type": "delivery", "locationId": "DELIVERY_LOCATION_ID" }
],
"freight": {
"handlingUnits": 20,
"handlingUnitType": "pallet",
"weight": 18400,
"weightUnit": "lbs"
}
}'Expected output (HTTP 201 Created):
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"friendlyId": "ORD-12345",
"key": "QS-ORDER-2026-0001",
"status": "draft",
"stops": [
{ "sequence": 1, "type": "pickup", "locationId": "770e8400-e29b-41d4-a716-446655440000" },
{ "sequence": 2, "type": "delivery", "locationId": "880e8400-e29b-41d4-a716-446655440002" }
],
"freight": {
"handlingUnits": 20,
"handlingUnitType": "pallet",
"weight": 18400,
"weightUnit": "lbs"
},
"createdAt": "2026-01-22T18:35:00Z",
"updatedAt": "2026-01-22T18:35:00Z"
}Save friendlyId (example: ORD-12345). You will use it to fetch the order.
// quickstart-order.mjs
async function getToken() {
const res = await fetch("https://api.mvmnt.io/oauth2/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.MVMNT_CLIENT_ID,
client_secret: process.env.MVMNT_CLIENT_SECRET,
}),
});
if (!res.ok) throw new Error(await res.text());
return (await res.json()).access_token;
}
const token = await getToken();
const orderRes = await fetch("https://api.mvmnt.io/v1/orders", {
method: "POST",
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify({
key: "QS-ORDER-2026-0001",
stops: [
{ sequence: 1, type: "pickup", locationId: "PICKUP_LOCATION_ID" },
{ sequence: 2, type: "delivery", locationId: "DELIVERY_LOCATION_ID" },
],
freight: { handlingUnits: 20, handlingUnitType: "pallet", weight: 18400, weightUnit: "lbs" },
}),
});
if (!orderRes.ok) {
console.error(await orderRes.text());
process.exit(1);
}
const order = await orderRes.json();
console.log({ orderId: order.id, friendlyId: order.friendlyId, status: order.status, key: order.key });Run:
node quickstart-order.mjs# quickstart_order.py
import os
import requests
token_res = requests.post(
"https://api.mvmnt.io/oauth2/token",
headers={"Content-Type": "application/x-www-form-urlencoded"},
data={
"grant_type": "client_credentials",
"client_id": os.environ["MVMNT_CLIENT_ID"],
"client_secret": os.environ["MVMNT_CLIENT_SECRET"],
},
timeout=30,
)
token_res.raise_for_status()
token = token_res.json()["access_token"]
order_res = requests.post(
"https://api.mvmnt.io/v1/orders",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={
"key": "QS-ORDER-2026-0001",
"stops": [
{"sequence": 1, "type": "pickup", "locationId": "PICKUP_LOCATION_ID"},
{"sequence": 2, "type": "delivery", "locationId": "DELIVERY_LOCATION_ID"},
],
"freight": {
"handlingUnits": 20,
"handlingUnitType": "pallet",
"weight": 18400,
"weightUnit": "lbs",
},
},
timeout=30,
)
order_res.raise_for_status()
order = order_res.json()
print({"orderId": order.get("id"), "friendlyId": order.get("friendlyId"), "status": order.get("status"), "key": order.get("key")})Run:
python quickstart_order.pyIf you set a unique key on the order, you can pull it back without storing an ID.
curl -sS "https://api.mvmnt.io/v1/orders?key=QS-ORDER-2026-0001" \
-H "Authorization: Bearer ${MVMNT_TOKEN}"Expected output (HTTP 200 OK):
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"friendlyId": "ORD-12345",
"key": "QS-ORDER-2026-0001",
"status": "draft",
"createdAt": "2026-01-22T18:35:00Z",
"updatedAt": "2026-01-22T18:35:00Z"
}For error response format and status codes, see Error Handling.
400with{"error":"invalid_client" ...}- Client ID or secret is wrong, or copied with extra whitespace.
- Re-copy credentials from your MVMNT account manager, then retry.
400with{"error":"invalid_grant" ...}grant_typemust beclient_credentials.
Common causes:
- Missing header:
Authorization: Bearer <token> - Token expired (default lifetime is 3600 seconds)
Fix:
- Request a new token, then retry the API request.
- Required fields are missing or field values do not match allowed enums.
- Print the response body and fix the field called out in
error.details.
- A
keymust be unique per resource type. - Change the
key(example:QS-ORDER-2026-0002) and retry.
- You hit the rate limit.
- Back off and retry after a short delay. Use exponential backoff for automation.
- Verify you can reach
https://api.mvmnt.iofrom your environment. - If you are behind a proxy, configure your HTTP client to use it.