You're viewing the public API reference. To generate an API key and start making requests, sign in to your Obermeyer Dropship account.

Sign in
API Documentation

Obermeyer Dropship Platform

A REST API for submitting drop-ship orders, browsing live inventory, and receiving push notifications when orders are accepted, shipped, or fail. All requests are authenticated with a bearer token; all responses are JSON unless otherwise noted.

Base URLhttps://dropship.obermeyer.comAuthBearer tokenFormatJSON · UTF-8

Introduction

Quick start in three steps

  1. 1. Generate an API key

    Go to Settings → API Keys and click Create Key. The raw token is shown exactly once at creation — copy it into a password manager or secret store immediately.

  2. 2. Validate your line items

    Hit POST /api/orders/validate with your line items to confirm UPCs match Obermeyer's inventory before you ever submit an order. Catches typos, missing barcodes, and out-of-stock SKUs.

  3. 3. Submit the order

    POST /api/orders with the customer PO, ship-to address, and lines. You'll get a 201 with the platform order id; subsequent state changes (accepted, shipped, error) come back via webhook events or by polling GET /api/orders/:id.

Authentication

Bearer tokens for all requests

Every request must include an Authorization header containing your API key. Keys are scoped to the user who created them and inherit that user's customer code — orders submitted with the key are automatically associated with your account.

http
Authorization: Bearer obey_live_a1b2c3d4e5f6...
  • Keys begin with the prefix obey_live_.
  • Keys are stored only as a SHA-256 hash. If you lose a key, regenerate — we cannot recover it.
  • Revoke a key at any time from Settings → API Keys — revoked keys 401 immediately.
  • Sessions cookies (browser) also work for the same endpoints if you're calling from a logged-in tab.

Conventions & errors

Status codes, error shape, dates

HTTP status codes

  • 200OKSuccessful read.
  • 201CreatedOrder created and relayed to ERP.
  • 400Bad RequestValidation failed — missing fields, unknown UPC, malformed JSON. Body contains details when applicable.
  • 401UnauthorizedMissing, malformed, or revoked API key.
  • 403ForbiddenAuthenticated, but the resource isn't yours.
  • 404Not FoundResource does not exist or is owned by another account.
  • 409ConflictDuplicate PO — same customer_po within the last 24 hours. Body includes existingOrder.
  • 502Bad GatewayOrder saved on our side but the downstream relay to the ERP failed. Order lands in the error queue; safe to retry from the dashboard.
  • 500Server ErrorUnexpected — please retry. Persistent 500s are bugs; report them.

Error response shape

jsonHTTP 400
{
  "error": "Order lines missing or unrecognized barcodes",
  "details": [
    {
      "style": "21104",
      "color": "BLK",
      "size": "M",
      "sku_composite": "21104-BLK-M",
      "requested": 2,
      "upc": null,
      "available": 0,
      "status": "missing_upc",
      "warning": true,
      "candidates": [
        { "upc": "888555971149", "size": "S",  "sku_composite": "21104-BLK-S",  "available": 12 },
        { "upc": "888555971156", "size": "M",  "sku_composite": "21104-BLK-M",  "available":  4 },
        { "upc": "888555971163", "size": "L",  "sku_composite": "21104-BLK-L",  "available":  0 }
      ]
    }
  ]
}

Other conventions

  • All timestamps are ISO 8601 in UTC (2026-04-21T15:08:22.000Z).
  • All money values are decimal strings or numbers in USD.
  • Order ids are zero-padded sequential strings prefixed with DS- (e.g. DS-001234) — give this number to your Obermeyer rep when calling about an order. Other resources (shipments, events) use lowercase v4 UUIDs.
  • Pagination uses page/limit query params; max limit is 100.

Orders endpoints

Create, list, retrieve, upload, and validate orders

POST/api/orders·API key

Create a single drop-ship order with one or more line items. The order is validated against live inventory, persisted, and immediately relayed to the ERP.

Request body

FieldTypeRequiredDescription
customer_postringRequiredYour purchase-order reference. Must be unique per customer per 24-hour window.
linesOrderLine[]RequiredAt least one line item. See OrderLine below.
ship_namestringOptionalRecipient name on the shipping label.
address1stringOptionalStreet address line 1.
address2stringOptionalStreet address line 2 (suite, apt, etc).
citystringOptionalDestination city.
statestringOptionalTwo-letter state or province code.
zipstringOptionalPostal code.
countrystringOptionalISO country code. Defaults to "US".
phonestringOptionalRecipient phone number for delivery.
emailstringOptionalRecipient email for delivery notifications.
contactstringOptionalContact name at the destination.
start_ship_datedate (YYYY-MM-DD)OptionalEarliest acceptable ship date.
comp_ship_datedate (YYYY-MM-DD)OptionalCancel-by / completion ship date.
special_instructionsstringOptionalFree-text delivery notes (max 500 chars).
routingstringOptionalCarrier routing instructions, if you have a preferred carrier on file.
order_notesstringOptionalInternal notes — visible to admins only.

OrderLine

FieldTypeRequiredDescription
stylestringRequiredObermeyer style number (e.g. 21104).
colorstringRequiredColor code (e.g. BLK).
sizestringRequiredSize code (e.g. M).
quantityintegerRequiredUnits ordered (≥ 1).
upcstringRequired12-digit barcode. Must match an active SKU in Obermeyer inventory.
seasonstringOptionalOptional season tag (e.g. "2025-26").
style_descriptionstringOptionalOptional description for your records.
bashRequest
curl -X POST https://dropship.obermeyer.com/api/orders \
  -H "Authorization: Bearer obey_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_po": "PO-12345",
    "ship_name": "Aspen Mountain Sports",
    "address1": "123 Main St",
    "city": "Aspen",
    "state": "CO",
    "zip": "81611",
    "country": "US",
    "phone": "970-555-0142",
    "email": "ops@example.com",
    "lines": [
      {
        "style": "21104",
        "color": "BLK",
        "size": "M",
        "quantity": 2,
        "upc": "888555971156"
      }
    ]
  }'
json201 Created
{
  "order": {
    "id": "DS-001234",
    "customer_code": "wick123",
    "customer_po": "PO-12345",
    "status": "submitted",
    "submission_method": "api",
    "ship_name": "Aspen Mountain Sports",
    "address1": "123 Main St",
    "city": "Aspen",
    "state": "CO",
    "zip": "81611",
    "country": "US",
    "submitted_at": "2026-04-21T15:07:52.791Z",
    "created_at": "2026-04-21T15:07:52.791Z",
    "order_total": 100,
    "lines": [
      {
        "id": "5a14...",
        "line_number": 1,
        "style": "21104",
        "color": "BLK",
        "size": "M",
        "quantity": 2,
        "sell_price": "50",
        "upc": "888555971156",
        "sku_composite": "21104-BLK-M"
      }
    ]
  }
}

Pricing

Prices are calculated on our side using your account's active terms (set by your sales rep) and returned in the response on every line and as order_total. Any sell_price sent in the request body is ignored. See the price list endpoint below to fetch your current prices ahead of time.

Notable error responses

  • 400Missing/unknown UPC — body includes details[].candidates with valid barcodes for that style + color so you can self-correct.
  • 409Duplicate PO — body includes existingOrder with the prior order id and status.
  • 409No pricing terms — your account has no active pricing terms. Contact your sales rep.
  • 422Product has no price — a UPC in the request has no published price yet. Body includes the offending upc.
  • 502Make.com relay failed — body includes the saved order plus makeError and makeStatus. Order is in the error queue and can be retried from the dashboard.
GET/api/orders·API key

List orders for your account, newest first. Customer accounts only see their own orders.

Query parameters

FieldTypeRequiredDescription
pageintegerOptional1-indexed page number. Default 1.
limitintegerOptionalPage size. Default 20, max 100.
statusstringOptionalFilter by status (e.g. submitted, accepted, shipped, error).
searchstringOptionalSubstring search on customer_po (case-insensitive).
bashRequest
curl "https://dropship.obermeyer.com/api/orders?status=accepted&limit=20" \
  -H "Authorization: Bearer obey_live_YOUR_KEY"
json200 OK
{
  "orders": [
    {
      "id": "DS-001234",
      "customer_po": "PO-12345",
      "customer_code": "wick123",
      "status": "accepted",
      "erp_order_number": "1048392",
      "accepted_at": "2026-04-21T15:08:22.000Z",
      "created_at": "2026-04-21T15:07:52.791Z",
      "_count": { "lines": 1 }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 47,
    "totalPages": 3
  }
}
GET/api/orders/{id}·API key

Retrieve a single order with its line items, shipments, and event timeline.

Path parameters

FieldTypeRequiredDescription
idstringRequiredThe platform order id returned from order creation, e.g. DS-001234.
bashRequest
curl https://dropship.obermeyer.com/api/orders/DS-001234 \
  -H "Authorization: Bearer obey_live_YOUR_KEY"
json200 OK
{
  "order": {
    "id": "DS-001234",
    "customer_po": "PO-12345",
    "status": "shipped",
    "erp_order_number": "1048392",
    "accepted_at": "2026-04-21T15:08:22.000Z",
    "shipped_at": "2026-04-23T16:00:00.000Z",
    "lines": [ /* OrderLine[] */ ],
    "shipments": [
      {
        "id": "a2b8f501-7e4c-49aa-b3a7-d2f1e0cbd6a9",
        "trackingNumber": "1Z999AA10123456784",
        "carrier": "UPS",
        "shipDate": "2026-04-23T16:00:00.000Z",
        "lines": [
          { "style": "21104", "color": "BLK", "size": "M", "qty": 2 }
        ]
      }
    ],
    "events": [
      {
        "type": "shipped",
        "fromStatus": "accepted",
        "toStatus": "shipped",
        "actorRole": "system",
        "actorLabel": "Make.com webhook",
        "message": "Shipment recorded via UPS (1Z999AA10123456784) — order fully shipped",
        "createdAt": "2026-04-23T16:00:00.000Z"
      }
    ]
  }
}
POST/api/orders/upload·API key

Submit an order whose line items come from a CSV or XLSX file. Header fields are sent alongside the file as multipart form fields.

Multipart form fields

FieldTypeRequiredDescription
filefileRequiredCSV or XLSX file. See the CSV format section for required columns.
customer_postringRequiredYour purchase-order reference.
ship_namestringOptionalRecipient name.
address1, address2, city, state, zipstringOptionalDestination address fields.
countrystringOptionalISO country code. Defaults to "US".
phone, emailstringOptionalDelivery contact info.
special_instructionsstringOptionalFree-text notes.
start_ship_date, comp_ship_datedateOptionalShipping window.
bashRequest
curl -X POST https://dropship.obermeyer.com/api/orders/upload \
  -H "Authorization: Bearer obey_live_YOUR_KEY" \
  -F "file=@order.csv" \
  -F "customer_po=PO-12345" \
  -F "ship_name=Aspen Mountain Sports" \
  -F "address1=123 Main St" \
  -F "city=Aspen" \
  -F "state=CO" \
  -F "zip=81611"
json201 Created
{
  "order": {
    "id": "DS-001234",
    "submission_method": "csv_upload",
    "status": "submitted",
    "lines": [ /* one OrderLine per CSV row */ ]
  },
  "parseWarnings": [
    { "row": 4, "message": "Skipped — missing required column 'qty'" }
  ]
}

parseWarnings is only present when some rows were skipped due to malformed data. Rows that parse successfully are submitted even when others are skipped.

POST/api/orders/validate·API key

Dry-run the inventory check that /api/orders performs at submit time. No order is created. Use this to surface UPC errors and stock warnings in your UI before the customer commits.

Request body

FieldTypeRequiredDescription
linesOrderLine[]RequiredSame shape as in /api/orders. Only style, color, size, quantity, upc are inspected.
bashRequest
curl -X POST https://dropship.obermeyer.com/api/orders/validate \
  -H "Authorization: Bearer obey_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "lines": [
      { "style": "21104", "color": "BLK", "size": "M", "quantity": 2, "upc": "888555971156" },
      { "style": "21104", "color": "BLK", "size": "L", "quantity": 999 }
    ]
  }'
json200 OK
{
  "validation": [
    {
      "style": "21104", "color": "BLK", "size": "M",
      "sku_composite": "21104-BLK-M",
      "requested": 2, "available": 12,
      "upc": "888555971156",
      "status": "ok",
      "warning": false
    },
    {
      "style": "21104", "color": "BLK", "size": "L",
      "sku_composite": "21104-BLK-L",
      "requested": 999, "available": 0,
      "upc": null,
      "status": "missing_upc",
      "warning": true,
      "candidates": [
        { "upc": "888555971163", "size": "L", "sku_composite": "21104-BLK-L", "available": 0 }
      ]
    }
  ]
}

Status values

  • ok — UPC matched and stock is sufficient.
  • insufficient — UPC matched but ordered qty exceeds available; /api/orders will still accept the order with a warning recorded.
  • missing_upc — line had no upc field. Hard fail at submit time.
  • unknown_upc — UPC was provided but doesn't match any active inventory record. Hard fail at submit time.

Inventory endpoints

Search live stock and bulk-validate baskets

GET/api/inventory·API key

Browse current stock. Customers only see SKUs that have been matched between Shopify and the warehouse feed (no internal-only or shopify-only rows).

Query parameters

FieldTypeRequiredDescription
stylestringOptionalSubstring match on style code (case-insensitive).
colorstringOptionalSubstring match on color code.
sizestringOptionalSubstring match on size code.
pageintegerOptionalDefault 1. Ignored when all=1.
limitintegerOptionalDefault 50, max 100. Ignored when all=1.
allbooleanOptionalPass "1" to return every match in one response. Recommended for nightly catalog sync.
bashRequest — paged
curl "https://dropship.obermeyer.com/api/inventory?style=21104&page=1&limit=50" \
  -H "Authorization: Bearer obey_live_YOUR_KEY"
bashRequest — bulk sync
curl "https://dropship.obermeyer.com/api/inventory?all=1" \
  -H "Authorization: Bearer obey_live_YOUR_KEY"
json200 OK
{
  "items": [
    {
      "style": "21104",
      "color": "BLK",
      "size": "M",
      "skuComposite": "21104-BLK-M",
      "upc": "888555971156",
      "availableQty": 12,
      "status": "matched",
      "msrp": 159,
      "yourPrice": 80,
      "lastFeedAt": "2026-04-29T13:00:00.000Z"
    }
  ],
  "page": 1,
  "limit": 50,
  "total": 1248,
  "totalPages": 25,
  "lastFeedAt": "2026-04-29T13:00:00.000Z",
  "pricingTerms": { "formula_type": "pct_off_msrp", "value": 50 }
}

yourPriceis calculated using your account's active pricing terms and rounded up to the nearest dollar. If your account has no active terms, yourPrice is null and orders will be rejected — contact your sales rep to set up pricing.

lastFeedAtat the top level reflects the most recent inventory sync run. Use it to display a “data current as of” timestamp in your UI.

GET/api/inventory/check·API key

Bulk-check a list of items against current stock without creating an order. Convenient for AJAX cart validation.

Query parameters

FieldTypeRequiredDescription
itemsJSON-encoded arrayRequiredURL-encoded JSON array of { style, color, size, quantity, upc? } objects.
bashRequest
ITEMS='[{"style":"21104","color":"BLK","size":"M","quantity":2,"upc":"888555971156"}]'

curl -G https://dropship.obermeyer.com/api/inventory/check \
  --data-urlencode "items=$ITEMS" \
  -H "Authorization: Bearer obey_live_YOUR_KEY"
json200 OK
{
  "results": [
    {
      "style": "21104", "color": "BLK", "size": "M",
      "sku_composite": "21104-BLK-M",
      "requested": 2, "available": 12,
      "upc": "888555971156",
      "status": "ok",
      "warning": false
    }
  ],
  "allSufficient": true
}

Pricing endpoints

Fetch your account-specific prices for every product

GET/api/pricing·API key

Returns the full price list for your account: one row per matched UPC with MSRP and your calculated price. Pass format=csv to download the same data as a CSV attachment.

Query parameters

FieldTypeRequiredDescription
formatstringOptionalPass "csv" for a downloadable CSV. Default returns JSON.
bashRequest — JSON
curl https://dropship.obermeyer.com/api/pricing \
  -H "Authorization: Bearer obey_live_YOUR_KEY"
bashRequest — CSV download
curl https://dropship.obermeyer.com/api/pricing?format=csv \
  -H "Authorization: Bearer obey_live_YOUR_KEY" \
  -o pricelist.csv
json200 OK
{
  "customer_code": "WICK001",
  "terms": {
    "formula_type": "pct_off_msrp",
    "value": 50,
    "description": "50% off MSRP"
  },
  "count": 1248,
  "items": [
    {
      "style": "21104",
      "color": "BLK",
      "size": "M",
      "sku": "21104-BLK-M",
      "upc": "888555971156",
      "msrp": 159,
      "your_price": 80,
      "available_qty": 12
    }
  ]
}

Notable error responses

  • 409No pricing terms — your account has no active terms. Contact your sales rep.
POST/api/pricing/quote·API key

Quote a basket of UPCs without creating an order. Returns unit price, line totals, and order total. Useful for showing live cart subtotals in your own UI.

bashRequest
curl -X POST https://dropship.obermeyer.com/api/pricing/quote \
  -H "Authorization: Bearer obey_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "lines": [
      { "upc": "888555971156", "quantity": 2 },
      { "upc": "888555971163", "quantity": 1 }
    ]
  }'
json200 OK
{
  "items": [
    { "upc": "888555971156", "unit_price": 80, "msrp": 159, "quantity": 2, "line_total": 160 },
    { "upc": "888555971163", "unit_price": 80, "msrp": 159, "quantity": 1, "line_total": 80 }
  ],
  "order_total": 240
}

Webhooks

Server-pushed events instead of polling

Register a webhook endpoint from Settings → Webhooks. Pick the events you want, copy the signing secret (shown exactly once), and we'll POST signed JSON to your URL whenever those events fire.

Supported events

order.submitted

Order received and relayed to the ERP. Fires once per order.

order.accepted

ERP confirmed the order and assigned an order number.

order.shipped

A shipment was recorded. Fires once per shipment row — partial shipments fire multiple times.

order.error

Submission failed at any stage. Includes error_message.

Request headers

http
Content-Type: application/json
X-Obey-Event: order.accepted
X-Obey-Signature: sha256=<hex-hmac>
X-Obey-Delivered-At: 2026-04-21T15:08:22.000Z
User-Agent: ObermeyerDropship-Webhook/1.0

Envelope

All events share the same outer envelope. The shape inside data varies per event — see below.

json
{
  "event": "order.accepted",
  "delivered_at": "2026-04-21T15:08:22.000Z",
  "data": { /* event-specific payload */ }
}

Event payloads

order.submitted · data

json
{
  "order_id": "DS-001234",
  "customer_po": "PO-12345",
  "customer_code": "wick123",
  "status": "submitted",
  "submitted_at": "2026-04-21T15:07:52.791Z",
  "lines": [
    { "line_number": 1, "style": "21104", "color": "BLK", "size": "M", "quantity": 2, "upc": "888555971156" }
  ]
}

order.accepted · data

json
{
  "order_id": "DS-001234",
  "customer_po": "PO-12345",
  "customer_code": "wick123",
  "status": "accepted",
  "erp_order_number": "1048392",
  "accepted_at": "2026-04-21T15:08:22.000Z",
  "line_results": [
    { "line_number": 1, "sql_id": "5820471", "success": true }
  ]
}

order.shipped · data

json
{
  "order_id": "DS-001234",
  "customer_po": "PO-12345",
  "customer_code": "wick123",
  "shipment_id": "a2b8f501-7e4c-49aa-b3a7-d2f1e0cbd6a9",
  "tracking_number": "1Z999AA10123456784",
  "carrier": "UPS",
  "ship_date": "2026-04-23T16:00:00.000Z",
  "line_items": [
    { "line_number": 1, "upc": "888555971156", "quantity": 2 }
  ],
  "order_complete": true,
  "total_ordered": 2,
  "total_shipped": 2
}

When an order ships in multiple boxes, this event fires per shipment. order_complete is false on intermediate shipments and true on the final one.

order.error · data

json
{
  "order_id": "DS-001234",
  "customer_po": "PO-12345",
  "customer_code": "wick123",
  "status": "error",
  "error_message": "Customer code 'wick123' not found in ERP",
  "error_code": "CUST_NOT_FOUND",
  "failed_at": "2026-04-21T15:08:22.000Z"
}

Verifying the signature

The signature is an HMAC-SHA256 of the raw request body using your webhook secret, prefixed with sha256=. Always compare with a constant-time comparison to avoid timing attacks. Use the raw body as received — re-serializing the parsed JSON will produce a different hash.

javascriptNode.js
import { createHmac, timingSafeEqual } from 'crypto';

export function verifyObeySignature(rawBody, headerValue, secret) {
  const expected = 'sha256=' + createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  const a = Buffer.from(headerValue || '');
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

// Express example — note express.raw() to keep the body unparsed
app.post('/obey-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-obey-signature'];
  if (!verifyObeySignature(req.body, sig, process.env.OBEY_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }
  const event = JSON.parse(req.body.toString('utf8'));
  // ... handle event ...
  res.json({ ok: true });
});
pythonPython
import hmac, hashlib

def verify_obey_signature(raw_body: bytes, header_value: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode("utf-8"),
        raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, header_value or "")

# Flask example
@app.route("/obey-webhook", methods=["POST"])
def obey_webhook():
    sig = request.headers.get("X-Obey-Signature", "")
    if not verify_obey_signature(request.get_data(), sig, os.environ["OBEY_WEBHOOK_SECRET"]):
        abort(401)
    event = request.get_json()
    # ... handle event ...
    return {"ok": True}

Retries & expectations

  • Respond with HTTP 2xx within 10 seconds to acknowledge.
  • Failed deliveries retry up to 3 times with exponential backoff (0s, 2s, 8s).
  • Repeated failures bump failure_count, visible from Settings; pause or resume any subscription.
  • Webhooks deliver at-least-once. Dedupe on your end by data.order_id + event if you persist them.
  • Order matters loosely but is not guaranteed. If you need strict ordering, fetch GET /api/orders/:id when you receive a notification rather than rebuilding state from the events.

CSV upload format

Required columns and template

CSV and XLSX files share the same column schema. Headers are case-insensitive and may appear in any order. Each row becomes one order line.

Columns

FieldTypeRequiredDescription
stylestringRequiredProduct style number (e.g. 21104)
colorstringRequiredColor code (e.g. BLK, NAV)
sizestringRequiredSize code (e.g. S, M, L, XL)
qtyintegerRequiredOrder quantity
pricedecimalRequiredUnit price (e.g. 49.99)
upcstringRequired12-digit barcode. Required at submit; CSV parser accepts blank rows but submission will reject them.
Download CSV template

Order field reference

Status flow, header fields, line item fields

Status flow

CreatedValidatedSubmittedAcceptedProcessingShipped

Orders may also enter partially_shipped when shipments are recorded one box at a time, and error on any failure. Terminal statuses are shipped, cancelled, and error.

Order header

FieldTypeRequiredDescription
customer_postringRequiredYour purchase-order number, unique per order.
ship_namestringOptionalRecipient name on the shipping label.
address1, address2stringOptionalStreet address lines.
city, state, zip, countrystringOptionalDestination address.
phone, emailstringOptionalContact info for the delivery.
start_ship_date, comp_ship_datedateOptionalEarliest and cancel-by ship dates.
special_instructionsstringOptionalFree-text delivery notes.

Order line

FieldTypeRequiredDescription
stylestringRequiredObermeyer style number.
colorstringRequiredColor code.
sizestringRequiredSize code.
quantityintegerRequiredUnits ordered.
upcstringRequired12-digit barcode (required at submit).

Need something not covered here?

Contact your Obermeyer account manager or email dropship@obermeyer.com. Bug reports against the dashboard or this API should include the order_id if applicable.