1. Enable access & choose scopes
Ask your service manager to enable API access. Your owner then creates a scoped API key in the portal.
catalog:read · orders:read · orders:write
Read the exact scope shown on each endpoint.
From your first integration to reliable order fulfillment.
Your server selects products, receives your negotiated prices and creates an order. CertiPeptide verifies payment, procures and ships; your system follows each order through the same API.
Explore the integration flowOne company per keyOnly your authorized company and stores.
Server-calculated pricingUSD cents; quantities in boxes.
One order, one payment journeyNo combined payment across orders.
Read from left to right. Select any endpoint to open its parameters and examples.
Read your SKU prices, box sizes and available stock.
Keep skuCode → send quantity in boxes.
Send items and addresses. The server calculates your payable amount.
Keep quote.id + expiresAt → create before expiry.
Use a unique external reference and a persisted idempotency key.
Store id + orderNumber + version in your system.
Read the live plan. Submit transfer evidence for staff verification.
A submission is not payment success. Confirm Paid.
Read current order details after events; open a case when needed.
Sync fulfillment, tracking events and case updates.
Webhook notifies → GET the current order → update your local record. Also use GET /orders with an update-time window to reconcile missed notifications.
The portal works independently. API access is an additional permission.
Ask your service manager to enable API access. Your owner then creates a scoped API key in the portal.
catalog:read · orders:read · orders:write
Read the exact scope shown on each endpoint.
Read B2B_API_KEY from your server environment and pass it as a Bearer token. Never place it in browser scripts, URLs, repositories or logs.
Authorization: Bearer <API_KEY>10000 cents = USD 100.00. Send 2 for two boxes; packQuantity tells you vials per box. SKU IDs, SKU codes, order IDs and order numbers are different identifiers.
Keep canonical field names and enum values in English in both languages.
Uploading a receipt or submitting a transfer never marks an order paid. Re-read payment status after verification. Online availability and per-part limits come from payment-plan; online parts cannot exceed USD 3,000.
Purpose, exact inputs, response fields and working request formats. Loaded from the current API contract.
Machine API shows endpoints usable with a scoped API key. Account activation, passwords and key management belong to the portal.
A timeout is an unknown outcome. Read state before taking another action.
For endpoints requiring Idempotency-Key, save the key and request body before sending. Retry the identical request with that key. A new business action needs a new key; changing a request body under an old key causes a conflict.
List orders with sort=updated_asc, updatedFrom inclusive and updatedTo exclusive. Follow nextCursor without changing filters. Persist the completed window only after all pages, overlap the next window slightly and deduplicate by order ID and version.
Use only IDs returned to your authorized company and stores. Never treat a storefront order link as a wholesale API resource. For amendments, get the current version and use the quote bound to that same order.
payment.received can cover only part of an order. order.paid plus the latest order/payment-plan confirms full coverage. Do not combine orders, repeat a transfer while another attempt is unresolved, or calculate payment parts yourself.
400Fix the inputRead message and fieldErrors. Correct the specified values; do not blindly retry.
401 / 403Check authorizationCheck key validity, company status, allowed scopes and stores. 403 is not solved by another order ID.
404Check the resourceThe resource is absent or outside the authorized scope. Confirm your own recorded ID.
409Refresh current stateRead the conflict code and current version, quote, stock or payment plan before deciding.
429Slow downHonor retryAfter / Retry-After. Use bounded backoff and avoid polling each order in a tight loop.
5xx / timeoutReconcile before retryKeep the original idempotency key. An ambiguous response may still have completed the operation.
GET /orders?sort=updated_asc&updatedFrom=2026-09-18T00%3A00%3A00Z&updatedTo=2026-09-19T00%3A00%3A00Z&limit=20 GET /orders?sort=updated_asc&updatedFrom=2026-09-18T00%3A00%3A00Z&updatedTo=2026-09-19T00%3A00%3A00Z&limit=20&cursor=<nextCursor>
Encode the returned cursor as a query value. These dates are an example; use your actual synchronization window.
Authenticate, store, acknowledge, then process. Use the order API as the current source of truth.
X-CertiPeptide-Event-Id: <stable event ID> X-CertiPeptide-Timestamp: <Unix seconds> X-CertiPeptide-Signature: v1=<hex HMAC-SHA256> HMAC-SHA256(secret, timestamp + '.' + rawBody)
Verify the original request bytes with a 300-second timestamp tolerance and constant-time comparison. Store the event durably before acknowledging. Delivery retries use backoff; repeated or out-of-order events are expected.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody must be the original Buffer, before JSON parsing.
export function verifyWebhook(rawBody, headers, secret) {
const ts = headers['x-certipeptide-timestamp'];
const sig = headers['x-certipeptide-signature'];
const eventId = headers['x-certipeptide-event-id'];
if (!/^\d{10,}$/.test(ts || '') ||
Math.abs(Date.now() / 1000 - Number(ts)) > 300 ||
!/^v1=[a-f0-9]{64}$/.test(sig || '')) {
throw new Error('Invalid or stale webhook');
}
const expected = createHmac('sha256', secret)
.update(ts + '.').update(rawBody).digest();
const received = Buffer.from(sig.slice(3), 'hex');
if (received.length !== expected.length ||
!timingSafeEqual(received, expected)) throw new Error('Bad signature');
const event = JSON.parse(rawBody.toString('utf8'));
if (!eventId || event.id !== eventId) throw new Error('Event ID mismatch');
return event;
}
// Atomically persist event.id + the event in a durable inbox, then return 2xx.
// Enforce a UNIQUE(event_id) constraint; duplicates should also receive 2xx.
// A worker fetches the current authenticated order before applying changes.
// payment.received / order.partially_paid are NOT order.paid.