API Docs
Security

Authentication

The 3PLGuys API supports two authentication methods. Choose the one that fits your use case.

API Keys

Static bearer tokens for programmatic access. No token exchange or refresh flow required.

  • Scripts, bots, and cron jobs
  • Server-to-server integrations
  • CI/CD pipelines
  • Simple — just set the header and go

OAuth 2.0

Authorization Code flow for apps that act on behalf of other users and organizations.

  • Third-party integrations
  • Multi-tenant apps
  • User-facing applications
  • MCP servers and AI agents

API Keys

API keys are the simplest way to authenticate. Create a key, include it as a Bearer token, and start making requests. Each key is tied to a specific organization and has scoped permissions.

1Create an API key

Go to Account Settings → API Keys in your 3PLGuys dashboard. Select the organization, choose the scopes your integration needs, and optionally set an expiration date.

Important

The raw API key is shown only once when you create it. Copy it immediately and store it securely. If you lose it, you'll need to create a new key.
2Use it in requests

Include the API key as a Bearer token in the Authorization header. That's it — no token exchange, no refresh flow.

Example request with API key
curl -X GET https://api.3plguys.com/v0/shipments \
-H "Authorization: Bearer 3pl_your_api_key_here"
3Manage your keys

From the API Keys page you can:

  • View all your keys, their scopes, and last used time
  • Edit the name, scopes, or expiration of existing keys
  • Revoke keys instantly — any integration using that key will stop working immediately

API keys are prefixed with 3pl_ so you can easily identify them in your codebase. Keys that have an expiration date will automatically stop working after that date.

OAuth 2.0

Use OAuth 2.0 when building applications that need to access other users' organizations. The user authorizes your app, you exchange the code for tokens, and use the access token in requests.

Create an OAuth app

Go to Account Settings → OAuth Apps in your 3PLGuys dashboard. Give your app a name, add redirect URIs, and select the scopes it needs. You'll receive a client_id and client_secret.

Important

The client secret is shown only once when you create the app. Copy it immediately. If you lose it, you can rotate the secret from the OAuth Apps page, but the old secret will stop working immediately.

OAuth 2.0 Authorization Code Flow

Your AppRedirect to 3PLGuysUser AuthorizesGet CodeExchange for TokensAccess API
Token RefreshToken ExpiredUse Refresh TokenNew Access Token
1Redirect to authorize

Redirect the user's browser to the 3PLGuys authorization page. The user will log in and grant your application access to the requested scopes.

Authorization URL
GET https://api.3plguys.com/oauth/authorize
?response_type=code
&client_id=your_client_id
&redirect_uri=https://yourapp.com/callback
&scope=inventory shipments
&state=random_state_value
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
ParameterTypeDescription
response_type*stringMust be "code"
client_id*stringYour OAuth client ID
redirect_uri*stringURL to redirect back to after authorization. Must match a registered redirect URI.
scopestringSpace-separated list of scopes (see below). Optional; omit to request the app's default scopes.
statestringRandom value to prevent CSRF attacks. Returned unchanged in the callback.
code_challengestringPKCE challenge: base64url(SHA256(code_verifier)). Recommended for all clients, required for public clients.
code_challenge_methodstringMust be "S256" when using PKCE.

After the user authorizes, they are redirected back to your redirect_uri with a code query parameter:

https://yourapp.com/callback?code=abc123&state=random_state_value
2Exchange code for tokens

Make a server-side POST request to exchange the authorization code for an access token and refresh token. This must be done within a few minutes of receiving the code. The token endpoint expects application/x-www-form-urlencoded per the OAuth 2.0 spec.

POST https://api.3plguys.com/oauth/token
grant_type=authorization_code
&code=abc123
&client_id=your_client_id
&client_secret=your_client_secret
&redirect_uri=https://yourapp.com/callback
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
ParameterTypeDescription
grant_type*stringMust be "authorization_code"
code*stringThe authorization code from step 1
client_id*stringYour OAuth client ID
client_secret*stringYour OAuth client secret
redirect_uristringMust match the redirect_uri from step 1. Optional but recommended.
code_verifierstringPKCE verifier. Required if code_challenge was sent in step 1.
resourcestringResource indicator (RFC 8707). Optional, accepted for MCP/OAuth compatibility.
3Refresh when expired

Access tokens expire after 1 hour. Use the refresh token to obtain a new access token without requiring the user to re-authorize. The response includes a new refresh token — always store and use the latest one.

POST https://api.3plguys.com/oauth/token
grant_type=refresh_token
&refresh_token=def456...
&client_id=your_client_id
&client_secret=your_client_secret

Token Response

Both the authorization code exchange and refresh token requests return the same response format.

200 OK
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "def456...",
"scope": "inventory shipments"
}
ParameterTypeDescription
access_token*stringJWT token to include in API requests
token_type*stringAlways "Bearer"
expires_in*numberToken lifetime in seconds (3600 = 1 hour)
refresh_token*stringUse to obtain a new access token when the current one expires
scope*stringScopes granted to this token

Using Your Token

Include the access token in the Authorization header of every API request.

Example request with OAuth token
curl -X GET https://api.3plguys.com/v0/shipments \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Scopes

Scopes control which parts of the API your application or key can access. Both API keys and OAuth tokens use the same scopes. Request only the scopes you need.

ScopeAccessEndpoints
inventoryProducts, cartons, stock levels/v0/inventory/*
shipmentsAll shipment operations (PnP, SPD, list, cancel)/v0/shipments/*
locationsWarehouse locations/v0/warehouses
invoicesInvoice history and line items (read-only)/v0/invoices/*
notificationsUnified activity feed and action items/v0/notifications
recommendationsAI-powered operational suggestions/v0/recommendations
user-accountOrganization and user account details/v0/organization

PKCE (Proof Key for Code Exchange)

PKCE (RFC 7636) prevents authorization code interception attacks. It is required for public clients (SPAs, mobile apps, CLI tools, MCP servers) and recommended for all clients.

Generate PKCE values (Node.js)
import crypto from "crypto";
// 1. Generate a random code verifier (43-128 chars)
const codeVerifier = crypto.randomBytes(32).toString("base64url");
// 2. Hash it with SHA-256 to produce the code challenge
const codeChallenge = crypto
.createHash("sha256")
.update(codeVerifier)
.digest("base64url");
// Send code_challenge + code_challenge_method=S256 in the authorize URL
// Send code_verifier in the token exchange POST

How it works

  1. Your app generates a random code_verifier string and computes code_challenge = base64url(SHA256(code_verifier)).
  2. Send code_challenge and code_challenge_method=S256 in the authorization URL (step 1).
  3. When exchanging the code (step 2), send the original code_verifier. The server verifies SHA256(code_verifier) == code_challenge.
  4. If the challenge was sent but the verifier is missing or wrong, the token request is rejected.

Error Responses

401 Unauthorized

Missing, invalid, or expired token / API key. For OAuth, refresh the token and retry. For API keys, verify the key is correct and not expired.

403 Forbidden

The token or API key does not have the required scope for this endpoint.

400 Bad Request

Invalid grant type, expired authorization code, or mismatched redirect URI.

Security Best Practices

  • Never expose client secrets or API keys in frontend code, public repositories, or client-side JavaScript.
  • Store credentials securely on the server side. Use environment variables or a secrets manager.
  • Always use HTTPS for all API requests.
  • Use the minimum scopes your integration actually needs.
  • Set expiration dates on API keys where possible.
  • Revoke keys and secrets immediately if they are compromised.
  • For OAuth, validate the state parameter in the callback to prevent CSRF attacks.
  • For OAuth, always use the latest refresh token from the most recent response.