Integration Guide

This guide covers the public API for integrating external systems with Vectis OMS.

Support: support@stoalogistics.com
Terms of Service: stoalogistics.com/terms


Authentication

All API requests require a Bearer token in the Authorization header.

Obtaining a Token

Request an access token from the OAuth endpoint:

curl -X POST https://api.vectisoms.app/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Making Requests

curl https://acme.vectisoms.app/api/orders \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Generate client credentials from Settings → API Access in your Vectis dashboard.


Ecommerce Integration

Push orders from your ecommerce platform to Vectis for fulfillment.

Check Inventory Before Selling

GET /api/inventory?sku=WIDGET-001
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "items": [{
    "id": "inv_123",
    "sku": "WIDGET-001",
    "quantityAvailable": 150,
    "quantityReserved": 10,
    "warehouseId": "wh_456"
  }]
}

Create an Order

POST /api/orders
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "externalId": "SHOPIFY-12345",
  "customerId": "cust_jane123",
  "customerEmail": "jane@example.com",
  "items": [
    { "sku": "WIDGET-001", "quantity": 2, "unitPrice": 29.99 }
  ],
  "shippingAddresses": [{
    "name": "Jane Smith",
    "line1": "123 Main St",
    "city": "Austin",
    "region": "TX",
    "postalCode": "78701",
    "country": "US"
  }],
  "billingAddress": {
    "name": "Jane Smith",
    "line1": "123 Main St",
    "city": "Austin",
    "region": "TX",
    "postalCode": "78701",
    "country": "US"
  }
}

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "total": 59.98,
  "itemCount": 2,
  "createdAt": "2026-01-26T12:00:00Z"
}

Get Order Status

GET /api/orders/{orderId}
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "shipped",
  "total": 59.98,
  "itemCount": 2,
  "items": [...],
  "createdAt": "2026-01-26T12:00:00Z"
}

Cancel an Order

Cancel an order before it ships:

POST /api/orders/{orderId}/cancel
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "reason": "Customer requested cancellation"
}

Product Catalog Sync

Keep your product catalog in sync with Vectis.

Create a Product

POST /api/products
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "sku": "WIDGET-001",
  "name": "Premium Widget",
  "weight": { "value": 1.5, "unit": "lb" },
  "dimensions": {
    "length": 10, "width": 8, "height": 4, "unit": "in"
  }
}

Get Product by SKU

GET /api/products/sku/WIDGET-001
Authorization: Bearer YOUR_ACCESS_TOKEN

Update a Product

PATCH /api/products/{productId}
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "name": "Premium Widget v2",
  "weight": { "value": 1.2, "unit": "lb" }
}

Inventory Adjustments

Adjust inventory levels when stock changes outside of Vectis.

PATCH /api/inventory/{id}/adjust
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "adjustment": -5,
  "reason": "Damaged goods removed"
}

Webhook Subscriptions

Subscribe to real-time events for order and inventory changes.

Create a Webhook Subscription

POST /api/webhooks
Content-Type: application/json
Authorization: Bearer YOUR_ACCESS_TOKEN

{
  "url": "https://your-system.com/webhooks/vectis",
  "events": ["order.shipped", "order.delivered", "inventory.low_stock"],
  "secret": "your-webhook-secret"
}

Available Events

EventDescription
order.createdNew order received
order.confirmedOrder confirmed for fulfillment
order.shippedOrder has shipped (includes tracking)
order.deliveredOrder delivered to customer
order.cancelledOrder was cancelled
inventory.adjustedInventory level changed
inventory.low_stockStock below reorder threshold

Webhook Payload Example

{
  "event": "order.shipped",
  "timestamp": "2026-01-27T14:30:00Z",
  "data": {
    "orderId": "550e8400-e29b-41d4-a716-446655440000",
    "trackingNumber": "1Z999AA10123456784",
    "carrier": "UPS"
  }
}

Verify webhooks using HMAC-SHA256 signature in the X-Webhook-Signature header.


Carrier Tracking Webhooks

Vectis receives tracking updates from carriers via webhook endpoints.

Supported carriers: fedex, ups, usps, dhl, ontrac, lasership, amazon

POST /api/webhooks/carriers/{carrier}/tracking
Content-Type: application/json

Payload format varies by carrier. Vectis normalizes all events internally.


Pagination

List endpoints return paginated results:

GET /api/orders?limit=50&offset=100
ParameterTypeDefaultMaxDescription
limitinteger20100Items per page
offsetinteger0-Items to skip

Response includes pagination metadata:

{
  "orders": [...],
  "pagination": {
    "limit": 50,
    "offset": 100,
    "total": 523
  }
}

Idempotency

Order creation is idempotent based on externalId. If you submit an order with an externalId that already exists, the existing order is returned instead of creating a duplicate.

This allows safe retries on network failures:

# First request (creates order)
POST /api/orders
{ "externalId": "SHOP-12345", ... }
# Response: 201 Created

# Retry (returns existing order)
POST /api/orders
{ "externalId": "SHOP-12345", ... }
# Response: 200 OK (same order returned)

Webhook Signature Verification

Outbound webhooks include an HMAC-SHA256 signature for verification.

Headers:

  • X-Webhook-Signature: HMAC signature (hex)
  • X-Webhook-Timestamp: Unix timestamp (seconds)

Verification (Node.js):

const crypto = require('crypto');

function verifyWebhook(rawBody, signature, timestamp, secret) {
  // Reject old timestamps (replay protection)
  const age = Date.now() / 1000 - parseInt(timestamp);
  if (age > 300) return false; // 5 minute window

  // Compute expected signature
  const signedPayload = `${timestamp}.${rawBody}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Timing-safe comparison
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Rate Limits

  • 1000 requests per minute per access token

Response headers:

  • X-RateLimit-Limit: Max requests per window
  • X-RateLimit-Remaining: Remaining requests
  • X-RateLimit-Reset: Window reset (Unix timestamp)

When rate limited (HTTP 429), check the Retry-After header for seconds to wait.


Error Handling

All errors return a consistent format:

{
  "error": "ValidationError",
  "message": "SKU is required",
  "details": { "field": "sku" }
}
Status CodeMeaning
400Bad request / validation error
401Invalid or missing access token
404Resource not found
409Conflict (e.g., order already cancelled)
429Rate limit exceeded
500Internal server error

API Reference

Full OpenAPI specification available at the API Reference page.

Need Help?