Authentication
All integration endpoints use the same JWT bearer token you get from a standard WardenIQ dashboard sign-in. The token carries your tenant ID, user ID, and role, so every API call is automatically scoped to your tenant's data.
Two layers of security
Integration endpoints require both:
- A service token (long-lived JWT, machine-to-machine)
- An HMAC-SHA256 signature of the request body
The service token identifies which tenant is calling. The signature proves the request wasn't tampered with in transit and blocks replay attacks. A leaked service token on its own is not sufficient to write to your account — the attacker also needs the signing secret, which is never sent over the wire.
Step 1: Generate a service token
Service tokens are long-lived (1 year by default) and tied to a specific name for audit purposes. They are issued by an admin or owner on your tenant.
Using your admin dashboard JWT (obtained by signing in at wardeniq.com/dashboard and reading localStorage.quoteiq_token), call:
curl -X POST https://wardeniq.com/api/integrations/tokens \
-H "Authorization: Bearer YOUR_ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{"name": "tms-production"}'
The response returns the plaintext token once. Save it immediately — it cannot be retrieved again.
{
"id": 1,
"name": "tms-production",
"scope": "integrations:write",
"expires_at": "2027-04-14T18:00:00Z",
"token": "eyJ0eXAiOiJKV1Qi...",
"warning": "Save this token now. It will not be shown again."
}
Step 2: Generate an HMAC signing secret
One-time setup. Run once per tenant for the TMS integration:
curl -X POST https://wardeniq.com/api/integrations/secrets \
-H "Authorization: Bearer YOUR_ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{"name": "tms"}'
Response (plaintext secret returned once):
{
"id": 1,
"name": "tms",
"secret": "k3z7Pq2A9fL5wR8vT1mN6yB4hJ0cX...",
"warning": "Save this secret now. It will not be shown again."
}
Step 3: Sign and send each request
For every API call, compute the signature over {unix_timestamp}.{request_body} using HMAC-SHA256 with your signing secret, then send these headers:
Authorization: Bearer <SERVICE_TOKEN>
X-WardenIQ-Timestamp: <unix_epoch_seconds>
X-WardenIQ-Signature: sha256=<hex_digest>
Content-Type: application/json
Full curl example (bash)
#!/bin/bash
SERVICE_TOKEN="eyJ0eXAiOiJKV1Qi..."
SECRET="k3z7Pq2A9fL5wR8vT1mN6yB4hJ0cX..."
BODY='{"lane_ref":"1041-030-025","truck_cost":2250,"carrier_name":"Best Freight Inc","carrier_mc":"MC-123456"}'
TS=$(date +%s)
SIGNED_PAYLOAD="${TS}.${BODY}"
SIG=$(printf "%s" "$SIGNED_PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -X POST https://wardeniq.com/api/integrations/tms/shipment-update \
-H "Authorization: Bearer $SERVICE_TOKEN" \
-H "X-WardenIQ-Timestamp: $TS" \
-H "X-WardenIQ-Signature: sha256=$SIG" \
-H "Content-Type: application/json" \
-d "$BODY"
Example in Python
import hmac, hashlib, time, json, requests
SERVICE_TOKEN = "eyJ0eXAiOiJKV1Qi..."
SECRET = "k3z7Pq2A9fL5wR8vT1mN6yB4hJ0cX..."
body = json.dumps({
"lane_ref": "1041-030-025",
"truck_cost": 2250,
"carrier_name": "Best Freight Inc",
"carrier_mc": "MC-123456",
}, separators=(',', ':')) # compact — same bytes server will verify
ts = str(int(time.time()))
signed = f"{ts}.{body}".encode()
sig = hmac.new(SECRET.encode(), signed, hashlib.sha256).hexdigest()
r = requests.post(
"https://wardeniq.com/api/integrations/tms/shipment-update",
headers={
"Authorization": f"Bearer {SERVICE_TOKEN}",
"X-WardenIQ-Timestamp": ts,
"X-WardenIQ-Signature": f"sha256={sig}",
"Content-Type": "application/json",
},
data=body, # NOT json= — we need the exact bytes we signed
)
print(r.status_code, r.json())
requests.post(url, json=payload), requests will re-serialize the JSON and the bytes won't match your signature. Always pre-serialize to a string, hash that string, and send the same string as data=.
Error responses
401 Unauthorized— missing or invalid token403 Forbidden— token is valid but lacks permission for the requested tenant scope404 Not Found— resource (likelane_ref) does not exist in your tenant400 Bad Request— malformed payload (missing required fields, invalid types)
Rate limiting
Integration endpoints are rate-limited to 10 requests per second per tenant. If your TMS pushes updates in bursts, add a small backoff on 429 Too Many Requests responses.
Security notes
- Never commit tokens to source control. Use environment variables or a secret manager.
- Rotate service tokens annually or whenever a team member with access leaves.
- All API traffic is served over TLS. Plain HTTP requests are redirected to HTTPS.
- If a token is compromised, contact [email protected] immediately to revoke it.