sales-channel API
One site or ordering channel
Use /api/v1 when one website or channel owns its catalog aliases, customers, orders, inventory, and webhook endpoint. Credentials begin with vial_test_ or vial_live_.
Authenticated catalog, idempotent order intake, signed webhooks, and a full sandbox lifecycle. The integration surface is small on purpose — narrow is what stays stable — and the machine-readable contract is public.
Contract: /api/v1/openapi.json · OpenAPI 3.2 · the document is the authority; this page is the tour.
VialAPI exposes two versioned contracts. They share validation, idempotency, sandbox behavior, signed events, and fulfillment state. The credential decides which commercial boundary the request operates inside.
sales-channel API
Use /api/v1 when one website or channel owns its catalog aliases, customers, orders, inventory, and webhook endpoint. Credentials begin with vial_test_ or vial_live_.
business-account API
Use /api/account/v1 when one operator needs a unified catalog, customers, and orders across websites, manual sales, or downstream channels. Credentials begin with vial_test_acct_ or vial_live_acct_.
The exact script below runs in this repository’s CI on every release — it is read from that file, not copied onto this page. A sandbox key (vial_test_) is issued with your workspace when access is approved; order creation returns 202 Accepted and the order then progresses through the lifecycle via webhooks and polling.
#!/usr/bin/env bash
set -euo pipefail
: "${VIALAPI_TEST_KEY:?Set VIALAPI_TEST_KEY to an expiring vial_test_ credential}"
VIALAPI_BASE_URL="${VIALAPI_BASE_URL:-https://vialapi.com}"
curl --fail-with-body --silent --show-error \
--request GET "${VIALAPI_BASE_URL}/api/v1/catalog?limit=20" \
--header "Authorization: Bearer ${VIALAPI_TEST_KEY}" \
--header "Accept: application/json"
# Replace VIAL-EXAMPLE-SKU with vialApiSku from the authenticated catalog.
curl --fail-with-body --silent --show-error \
--request POST "${VIALAPI_BASE_URL}/api/v1/orders/validate" \
--header "Authorization: Bearer ${VIALAPI_TEST_KEY}" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--data '{"lineItems":[{"sku":"VIAL-EXAMPLE-SKU","quantity":1}]}'
# Use the exact same Idempotency-Key and exact JSON bytes after an uncertain result.
curl --fail-with-body --silent --show-error \
--request POST "${VIALAPI_BASE_URL}/api/v1/orders" \
--header "Authorization: Bearer ${VIALAPI_TEST_KEY}" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: quickstart-order-001" \
--data '{"externalOrderId":"QUICKSTART-001","customer":{"firstName":"API","lastName":"Certification","email":"api-certification@example.test"},"shipTo":{"recipientName":"API Certification","address1":"100 Example Avenue","city":"Atlanta","state":"GA","postalCode":"30309","country":"US","residential":true},"lineItems":[{"sku":"VIAL-EXAMPLE-SKU","quantity":1}],"metadata":{"source":"published-quickstart","vialapiSandboxScenario":"standard"}}'Retry rule: reuse the exact same Idempotency-Key and the exact same JSON bytes after any uncertain result. A retried request can never create a second order.
Every delivery uses Standard Webhooks headers — webhook-id, webhook-timestamp, webhook-signature — with an HMAC-SHA256 over the exact raw body. Delivery is at least once and ordering is not guaranteed: verify the bytes, then deduplicate by webhook-id.
order.createdorder.status_changedorder.approvedorder.awaiting_paymentorder.label_createdorder.shippedorder.deliveredorder.cancelledorder.needs_reviewinventory.low_stockpingimport { createHmac, timingSafeEqual } from "node:crypto";
type WebhookHeaders = {
"webhook-id": string;
"webhook-timestamp": string;
"webhook-signature": string;
};
/** Verify the exact raw bytes before parsing JSON, then deduplicate webhook-id. */
export function verifyVialApiWebhook({
headers,
rawBody,
secret,
nowSeconds = Math.floor(Date.now() / 1_000),
toleranceSeconds = 5 * 60
}: {
headers: WebhookHeaders;
rawBody: string;
secret: string;
nowSeconds?: number;
toleranceSeconds?: number;
}) {
if (!/^\d+$/u.test(headers["webhook-timestamp"])) return false;
const sentAt = Number(headers["webhook-timestamp"]);
if (!Number.isSafeInteger(sentAt) || Math.abs(nowSeconds - sentAt) > toleranceSeconds) return false;
if (!secret.startsWith("whsec_")) return false;
const encodedSecret = secret.slice("whsec_".length);
const key = Buffer.from(encodedSecret, "base64");
if (key.length < 24 || key.length > 64 || key.toString("base64") !== encodedSecret) return false;
const expected = createHmac("sha256", key)
.update(`${headers["webhook-id"]}.${headers["webhook-timestamp"]}.${rawBody}`, "utf8")
.digest();
return headers["webhook-signature"].split(/\s+/u).some((candidate) => {
const match = /^v1,([A-Za-z0-9+/]+={0,2})$/u.exec(candidate);
if (!match?.[1]) return false;
const received = Buffer.from(match[1], "base64");
return received.length === expected.length && timingSafeEqual(received, expected);
});
}Keys prefixed vial_test_ run the full order lifecycle against a simulated supplier — shipped after about two minutes, delivered after about four — excluded from billing and reports, and purged after thirty days. Webhook payloads carry "livemode": false. There are no magic sandbox SKUs; you exercise the same catalog contract your production key will see.
orders:readorders:validateorders:writecatalog:readcustomers:readwebhooks:manageAn empty scope list grants nothing. Every credential carries explicit least-privilege scopes and may be bound to a single connection.
Add a custom connector or remote MCP server in your AI app, then paste this one URL. VialAPI opens its own secure sign-in, selects the owner’s most recently used business, and asks them to review the exact access. No Anthropic or OpenAI API key is needed.
remote MCP URL
https://vialapi.com/api/mcpReading documentation can teach an AI how the ordering flow works, but it cannot silently authorize tools. OAuth owner consent is what securely connects the AI to one business. A future connector-directory listing can make the VialAPI connection easier to discover without changing that security boundary.
Versioning: /api/v1 is the compatibility boundary. Breaking changes require a new path version; deprecated operations carry Deprecation and Sunset headers for at least 180 days.
Access is reviewed. Tell us what you run and we will provision the right workspace, catalog, and credentials — sandbox keys arrive with the workspace, and production keys follow certification against the sandbox lifecycle above.