For engineering teams

Build on Deliver Hub.
Signed events, typed SDK, every courier.

One API, one SDK, 22 couriers across 18 countries. Every shipment event is cryptographically signed and verifiable by anyone with the shipment id. No proprietary dashboards, no scraping, no surprises.

Download Postman collection OpenAPI 3 JSONno auth required to read the schema

Typed end to end

Every endpoint, every payload, every response has a hand-written TypeScript type. Autocomplete in your editor instead of a Postman tab.

Works in Node and the browser

Bearer token via a callback so your refresh logic plugs in cleanly. Multipart uploads for KYC docs and product images are handled.

Signed shipment events

Every lifecycle event (manifest, pickup, OFD, delivery) is Ed25519-signed and hashed into a per-shipment chain. The public /verify endpoint recomputes it on every request.

HMAC-SHA256 webhooks

Outbound webhooks are signed with a per-endpoint secret. Verify in constant time with any standard crypto library.

TypeScript SDK

From install to first order in five minutes.

The SDK is hand-written, not generated. Every method on HypershipClient maps to one endpoint, with the same name you'd guess.

Install
bash
pnpm add @hypership/sdk
# or: npm install @hypership/sdk / yarn add @hypership/sdk
Initialise
ts
import { HypershipClient } from '@hypership/sdk';

const api = new HypershipClient({
  baseUrl: 'https://api.deliverhub.in',
  getAuthToken: () => process.env.DELIVER_HUB_TOKEN,
});

Three common flows

Rate compare
api.couriers.compareAll
ts
// Rate compare across every live courier for a lane.
const quotes = await api.couriers.compareAll({
  originPincode: '110001',
  destinationPincode: '400001',
  weightGrams: 500,
  paymentMode: 'prepaid',
});

// quotes.options is sorted; pick the cheapest or the fastest.
const cheapest = quotes.options[0];
console.log(cheapest.courierCode, cheapest.totalMinor);
Create and ship
api.orders.create + api.orders.ship
ts
// Create an order. Every field is typed - no runtime guessing.
const order = await api.orders.create({
  externalOrderRef: 'MY-SHOP-1024',
  paymentMode: 'prepaid',
  buyerName: 'Priya Sharma',
  buyerPhone: '9999999999',
  shippingAddressLine1: '12 MG Road',
  shippingCity: 'Bengaluru',
  shippingState: 'KA',
  shippingPincode: '560001',
  weightGrams: 500,
  items: [{ name: 'Cotton tee', quantity: 1, unitPriceMinor: 59900 }],
});

// Then ship it with the courier code you picked from compareAll.
const shipment = await api.orders.ship(order.id, {
  courierCode: 'delhivery_surface_500g',
});
Verify chain
publicApi.verify.getChain
ts
// Fetch the signed event chain for a shipment. No auth required - this
// endpoint is public and rate-limited (30 req/min/IP).
const publicApi = new HypershipClient({ baseUrl: 'https://api.deliverhub.in' });

const chain = await publicApi.verify.getChain(shipment.id);

// Server verifies every hash + Ed25519 signature on each request.
// chain.badge is 'verified' | 'partially_verified' | 'chain_intact' | 'broken'.
if (chain.badge === 'verified') {
  console.log('Chain intact across', chain.events.length, 'events');
}
REST API

Prefer curl? The REST API is the same shape.

Every SDK method is a thin wrapper over a stable REST endpoint. Bearer tokens, JSON in, JSON out, no surprises.

1. Log in
POST /api/auth/login
bash
# 1. Exchange email + password for an access token.
curl -X POST https://api.deliverhub.in/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"..."}'

# Response (shape from AuthService):
# {
#   "accessToken": "eyJhbGciOi...",
#   "accessTokenExpiresAt": "2026-10-13T13:00:00.000Z",
#   "refreshToken": "...",
#   "refreshTokenExpiresAt": "2026-11-12T12:00:00.000Z",
#   "userId": "usr_..."
# }
2. Compare rates
POST /api/merchants/me/couriers/compare-all
bash
# 2. Compare every courier for a lane. Returns normalised quotes
# with freight, surcharges, GST and the final payable.
curl -X POST https://api.deliverhub.in/api/merchants/me/couriers/compare-all \
  -H 'Authorization: Bearer eyJhbGciOi...' \
  -H 'Content-Type: application/json' \
  -d '{
    "originPincode": "110001",
    "destinationPincode": "400001",
    "weightGrams": 500,
    "paymentMode": "prepaid"
  }'

# Response excerpt:
# {
#   "options": [
#     {
#       "courierCode": "delhivery_surface_500g",
#       "courierDisplayName": "Delhivery Surface",
#       "totalMinor": 6900,
#       "freightMinor": 5800,
#       "codMinor": 0,
#       "gstMinor": 1100,
#       "etaBusinessDays": { "min": 3, "max": 5 },
#       "rating": { "stars": 4.2, "sampleSize": 1840 }
#     },
#     ...
#   ]
# }
Webhooks

HMAC-SHA256, timing-safe, over the raw body.

The same shape our Shopify ingestion uses. We sign the raw request bytes with your per-endpoint secret and deliver the digest in the X-DeliverHub-Signature header. Verify in constant time; reject anything that doesn't match.

Node + Express receiver
constant-time signature check
ts
import { createHmac, timingSafeEqual } from 'node:crypto';
import express from 'express';

const app = express();

// IMPORTANT: capture the RAW body - the signature is over bytes, not JSON.
app.post(
  '/deliverhub/webhooks',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const header = String(req.headers['x-deliverhub-signature'] ?? '');
    const digest = createHmac('sha256', process.env.DH_WEBHOOK_SECRET!)
      .update(req.body)
      .digest('base64');

    const a = Buffer.from(header, 'utf8');
    const b = Buffer.from(digest, 'utf8');
    if (a.length !== b.length || !timingSafeEqual(a, b)) {
      return res.status(401).send('bad signature');
    }

    const event = JSON.parse(req.body.toString('utf8'));
    // event.type: 'shipment.manifested' | 'shipment.in_transit' |
    //             'shipment.out_for_delivery' | 'shipment.delivered' |
    //             'shipment.ndr' | 'shipment.rto' | ...
    handle(event);
    res.sendStatus(200);
  },
);
Why raw bytes? JSON stringification is not deterministic across runtimes. The HMAC is over the exact bytes we delivered, so app.use(express.json()) would eat the signable payload before you got to it. Use express.raw() for the webhook route only.

Also available

The REST API is the source of truth; language SDKs sit on top. We ship TypeScript today and will add others as pilot teams ask for them, not before.

TypeScript / JavaScript

Shipped

Shipped. @hypership/sdk, hand-maintained, no codegen.

Python

Roadmap

On the roadmap. The REST API is stable - a Python client over httpx is a weekend project if you need it before we ship.

Go

Roadmap

On the roadmap. Public OpenAPI schema below will let us generate it when demand surfaces.

PHP

Roadmap

On the roadmap. We will prioritise this when the Magento integration lands.

Resources

Public developer surfaces as we open them up. Ask us for anything you need to evaluate the API against your stack.

None of the above is a dark pattern. If a resource is marked "Coming soon" it isn't live yet. Email dev@deliverhub.in for the current working link.

Grab a sandbox token.

Tell us what you're building and we'll send credentials, a seeded merchant, and someone on the eng team to help you ship.

Start shipping in 5 minutes.

No setup fees. No minimum volume. Live rate comparison from day one.