API Docs

Getting Started

Go from zero to your first API call in under 5 minutes.

Prerequisites

You need a 3PLGuys account with API access enabled. Contact your account manager or reach out via 3plguys.com/contact-us to get started.

What You'll Build

By the end of this guide, you'll have a working script that authenticates with the 3PLGuys API and retrieves your shipments. Here's the flow:

Authentication Flow
Register App
Get client credentials
Get Auth Code
User authorization
Exchange Token
Code → access token
Call API
Use Bearer token

Step 1: Get Your Credentials

Once your developer application is approved, you'll receive:

CredentialDescription
Client IDPublic identifier for your app (e.g. myapp_abc123...)
Client SecretPrivate key — keep this secure, never expose in client-side code
Allowed ScopesWhich API sections you can access (e.g. shipments, inventory)

Security

Never commit your client secret to version control. Use environment variables or a secrets manager.


Step 2: Obtain an Access Token

Exchange your credentials for an access token using the OAuth 2.0 Authorization Code flow.

curl -X POST https://api.3plguys.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'grant_type=authorization_code&code=AUTH_CODE_FROM_REDIRECT&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&redirect_uri=https%3A%2F%2Fyourapp.com%2Fcallback'
const res = await fetch("https://api.3plguys.com/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
code: "AUTH_CODE_FROM_REDIRECT",
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
redirect_uri: "https://yourapp.com/callback",
}),
});
const { access_token, refresh_token } = await res.json();
import httpx
res = httpx.post("https://api.3plguys.com/oauth/token", data={
"grant_type": "authorization_code",
"code": "AUTH_CODE_FROM_REDIRECT",
"client_id": os.environ["CLIENT_ID"],
"client_secret": os.environ["CLIENT_SECRET"],
"redirect_uri": "https://yourapp.com/callback",
})
data = res.json()
access_token = data["access_token"]
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyBpcyBh...",
"scope": "shipments inventory"
}

The access_token is valid for 1 hour. Use the refresh_token to obtain new access tokens without re-authenticating.


Step 3: Make Your First API Call

Use your access token to fetch shipments:

curl https://api.3plguys.com/v0/shipments?take=5 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
const res = await fetch("https://api.3plguys.com/v0/shipments?take=5", {
headers: { Authorization: `Bearer ${access_token}` },
});
const shipments = await res.json();
console.log(`Found ${shipments.length} shipments`);
res = httpx.get(
"https://api.3plguys.com/v0/shipments",
params={"take": 5},
headers={"Authorization": f"Bearer {access_token}"},
)
shipments = res.json()
print(f"Found {len(shipments)} shipments")
[
{
"id": "42",
"status": "pending",
"type": "outbound-pickpack",
"warehouse": {
"id": "1",
"name": "Main Warehouse"
},
"notes": "",
"cartons": [
{
"id": "5",
"name": "24-Pack",
"quantity": 6,
"contents": [
{ "productId": "1", "sku": "ABC123", "productName": "Egg Shells", "quantity": 24 }
]
}
],
"totalCartonQuantity": 6,
"totalProductQuantity": 144,
"createdAt": "2025-01-15T10:30:00Z",
"updatedAt": "2025-01-15T10:30:00Z"
}
]

That's it — you're integrated!


Step 4: Explore the Sandbox

Before pointing at production, test your integration against the sandbox:

EnvironmentBase URL
Productionhttps://api.3plguys.com
Sandboxhttps://sandbox.3plguys.com

The sandbox has completely separate data from production. Use your sandbox OAuth credentials to test without affecting real inventory or shipments.

curl https://sandbox.3plguys.com/v0/shipments \
-H "Authorization: Bearer YOUR_SANDBOX_TOKEN"

Step 5: Handle Token Refresh

Access tokens expire after 1 hour. Here's how to refresh automatically:

async function refreshAccessToken(refreshToken) {
const res = await fetch("https://api.3plguys.com/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
}),
});
const data = await res.json();
// Important: store the NEW refresh token — it rotates each time
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresIn: data.expires_in,
};
}
def refresh_access_token(refresh_token: str) -> dict:
res = httpx.post("https://api.3plguys.com/oauth/token", data={
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": os.environ["CLIENT_ID"],
"client_secret": os.environ["CLIENT_SECRET"],
})
data = res.json()
# Important: store the NEW refresh token — it rotates each time
return {
"access_token": data["access_token"],
"refresh_token": data["refresh_token"],
"expires_in": data["expires_in"],
}

Refresh token rotation

Each token refresh returns a new refresh token. Always store and use the latest one. The previous refresh token is invalidated.


What's Next?

Automate Shipments

Create and manage SPD shipments end-to-end with the full draft → submit → track lifecycle.

Sync Inventory

Keep stock levels in sync across systems with polling and incremental sync patterns.

OAuth Deep Dive

Token refresh, scopes, security patterns, and building a production-ready auth wrapper.


More Guides