Skip to main content

Base URL

Production base URL:
https://api.trailflow.ai
All Partner API routes are server-to-server HTTPS endpoints.

Authentication Model

Partner API credentials are scoped to the customer organization, allowed scopes, and key mode. After Trailflow enables test access for an organization, approved partner admins may create additional test keys in the authenticated partner portal. live keys remain Trailflow-managed. Send the key as a bearer token:
Authorization: Bearer <Partner API key>
Trailflow manages access at two layers:
  • organization-level access: not enabled, test access enabled, live access enabled, or disabled
  • key-level mode: test or live
New integrations should start with test keys. live keys are issued only for approved production use. Current supported scopes:
  • loads:read
  • loads:write
  • customers:read
  • carriers:read
  • tracking:read
  • webhooks:manage

Route-to-scope mapping

  • GET /v1/loads and GET /v1/loads/{id} require loads:read
  • POST /v1/loads, POST /v1/loads/upsert, and POST /v1/loads/{id}/status require loads:write
  • GET /v1/customers requires customers:read
  • GET /v1/carriers requires carriers:read
  • GET /v1/tracking/{loadId} requires tracking:read
  • POST /v1/webhooks, GET /v1/webhooks, and DELETE /v1/webhooks/{id} require webhooks:manage

Test And Live Behavior

Test and live credentials use the same base URL and contract shape.
  • test keys are for setup, UAT, and partner-side validation
  • approved partner admins may self-serve additional test keys after test access is enabled
  • live keys are for approved production traffic only
  • test traffic still executes against the same production API surface for the approved customer organization; Trailflow does not provide isolated sandbox data
  • partners should use non-production webhook endpoints while validating with test keys
  • rate limits are configured per key and can differ by client regardless of mode
If a key is missing, invalid, revoked, blocked by org access state, or does not have the required scope, the API returns a structured error response.

Required Request Headers

All requests:
  • Authorization: Bearer <api key>
  • Content-Type: application/json for JSON request bodies
The following routes require:
  • Idempotency-Key: <unique key>
Idempotency-Key today:
  • POST /v1/loads
  • POST /v1/loads/upsert
  • POST /v1/loads/{id}/status
  • POST /v1/webhooks

Idempotency Rules

Trailflow treats Idempotency-Key as replay protection for those four POST routes. Behavior:
  • same key plus same request body: Trailflow replays the original response when the original ledger entry still exists
  • same key plus different request body: Trailflow returns 409 idempotency_conflict
  • same key while the original request is still processing: Trailflow returns 409 idempotency_in_progress
  • rate limits are checked before replay lookup, so duplicate requests can still receive 429 rate_limited
Trailflow enforces a 24 hour idempotency replay window for those four POST routes. Reusing the same key after that window starts a fresh request.

Rate Limits

Each API client has a server-side per-minute rate limit. Current policy:
  • default issuance target: 60 requests per minute
  • allowed configured range: 1 to 600 requests per minute
  • rate-limit responses use HTTP 429
  • response error code: rate_limited
  • rate-limited responses include Retry-After: 60
Partners should not rely on bursty retry loops to recover from 429 responses. If sustained throughput needs differ from the initial integration posture, Trailflow must approve a higher limit explicitly.

Request Tracing

Every authenticated Partner API route returns a requestId on success and failure. The unauthenticated /health/api endpoint is the exception. Example response envelope:
{
  "data": {
    "id": "load_123"
  },
  "requestId": "req_123"
}
When opening a support ticket, include:
  • customer organization
  • request timestamp
  • route called
  • HTTP status code
  • requestId
Do not send raw API keys or webhook signing secrets over email or chat.

Error Model

Errors are returned as JSON with a stable envelope:
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded"
  },
  "requestId": "req_123"
}
Common status and error code combinations:
  • 400 bad_request
  • 400 missing_idempotency_key
  • 401 unauthorized
  • 403 forbidden
  • 403 partner_access_not_enabled
  • 403 live_access_not_enabled
  • 403 partner_access_disabled
  • 404 not_found
  • 409 conflict
  • 409 idempotency_conflict
  • 409 idempotency_in_progress
  • 429 rate_limited
  • 500 internal_error

Example Requests

List loads:
curl --request GET \
  --url 'https://api.trailflow.ai/v1/loads?eligibleOnly=true&limit=100' \
  --header 'Authorization: Bearer <api key>'
Create a webhook subscription:
curl --request POST \
  --url 'https://api.trailflow.ai/v1/webhooks' \
  --header 'Authorization: Bearer <api key>' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: wh_20260321_001' \
  --data '{
    "url": "https://partner.example/webhooks/trailflow",
    "eventTypes": ["load.created", "load.status_changed"]
  }'