Skip to main content

Order Management Guide

This guide covers how to manage orders programmatically using the VesuvioPay Order Management API, including creating orders from external platforms, updating order status, and handling cancellations.

Overview​

The VesuvioPay Order Management API enables you to:

  • Retrieve orders from your store
  • Get detailed order information
  • Create orders from external platforms
  • Update order status and metadata
  • Cancel orders with automatic refunds
  • Link existing orders to platform orders

Authentication​

All Order API endpoints require a Private API Key passed in the X-Api-Key header.

X-Api-Key: your-private-api-key

Important: Webhook Behavior​

CRITICAL: Order operations performed via the SDK API do NOT trigger webhooks.

This design prevents webhook loops when syncing orders between platforms:

  • Orders created via SDK: No webhooks sent
  • Orders updated via SDK: No webhooks sent
  • Orders cancelled via SDK: No webhooks sent

Orders created through the VesuvioPay checkout flow DO trigger webhooks normally.

If you need to notify your system of SDK order changes, implement your own notification logic after successful API calls.

Retrieving Orders​

Get List of Orders​

Endpoint: GET /api/v1/orders

Retrieve a paginated list of orders for your store with optional filtering.

async function getOrders(filters = {}) {
const params = new URLSearchParams({
limit: filters.limit || 50,
...(filters.status && { status: filters.status }),
...(filters.dateFrom && { dateFrom: filters.dateFrom.toISOString() }),
...(filters.dateTo && { dateTo: filters.dateTo.toISOString() })
});

const response = await fetch(
`https://api.vesuviopay.com/api/v1/orders?${params}`,
{
headers: {
'X-Api-Key': 'your-private-api-key'
}
}
);

return await response.json();
}

// Usage examples
const allOrders = await getOrders();
const pendingOrders = await getOrders({ status: 'Pending' });
const recentOrders = await getOrders({
dateFrom: new Date('2025-10-01'),
dateTo: new Date('2025-10-08'),
limit: 100
});

Query Parameters:

  • limit (int, 1-50): Maximum number of orders to return (default: 50)
  • status (string): Filter by order status
  • dateFrom (datetime): Orders placed after this date
  • dateTo (datetime): Orders placed before this date

Response:

{
"success": true,
"data": {
"orders": [
{
"id": "a3f2b1c0-1234-5678-90ab-cdef12345678",
"orderNumber": "VES-1001",
"externalOrderId": "shopify_order_12345",
"status": "Confirmed",
"customerId": "b4e3c2d1-2345-6789-01bc-def123456789",
"customerEmail": "customer@example.com",
"customerPhone": "+1234567890",
"totalAmount": 159.97,
"subtotal": 149.97,
"taxTotal": 10.00,
"shippingTotal": 0.00,
"discountTotal": 0.00,
"itemCount": 3,
"placedAt": "2025-10-08T10:30:00Z",
"updatedAt": "2025-10-08T11:00:00Z"
}
],
"totalCount": 1,
"hasMore": false
}
}

Order Status Values:

  • Pending: Order placed, awaiting confirmation
  • Confirmed: Payment confirmed
  • Processing: Order being prepared
  • Shipped: Order shipped to customer
  • PartiallyShipped: Some items shipped
  • Delivered: Order delivered
  • PartiallyDelivered: Some items delivered
  • Cancelled: Order cancelled
  • Refunded: Order refunded

Get Specific Order​

Endpoint: GET /api/v1/orders/{orderId}

Retrieve detailed information about a specific order. You can use either the internal VesuvioPay order ID or the external order ID.

async function getOrder(orderId) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/orders/${orderId}`,
{
headers: {
'X-Api-Key': 'your-private-api-key'
}
}
);

return await response.json();
}

// Using internal ID
const order1 = await getOrder('a3f2b1c0-1234-5678-90ab-cdef12345678');

// Using external ID
const order2 = await getOrder('shopify_order_12345');

Response:

{
"success": true,
"data": {
"id": "a3f2b1c0-1234-5678-90ab-cdef12345678",
"orderNumber": "VES-1001",
"externalOrderId": "shopify_order_12345",
"platformName": "Shopify",
"status": "Confirmed",
"customer": {
"id": "b4e3c2d1-2345-6789-01bc-def123456789",
"email": "customer@example.com",
"phone": "+1234567890",
"firstName": "John",
"lastName": "Doe"
},
"items": [
{
"id": "c5f4d3e2-3456-7890-12cd-ef1234567890",
"productVariantId": "d6g5e4f3-4567-8901-23de-f12345678901",
"externalProductId": "shopify_12345",
"externalVariantId": "shopify_variant_001",
"productTitle": "Premium T-Shirt",
"variantTitle": "Small / Red",
"sku": "TSHIRT-S-RED",
"quantity": 2,
"unitPrice": 29.99,
"totalPrice": 59.98,
"itemStatus": "Confirmed"
}
],
"shippingAddress": {
"firstName": "John",
"lastName": "Doe",
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "New York",
"province": "NY",
"country": "United States",
"countryIsoCode": "US",
"postalCode": "10001",
"phone": "+1234567890"
},
"billingAddress": {
"firstName": "John",
"lastName": "Doe",
"address1": "123 Main St",
"city": "New York",
"province": "NY",
"country": "United States",
"countryIsoCode": "US",
"postalCode": "10001"
},
"totals": {
"subtotal": 59.98,
"shippingTotal": 5.00,
"taxTotal": 4.80,
"discountTotal": 0.00,
"totalAmount": 69.78
},
"paymentMethod": {
"method": "credit_card",
"display": "Visa ending in 4242",
"last4": "4242",
"brand": "Visa"
},
"placedAt": "2025-10-08T10:30:00Z",
"updatedAt": "2025-10-08T11:00:00Z",
"deliveryInstructions": "Leave at door",
"metadata": {
"source": "mobile_app",
"campaign": "summer_sale"
}
}
}

Creating Orders from External Platforms​

Create orders in VesuvioPay from external platforms like Shopify, WooCommerce, etc.

Endpoint: POST /api/v1/orders

Important: Orders created via this endpoint do NOT trigger webhooks.

async function createOrderFromPlatform(orderData) {
const response = await fetch('https://api.vesuviopay.com/api/v1/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': 'your-private-api-key'
},
body: JSON.stringify(orderData)
});

return await response.json();
}

// Usage
const newOrder = await createOrderFromPlatform({
externalOrderId: 'shopify_order_67890',
platformName: 'Shopify',
customerEmail: 'customer@example.com',
customerPhone: '+1234567890',
items: [
{
externalProductId: 'shopify_12345',
externalVariantId: 'shopify_variant_001',
quantity: 2,
unitPrice: 29.99
}
],
shippingAddress: {
firstName: 'Jane',
lastName: 'Smith',
address1: '456 Oak Ave',
city: 'Los Angeles',
province: 'CA',
country: 'United States',
countryIsoCode: 'US',
postalCode: '90001',
phone: '+1987654321'
},
billingAddress: {
firstName: 'Jane',
lastName: 'Smith',
address1: '456 Oak Ave',
city: 'Los Angeles',
province: 'CA',
country: 'United States',
countryIsoCode: 'US',
postalCode: '90001'
},
totals: {
subtotal: 59.98,
shippingTotal: 5.00,
taxTotal: 4.80,
discountTotal: 0.00,
totalAmount: 69.78
},
paymentMethod: {
method: 'credit_card',
display: 'Visa ending in 1234'
},
status: 'Confirmed',
deliveryInstructions: 'Ring doorbell',
metadata: {
platformOrderNumber: 'SHOP-1234',
paymentId: 'pi_abc123xyz'
}
});

Request Body Fields:

Required:

  • externalOrderId (string): Your platform's order ID
  • customerEmail (string): Customer email address
  • items (array): Order line items
    • externalProductId (string): External product ID
    • externalVariantId (string): External variant ID
    • quantity (int): Item quantity
    • unitPrice (decimal): Price per unit
  • shippingAddress (object): Delivery address
  • totals (object): Order totals

Optional:

  • platformName (string): Name of the source platform
  • customerPhone (string): Customer phone number
  • billingAddress (object): Billing address
  • paymentMethod (object): Payment method details
  • status (string): Initial order status (default: "Pending")
  • deliveryInstructions (string): Special delivery instructions
  • metadata (object): Custom metadata

Response:

{
"success": true,
"data": {
"id": "e7h6f5g4-5678-9012-34ef-g123456789012",
"orderNumber": "VES-1002",
"externalOrderId": "shopify_order_67890",
"status": "Confirmed",
"createdAt": "2025-10-08T12:00:00Z"
}
}

Updating Order Status​

Update the status of an order with optional metadata and status change reason.

Endpoint: PUT /api/v1/orders/{orderId}/status

Important: Status updates via this endpoint do NOT trigger webhooks.

async function updateOrderStatus(orderId, newStatus, metadata = {}) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/orders/${orderId}/status`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': 'your-private-api-key'
},
body: JSON.stringify({
status: newStatus,
statusChangeReason: metadata.reason,
metadata: metadata.additionalData
})
}
);

return await response.json();
}

// Usage examples

// Mark order as shipped
await updateOrderStatus('shopify_order_67890', 'Shipped', {
reason: 'Order dispatched via FedEx',
additionalData: {
trackingNumber: 'FDX123456789',
carrier: 'FedEx',
estimatedDelivery: '2025-10-12'
}
});

// Mark order as delivered
await updateOrderStatus('shopify_order_67890', 'Delivered', {
reason: 'Package delivered successfully'
});

// Update to processing
await updateOrderStatus('shopify_order_67890', 'Processing', {
reason: 'Order being prepared for shipment'
});

Request Body:

{
"status": "Shipped",
"statusChangeReason": "Order dispatched via FedEx",
"metadata": {
"trackingNumber": "FDX123456789",
"carrier": "FedEx",
"estimatedDelivery": "2025-10-12"
}
}

Valid Status Transitions:

  • Pending → Confirmed, Cancelled
  • Confirmed → Processing, Cancelled
  • Processing → Shipped, PartiallyShipped, Cancelled
  • Shipped → Delivered, PartiallyDelivered
  • Any status → Cancelled, Refunded

Cancelling Orders​

Cancel an order with optional refund amount and cancellation reason.

Endpoint: PUT /api/v1/orders/{orderId}/cancel

Important: Order cancellations via this endpoint do NOT trigger webhooks.

async function cancelOrder(orderId, reason, refundAmount = null) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/orders/${orderId}/cancel`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': 'your-private-api-key'
},
body: JSON.stringify({
cancellationReason: reason,
refundAmount: refundAmount,
notifyCustomer: true
})
}
);

return await response.json();
}

// Usage examples

// Full cancellation with full refund
await cancelOrder('shopify_order_67890', 'Customer requested cancellation', 69.78);

// Cancellation without refund
await cancelOrder('shopify_order_67890', 'Duplicate order', null);

// Partial refund
await cancelOrder('shopify_order_67890', 'Item out of stock', 35.00);

Request Body:

{
"cancellationReason": "Customer requested cancellation",
"refundAmount": 69.78,
"notifyCustomer": true,
"restockItems": true
}

Fields:

  • cancellationReason (string, required): Reason for cancellation
  • refundAmount (decimal, optional): Amount to refund. If null, no refund is processed
  • notifyCustomer (boolean, optional): Send cancellation notification to customer (default: true)
  • restockItems (boolean, optional): Return items to inventory (default: true)

Response:

{
"success": true,
"message": "Order cancelled successfully",
"data": {
"orderId": "a3f2b1c0-1234-5678-90ab-cdef12345678",
"status": "Cancelled",
"refundAmount": 69.78,
"refundId": "rf_abc123xyz",
"cancelledAt": "2025-10-08T14:00:00Z"
}
}

Linking Orders to Platform Orders​

Link an existing VesuvioPay order to an external platform order ID.

Endpoint: PUT /api/v1/orders/{orderId}/link

Note: This endpoint requires the internal VesuvioPay order ID (UUID), not the external ID.

async function linkOrderToPlatform(vesuvioOrderId, externalOrderId, platformName) {
const response = await fetch(
`https://api.vesuviopay.com/api/v1/orders/${vesuvioOrderId}/link`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': 'your-private-api-key'
},
body: JSON.stringify({
externalOrderId: externalOrderId,
platformName: platformName
})
}
);

return await response.json();
}

// Usage
await linkOrderToPlatform(
'a3f2b1c0-1234-5678-90ab-cdef12345678',
'shopify_order_99999',
'Shopify'
);

Request Body:

{
"externalOrderId": "shopify_order_99999",
"platformName": "Shopify"
}

Response:

{
"success": true,
"message": "Order linked to platform successfully",
"data": {
"orderId": "a3f2b1c0-1234-5678-90ab-cdef12345678",
"externalOrderId": "shopify_order_99999",
"platformName": "Shopify",
"linkedAt": "2025-10-08T15:00:00Z"
}
}

Complete Integration Example​

Here's a complete example of syncing orders from Shopify to VesuvioPay:

const VESUVIO_API_KEY = process.env.VESUVIO_PRIVATE_API_KEY;
const VESUVIO_BASE_URL = 'https://api.vesuviopay.com/api/v1';

class VesuvioOrderSync {
constructor(apiKey) {
this.apiKey = apiKey;
}

async syncOrderFromShopify(shopifyOrder) {
// Transform Shopify order to VesuvioPay format
const vesuvioOrder = {
externalOrderId: `shopify_${shopifyOrder.id}`,
platformName: 'Shopify',
customerEmail: shopifyOrder.email,
customerPhone: shopifyOrder.phone,
items: shopifyOrder.line_items.map(item => ({
externalProductId: `shopify_${item.product_id}`,
externalVariantId: `shopify_variant_${item.variant_id}`,
quantity: item.quantity,
unitPrice: parseFloat(item.price)
})),
shippingAddress: {
firstName: shopifyOrder.shipping_address.first_name,
lastName: shopifyOrder.shipping_address.last_name,
address1: shopifyOrder.shipping_address.address1,
address2: shopifyOrder.shipping_address.address2,
city: shopifyOrder.shipping_address.city,
province: shopifyOrder.shipping_address.province,
country: shopifyOrder.shipping_address.country,
countryIsoCode: shopifyOrder.shipping_address.country_code,
postalCode: shopifyOrder.shipping_address.zip,
phone: shopifyOrder.shipping_address.phone
},
billingAddress: {
firstName: shopifyOrder.billing_address.first_name,
lastName: shopifyOrder.billing_address.last_name,
address1: shopifyOrder.billing_address.address1,
address2: shopifyOrder.billing_address.address2,
city: shopifyOrder.billing_address.city,
province: shopifyOrder.billing_address.province,
country: shopifyOrder.billing_address.country,
countryIsoCode: shopifyOrder.billing_address.country_code,
postalCode: shopifyOrder.billing_address.zip
},
totals: {
subtotal: parseFloat(shopifyOrder.subtotal_price),
shippingTotal: parseFloat(shopifyOrder.total_shipping_price_set.shop_money.amount),
taxTotal: parseFloat(shopifyOrder.total_tax),
discountTotal: parseFloat(shopifyOrder.total_discounts),
totalAmount: parseFloat(shopifyOrder.total_price)
},
paymentMethod: {
method: shopifyOrder.payment_gateway_names[0]?.toLowerCase() || 'unknown',
display: shopifyOrder.payment_gateway_names[0] || 'Unknown'
},
status: this.mapShopifyStatus(shopifyOrder.financial_status, shopifyOrder.fulfillment_status),
deliveryInstructions: shopifyOrder.note,
metadata: {
shopifyOrderNumber: shopifyOrder.order_number,
shopifyOrderName: shopifyOrder.name,
tags: shopifyOrder.tags
}
};

try {
const response = await fetch(`${VESUVIO_BASE_URL}/orders`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': this.apiKey
},
body: JSON.stringify(vesuvioOrder)
});

if (!response.ok) {
const error = await response.json();
throw new Error(`Failed to sync order: ${error.message}`);
}

return await response.json();
} catch (error) {
console.error(`Error syncing Shopify order ${shopifyOrder.id}:`, error);
throw error;
}
}

mapShopifyStatus(financialStatus, fulfillmentStatus) {
if (fulfillmentStatus === 'fulfilled') return 'Delivered';
if (fulfillmentStatus === 'partial') return 'PartiallyShipped';
if (fulfillmentStatus === 'shipped') return 'Shipped';
if (financialStatus === 'paid') return 'Confirmed';
if (financialStatus === 'pending') return 'Pending';
if (financialStatus === 'refunded') return 'Refunded';
if (financialStatus === 'voided') return 'Cancelled';
return 'Pending';
}

async updateOrderStatus(vesuvioOrderId, newStatus, metadata = {}) {
try {
const response = await fetch(
`${VESUVIO_BASE_URL}/orders/${vesuvioOrderId}/status`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': this.apiKey
},
body: JSON.stringify({
status: newStatus,
statusChangeReason: metadata.reason,
metadata: metadata.additionalData
})
}
);

return await response.json();
} catch (error) {
console.error(`Error updating order status:`, error);
throw error;
}
}

async cancelOrder(vesuvioOrderId, reason, refundAmount = null) {
try {
const response = await fetch(
`${VESUVIO_BASE_URL}/orders/${vesuvioOrderId}/cancel`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': this.apiKey
},
body: JSON.stringify({
cancellationReason: reason,
refundAmount: refundAmount,
notifyCustomer: true,
restockItems: true
})
}
);

return await response.json();
} catch (error) {
console.error(`Error cancelling order:`, error);
throw error;
}
}

async getOrdersByDateRange(dateFrom, dateTo) {
try {
const params = new URLSearchParams({
dateFrom: dateFrom.toISOString(),
dateTo: dateTo.toISOString(),
limit: 50
});

const response = await fetch(`${VESUVIO_BASE_URL}/orders?${params}`, {
headers: { 'X-Api-Key': this.apiKey }
});

return await response.json();
} catch (error) {
console.error(`Error fetching orders:`, error);
throw error;
}
}
}

// Usage Examples

const orderSync = new VesuvioOrderSync(VESUVIO_API_KEY);

// Sync a new order from Shopify
const shopifyOrder = await fetchShopifyOrder('shopify_order_id');
const syncedOrder = await orderSync.syncOrderFromShopify(shopifyOrder);
console.log('Order synced:', syncedOrder.data.orderNumber);

// Update order status when Shopify order is fulfilled
await orderSync.updateOrderStatus(
syncedOrder.data.id,
'Shipped',
{
reason: 'Fulfilled in Shopify',
additionalData: {
trackingNumber: 'TRACK123',
carrier: 'USPS'
}
}
);

// Cancel order when Shopify order is cancelled
await orderSync.cancelOrder(
syncedOrder.data.id,
'Cancelled in Shopify',
69.78
);

// Get recent orders
const recentOrders = await orderSync.getOrdersByDateRange(
new Date('2025-10-01'),
new Date('2025-10-08')
);

Error Handling​

Common Errors​

400 Bad Request - Invalid Order Data​

{
"success": false,
"message": "Validation failed",
"errors": [
"Customer email is required",
"At least one order item is required"
]
}

404 Not Found - Order Not Found​

{
"success": false,
"message": "Order not found",
"errorCode": "ORDER_NOT_FOUND"
}

409 Conflict - External Order ID Already Exists​

{
"success": false,
"message": "An order with this external ID already exists",
"errorCode": "DUPLICATE_EXTERNAL_ORDER_ID"
}

400 Bad Request - Invalid Status Transition​

{
"success": false,
"message": "Cannot change status from Delivered to Pending",
"errorCode": "INVALID_STATUS_TRANSITION"
}

Best Practices​

1. Remember: No Webhook Triggers​

Always implement your own notification logic after SDK order operations:

async function syncAndNotify(shopifyOrder) {
const result = await orderSync.syncOrderFromShopify(shopifyOrder);

// Manually notify your systems since SDK doesn't trigger webhooks
await notifyWarehouse(result.data);
await updateAnalytics(result.data);
}

2. Use External IDs Consistently​

Always use your platform's order IDs as external IDs for easy lookups:

const externalOrderId = `shopify_${shopifyOrderId}`;

3. Handle Idempotency​

Check if an order already exists before creating:

async function createOrderIdempotent(orderData) {
try {
// Try to get existing order first
const existing = await getOrder(orderData.externalOrderId);
if (existing.success) {
console.log('Order already exists');
return existing;
}
} catch (error) {
// Order doesn't exist, create it
}

return await createOrderFromPlatform(orderData);
}

4. Sync Order Status Changes​

Listen to your platform's webhooks and sync status to VesuvioPay:

// Express webhook handler for Shopify
app.post('/webhooks/shopify/orders/fulfilled', async (req, res) => {
const shopifyOrder = req.body;

await orderSync.updateOrderStatus(
`shopify_${shopifyOrder.id}`,
'Shipped',
{
reason: 'Fulfilled in Shopify',
additionalData: {
fulfillmentId: shopifyOrder.fulfillments[0].id
}
}
);

res.status(200).send('OK');
});

5. Maintain Metadata​

Store important platform-specific data in metadata:

metadata: {
platformOrderNumber: shopifyOrder.order_number,
platformOrderId: shopifyOrder.id,
platformName: 'Shopify',
paymentId: shopifyOrder.payment_id,
tags: shopifyOrder.tags,
source: shopifyOrder.source_name
}

Next Steps​