fulkruma.stock.adjusted.v1
Fires every time a stock level changes — every POST /api/v1/stock/adjust, whatever its reason (a refund restock or an import included: they go through the same call). This is the highest-volume event Fulkruma emits; back-pressure your handler accordingly.
The payload carries the signed delta, the reason code, and the post-mutation quantityAfter so consumers can sync state without a separate read.
When it fires
Inside the same Prisma transaction as the underlying StockMovement insert. Exactly one emission per movement ID; retries reuse the same evt_….
Reasons that fire this event:
manual_adjust— merchant-initiated correction.initial_stock— onboarding seed.transfer_in/transfer_out— inter-warehouse movement.damaged/returned_to_supplier— write-offs.refund_restock— refund through Plugipay restored quantity.import— bulk import.
Payload
{
"id": "evt_01HXAB7K3M9N2P5QRS8TVWXY3Z",
"type": "fulkruma.stock.adjusted.v1",
"occurredAt": "2026-05-12T10:42:00.123Z",
"accountId": "acc_01HX...",
"data": {
"variantId": "var_01HX...",
"warehouseId": "wh_01HX...",
"delta": -3,
"reason": "damaged",
"quantityAfter": 39,
"movementId": "mv_01HX..."
}
}
delta is signed: negative for decrement, positive for increment. quantityAfter is the post-mutation on-hand level; treat it as authoritative.
Handler examples
// Node
if (event.type === 'fulkruma.stock.adjusted.v1') {
const { variantId, warehouseId, delta, reason, quantityAfter } = event.data;
await inventory.upsertLevel(variantId, warehouseId, quantityAfter);
if (reason === 'damaged' || reason === 'returned_to_supplier') {
await analytics.track('stock_loss', { variantId, delta });
}
}
# Python
if event["type"] == "fulkruma.stock.adjusted.v1":
d = event["data"]
inventory.upsert_level(d["variantId"], d["warehouseId"], d["quantityAfter"])
if d["reason"] in ("damaged", "returned_to_supplier"):
analytics.track("stock_loss", variant=d["variantId"], delta=d["delta"])
// Go
if event.Type == "fulkruma.stock.adjusted.v1" {
var d struct {
VariantID, WarehouseID, Reason, MovementID string
Delta, QuantityAfter int
}
_ = json.Unmarshal(event.Data, &d)
inventory.Upsert(ctx, d.VariantID, d.WarehouseID, d.QuantityAfter)
}
What to do
- Mirror the level into your own inventory store, keyed by
(variantId, warehouseId). - For low-stock alerts, subscribe to
fulkruma.stock.low.v1instead of comparingquantityAfteryourself. - Reconcile against Fulkruma's movements log at end-of-day — the
movementIdin the payload joins straight to that table.
Common pitfalls
- Recomputing the level from
delta. UsequantityAfterdirectly. Out-of-order delivery can make adelta-based replay drift. - Volume. A merchant fulfilling 1000 orders a day can fire 2-3000 stock events. Queue your handler; don't process synchronously.
- Treating
damagedas fraud-positive. It's just write-offs — routine inventory hygiene. Cross-reference with audit-log to see who triggered it. - Missing the movement ID.
movementIdis the join key to the immutable movements audit table — capture it.
Related events
fulkruma.stock.low.v1 fires right after this event when the adjustment takes the level below the variant's lowStockThreshold.
Next
- Webhooks reference.
- Stock resource — the levels, movements, and reservation APIs.