Integrations / Shopify
Shopify integration guide
Shopify stores call our API through a custom app that runs on your own server. The pattern is: Shopify fires an order webhook → your server calls Serian ShipKit → your server writes the fulfillment back to Shopify.
1. Create a Shopify custom app
- In your Shopify admin, go to Settings → Apps and sales channels → Develop apps.
- Create a new app. Grant it the scopes
read_orders,write_fulfillments, andwrite_shipping. - Install the app and copy its Admin API access token.
2. Register a webhook for new orders
In the app’s configuration, add an orders/paid webhook that points to a URL on your server, for example https://yourserver.com/hooks/shopify.
3. On your server, create a label and fulfill
import express from "express";
import crypto from "crypto";
const app = express();
app.use(express.raw({ type: "application/json" }));
app.post("/hooks/shopify", async (req, res) => {
const hmac = req.get("X-Shopify-Hmac-Sha256");
const expected = crypto
.createHmac("sha256", process.env.SHOPIFY_WEBHOOK_SECRET!)
.update(req.body)
.digest("base64");
if (hmac !== expected) return res.status(401).end();
const order = JSON.parse(req.body.toString());
// 1. Ask Serian ShipKit for rates using the shipping address
const ratesRes = await fetch("https://api.serianshipkit.com/api/v1/rates", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.SERIAN_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
shipper: {
name: "Your Warehouse",
street1: process.env.SHIP_FROM_STREET1!,
city: process.env.SHIP_FROM_CITY!,
state: process.env.SHIP_FROM_STATE!,
zip: process.env.SHIP_FROM_ZIP!,
country: process.env.SHIP_FROM_COUNTRY ?? "GH",
},
recipient: {
name: order.shipping_address.name,
street1: order.shipping_address.address1,
city: order.shipping_address.city,
state: order.shipping_address.province_code,
zip: order.shipping_address.zip,
country: order.shipping_address.country_code,
},
parcels: [{
length: 20, width: 15, height: 10, weight: 1,
dimension_unit: "CM", weight_unit: "KG",
}],
}),
});
const { rates } = await ratesRes.json();
const cheapest = rates[0];
// 2. Create the label
const shipRes = await fetch("https://api.serianshipkit.com/api/v1/shipments", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.SERIAN_API_KEY!,
"Content-Type": "application/json",
"Idempotency-Key": "shopify-order-" + order.id,
},
body: JSON.stringify({
rate_id: cheapest.rate_id,
reference: order.name,
}),
});
const shipment = await shipRes.json();
// 3. Write the fulfillment back into Shopify
await fetch(
`https://${process.env.SHOPIFY_STORE}.myshopify.com/admin/api/2024-10/orders/${order.id}/fulfillments.json`,
{
method: "POST",
headers: {
"X-Shopify-Access-Token": process.env.SHOPIFY_ADMIN_TOKEN!,
"Content-Type": "application/json",
},
body: JSON.stringify({
fulfillment: {
tracking_number: shipment.tracking_number,
tracking_company: shipment.carrier.toUpperCase(),
tracking_urls: [shipment.label_url],
notify_customer: true,
},
}),
}
);
res.status(200).end();
});4. Close the loop with tracking webhooks
Register a webhook in your Serian ShipKit dashboard for shipment.delivered and tracking.updated, pointing to another endpoint on your server. When those fire, update the Shopify fulfillment’s tracking info (the buyer already gets an email from Shopify).
Deploying
Any HTTP-capable host works: Vercel, Render, Fly.io, Railway, your own box. We recommend always sending the Idempotency-Key header so retries are safe, and starting with a sk_test_key until you’ve verified end-to-end.