CertiPeptideKA PARTNERS
CERTIPEPTIDE / API v1

Developer documentation

From your first integration to reliable order fulfillment.

ONE CONNECTED PROCUREMENT WORKFLOW

Your storefront. Our 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 flow
01

One company per keyOnly your authorized company and stores.

02

Server-calculated pricingUSD cents; quantities in boxes.

03

One order, one payment journeyNo combined payment across orders.

01

See where each API fits

Read from left to right. Select any endpoint to open its parameters and examples.

  1. 01

    Discover & select

    Read your SKU prices, box sizes and available stock.

    Keep skuCode → send quantity in boxes.

  2. 02

    Quote & validate

    Send items and addresses. The server calculates your payable amount.

    Keep quote.id + expiresAt → create before expiry.

  3. 03

    Create once

    Use a unique external reference and a persisted idempotency key.

    Store id + orderNumber + version in your system.

  4. 04

    Pay & reconcile

    Read the live plan. Submit transfer evidence for staff verification.

    A submission is not payment success. Confirm Paid.

  5. 05

    Track & support

    Read current order details after events; open a case when needed.

    Sync fulfillment, tracking events and case updates.

Stay in sync throughout

Webhook notifies → GET the current order → update your local record. Also use GET /orders with an update-time window to reconcile missed notifications.

02

Prepare your integration

The portal works independently. API access is an additional permission.

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.

2. Keep secrets server-side

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>

3. Let the server calculate

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.

4. Respect payment state

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.

Sign in to manage API keys and webhook endpoints →

03

The endpoint reference

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.

Loading your workspace…
04

Build for retries and reconciliation

A timeout is an unknown outcome. Read state before taking another action.

Retain one key per operation

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.

Use a stable update window

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.

Keep each order isolated

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.

Separate receipt from settlement

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 input

Read message and fieldErrors. Correct the specified values; do not blindly retry.

401 / 403Check authorization

Check key validity, company status, allowed scopes and stores. 403 is not solved by another order ID.

404Check the resource

The resource is absent or outside the authorized scope. Confirm your own recorded ID.

409Refresh current state

Read the conflict code and current version, quote, stock or payment plan before deciding.

429Slow down

Honor retryAfter / Retry-After. Use bounded backoff and avoid polling each order in a tight loop.

5xx / timeoutReconcile before retry

Keep the original idempotency key. An ambiguous response may still have completed the operation.

Incremental order reconciliation
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.

05

Events that keep your system current

Authenticate, store, acknowledge, then process. Use the order API as the current source of truth.

Verify HMAC over the exact original request bytes and timestamp. Persist event.id in a durable inbox with a unique constraint before replying 2xx. Repeated events must not repeat business actions; out-of-order events must not overwrite a newer order version.
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.