Beta, invite only
BulkVault API
Show your BulkVault inventory on your own site, sell it with your own checkout, and keep stock in sync with eBay automatically.
Quickstart
- Request access in Settings > API. Once approved, create a key with the permissions you need.
- Keep the key on your server (env var). Never ship it to the browser.
- Fetch your cards:
GET /api/v1/listings. Each entry is one physical copy with its own photos, condition and price. - When your checkout takes payment, report it:
POST /api/v1/orders. Those copies are marked sold and, depending on your setting, their eBay listings are ended. - Add a webhook so your site hears when stock changes anywhere (eBay sales, in-person sales, edits).
curl https://bulkvault.app/api/v1/listings?limit=20 \
-H "Authorization: Bearer $BULKVAULT_API_KEY"Limits and errors
120 requests per minute per key. Every response includes X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; a 429 also has Retry-After. Errors are { "error": { "code", "message" } }.
Selling with Stripe
Report the order from your Stripe webhook. Use the Checkout Session id as external_order_id: retries are safe and never sell twice. If any copy is gone you get a 409 and nothing is marked sold.
// Your Stripe webhook handler, after verifying Stripe's signature
if (event.type === 'checkout.session.completed') {
const session = event.data.object
// You put the BulkVault listing ids in metadata when creating the session
const items = JSON.parse(session.metadata.bulkvault_items) // [{ listing_id, price }]
const res = await fetch('https://bulkvault.app/api/v1/orders', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BULKVAULT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ external_order_id: session.id, items }),
})
if (res.status === 409) {
// Someone bought it elsewhere first: refund this session
}
}Verifying webhooks
Each delivery has a BulkVault-Signature header, t=<unix>,v1=<hex>, an HMAC-SHA256 of `${t}.${rawBody}` with your endpoint secret. Failed deliveries are retried up to 3 times.
import crypto from 'node:crypto'
// rawBody: the exact request body string (don't re-serialize parsed JSON)
export function verifyBulkVault(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')))
const t = Number(parts.t)
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false
const expected = crypto
.createHmac('sha256', secret)
.update(`${t}.${rawBody}`)
.digest('hex')
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ''))
}Reference
Raw spec: /api/v1/openapi.json