Developers
Feeds and webhooks for the systems around your warehouse
Pull any client's inventory as JSON or CSV, and get a signed HTTP POST the moment an invoice is paid, a request changes, or items arrive. No SDK to install, no OAuth dance.
Data feeds
One URL per client, read-only
Create a feed for a client in Settings → Integrations. The token is scoped to that client only, so a designer, gallery, or brand can wire it straight into their own tools without seeing anyone else's items.
- JSON by default, CSV with one query parameter
- Incremental pulls with updated_since
- Locations, dimensions, values, tags, and photos
- Revoke in one click; the URL dies instantly
GET https://stowley.com/api/public/feeds/<token>/inventory
GET https://stowley.com/api/public/feeds/<token>/inventory?format=csv
GET https://stowley.com/api/public/feeds/<token>/inventory?updated_since=2026-09-01T00:00:00Z
{
"object": "inventory_feed",
"generated_at": "2026-09-04T14:02:11.000Z",
"client": { "id": "…", "name": "Hartley & Co." },
"organization": { "name": "Northside Receiving" },
"filter": { "updated_since": null },
"count": 128,
"items": [
{
"id": "…",
"name": "Walnut sideboard",
"sku": "HC-0042",
"client_sku": "PO-118-3",
"quantity": 1,
"status": "in_storage",
"condition": "excellent",
"category": "furniture",
"project": "Greenwich",
"location": { "warehouse": "Main", "zone": "A", "aisle": "12", "bin": "3" },
"dimensions_in": { "length": 72, "width": 18, "height": 34 },
"weight_lbs": 140,
"insured_value": 4200,
"tags": ["fragile", "designer"],
"notes": "Blanket wrap, no stacking",
"image_count": 2,
"primary_image_url": "https://…/sideboard.jpg",
"storage_start_date": "2026-06-01",
"created_at": "2026-06-01T12:00:00.000Z",
"updated_at": "2026-08-20T09:30:00.000Z"
}
]
}Webhooks
Signed events, delivered within a minute
Subscribe an HTTPS endpoint to the events you care about. Every delivery carries an HMAC-SHA256 signature over the timestamp and body, so you can trust what arrives.
invoice.paidInvoice paidrequest.createdService request createdrequest.status_changedService request status changedrequest.completedService request completeditems.receivedItems received into inventoryPOST https://hooks.example.com/stowley
Content-Type: application/json
X-Stowley-Event: invoice.paid
X-Stowley-Delivery: 5f1c… (the event id; use it to de-duplicate)
X-Stowley-Signature: t=1756992131,v1=8a2f…
{
"id": "5f1c…",
"type": "invoice.paid",
"created_at": "2026-09-04T14:02:11.000Z",
"org_id": "…",
"data": {
"invoice": {
"id": "…",
"number": "INV-1042",
"client_id": "…",
"client_name": "Hartley & Co.",
"total": 4820,
"paid_at": "2026-09-04T14:02:09.000Z",
"payment_method": "card",
"stripe_payment_intent_id": "pi_…"
}
}
}Verify the signature (Node)
Compute HMAC-SHA256 of <t>.<raw body> with your endpoint's secret and compare it to v1 in constant time. Reject anything older than five minutes.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyStowleySignature(secret, header, rawBody, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
const timestamp = Number(parts.t);
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
const expected = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const got = Buffer.from(parts.v1 ?? "", "hex");
const want = Buffer.from(expected, "hex");
return got.length === want.length && timingSafeEqual(got, want);
}FAQ
Questions integrators ask
Is there a full REST API?
How are webhooks retried?
Can I use this with Zapier or Make?
Who can see a feed?
Which plan includes this?
Migrating from another system? See the import path.
Live demo
Try it in a sandbox first
Sandbox workspaces have feeds and webhooks switched on. Spin one up, add an endpoint, and send a test event.
Quick email check-in for a demo. No strings attached.