marketplace-order-hook
Apply when implementing order integration hooks, feeds, or webhook handlers for VTEX marketplace connectors. Covers Feed v3 (pull) vs Hook (push), filter types (FromWorkflow and FromOrders), order status lifecycle, payload validation, and idempotent processing. Use for building order integrations between VTEX marketplaces and external systems such as ERPs, WMS, or fulfillment services.
What this skill does
# Order Integration & Webhooks
## When this skill applies
Use this skill when building an integration that needs to react to order status changes in a VTEX marketplace — such as syncing orders to an ERP, triggering fulfillment workflows, or sending notifications to external systems.
- Configuring Feed v3 or Hook for order updates
- Choosing between Feed (pull) and Hook (push) delivery models
- Validating webhook authentication and processing events idempotently
- Handling the complete order status lifecycle
Do not use this skill for:
- Catalog or SKU synchronization (see `marketplace-catalog-sync`)
- Invoice and tracking submission (see `marketplace-fulfillment`)
- General API rate limiting (see `marketplace-rate-limiting`)
## Decision rules
- Use **Hook (push)** for high-performance middleware that needs real-time order updates. Your endpoint must respond with HTTP 200 within 5000ms.
- Use **Feed (pull)** for ERPs or systems with limited throughput where you control the consumption pace. Events persist in a queue until committed.
- Use **Feed as a backup** alongside Hook to catch events missed during downtime.
- Use **FromWorkflow** filter when you only need to react to order status changes (simpler, most common).
- Use **FromOrders** filter when you need to filter by any order property using JSONata expressions (e.g., by sales channel).
- The two filter types are **mutually exclusive**. Using both in the same configuration request returns `409 Conflict`.
- Each appKey can configure only one feed and one hook. Different users sharing the same appKey access the same feed/hook.
| | Feed | Hook |
|---|---|---|
| Model | Pull (active) | Push (reactive) |
| Scalability | You control volume | Must handle any volume |
| Reliability | Events persist in queue | Must be always available |
| Best for | ERPs with limited throughput | High-performance middleware |
**Hook Notification Payload**:
```json
{
"Domain": "Marketplace",
"OrderId": "v40484048naf-01",
"State": "payment-approved",
"LastChange": "2019-07-29T23:17:30.0617185Z",
"Origin": {
"Account": "accountABC",
"Key": "vtexappkey-keyEDF"
}
}
```
The payload contains only the order ID and state — not the full order data. Your integration must call `GET /api/oms/pvt/orders/{orderId}` to retrieve complete order details.
**Architecture/Data Flow**:
```text
VTEX OMS Your Integration
│ │
│── Order status change ──────────────▶│ (Hook POST to your URL)
│ │── Validate auth headers
│ │── Check idempotency (orderId + State)
│◀── GET /api/oms/pvt/orders/{id} ─────│ (Fetch full order)
│── Full order data ──────────────────▶│
│ │── Process order
│◀── HTTP 200 ─────────────────────────│ (Must respond within 5000ms)
```
## Hard constraints
### Constraint: Validate Webhook Authentication
Your hook endpoint MUST validate the authentication headers sent by VTEX before processing any event. The `Origin.Account` and `Origin.Key` fields in the payload must match your expected values.
**Why this matters**
Without auth validation, any actor can send fake order events to your endpoint, triggering unauthorized fulfillment actions, data corruption, or financial losses.
**Detection**
If you see a hook endpoint handler that processes events without checking `Origin.Account`, `Origin.Key`, or custom headers → STOP and add authentication validation.
**Correct**
```typescript
import { RequestHandler } from "express";
interface HookPayload {
Domain: string;
OrderId: string;
State: string;
LastChange: string;
Origin: {
Account: string;
Key: string;
};
}
interface HookConfig {
expectedAccount: string;
expectedAppKey: string;
customHeaderKey: string;
customHeaderValue: string;
}
function createHookHandler(config: HookConfig): RequestHandler {
return async (req, res) => {
const payload: HookPayload = req.body;
// Handle VTEX ping during hook configuration
if (payload && "hookConfig" in payload) {
res.status(200).json({ success: true });
return;
}
// Validate Origin credentials
if (
payload.Origin?.Account !== config.expectedAccount ||
payload.Origin?.Key !== config.expectedAppKey
) {
console.error("Unauthorized hook event", {
receivedAccount: payload.Origin?.Account,
receivedKey: payload.Origin?.Key,
});
res.status(401).json({ error: "Unauthorized" });
return;
}
// Validate custom header (configured during hook setup)
if (req.headers[config.customHeaderKey.toLowerCase()] !== config.customHeaderValue) {
console.error("Invalid custom header");
res.status(401).json({ error: "Unauthorized" });
return;
}
// Process the event
await processOrderEvent(payload);
res.status(200).json({ success: true });
};
}
async function processOrderEvent(payload: HookPayload): Promise<void> {
console.log(`Processing order ${payload.OrderId} in state ${payload.State}`);
}
```
**Wrong**
```typescript
// WRONG: No authentication validation — accepts events from anyone
const unsafeHookHandler: RequestHandler = async (req, res) => {
const payload: HookPayload = req.body;
// Directly processing without checking Origin or headers
// Any actor can POST fake events and trigger unauthorized actions
await processOrderEvent(payload);
res.status(200).json({ success: true });
};
```
---
### Constraint: Process Events Idempotently
Your integration MUST process order events idempotently. Use the combination of `OrderId` + `State` + `LastChange` as a deduplication key to prevent duplicate processing.
**Why this matters**
VTEX may deliver the same hook notification multiple times (at-least-once delivery). Without idempotency, duplicate processing can result in double fulfillment, duplicate invoices, or inconsistent state.
**Detection**
If you see an order event handler without an `orderId` duplicate check or deduplication mechanism → warn about idempotency. If the handler directly mutates state without checking if the event was already processed → warn.
**Correct**
```typescript
interface ProcessedEvent {
orderId: string;
state: string;
lastChange: string;
processedAt: Date;
}
// In-memory store for example — use Redis or database in production
const processedEvents = new Map<string, ProcessedEvent>();
function buildDeduplicationKey(payload: HookPayload): string {
return `${payload.OrderId}:${payload.State}:${payload.LastChange}`;
}
async function idempotentProcessEvent(payload: HookPayload): Promise<boolean> {
const deduplicationKey = buildDeduplicationKey(payload);
// Check if this exact event was already processed
if (processedEvents.has(deduplicationKey)) {
console.log(`Event already processed: ${deduplicationKey}`);
return false; // Skip — already handled
}
// Mark as processing (with TTL in production)
processedEvents.set(deduplicationKey, {
orderId: payload.OrderId,
state: payload.State,
lastChange: payload.LastChange,
processedAt: new Date(),
});
try {
await handleOrderStateChange(payload.OrderId, payload.State);
return true;
} catch (error) {
// Remove from processed set so it can be retried
processedEvents.delete(deduplicationKey);
throw error;
}
}
async function handleOrderStateChange(orderId: string, state: string): Promise<void> {
switch (state) {
case "ready-for-handling":
await startOrderFulfillment(orderId);
break;
case "handling":
await updateOrderInERP(orderId, "in_progress");
break;
case "invoiced":
await confirmOrderShipped(orderId);
break;
case "cancel":
await cancelOrderInERP(orderId);
break;
default:
console.log(`Unhandled state: ${state} for order ${orderId}`);
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.