API reference 2026-07

Slab Dynasty Partner API — REST, dated versions, Shopify-shaped.

Request access
On this page

The Partner API follows Shopify's Admin REST conventions closely — same resource envelopes, same cursor-based pagination, same leaky-bucket rate limiting. If you have integrated with Shopify before, the main change is the host and the auth header name.

Every request goes to https://api.slabdynasty.com and carries an API key. Keys are issued from the Developers tab of an approved seller account.

Verify your key

export SLAB_API_KEY="slab_live_..."   # the token shown once at creation

curl https://api.slabdynasty.com/2026-07/ping \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY"

Reading this with a tool instead of a browser? The whole reference is also published as an OpenAPI 3.1 spec and as a single Markdown document (llms.txt) — both generated from the same source as this page, so they cannot drift from it.

Pass your key in either header. The X-SlabDynasty-Access-Token form mirrors Shopify's X-Shopify-Access-Token, so ported client code needs only a header rename.

X-SlabDynasty-Access-Token: slab_live_...

# or, equivalently
Authorization: Bearer slab_live_...

Credentials are never accepted in the query string — a request carrying ?access_token= is rejected with a 401 before anything else happens, because URLs end up in logs, proxies and browser history.

Keys are minted per environment and the environment is baked into the token (slab_live_… / slab_test_…). Responses carry X-SlabDynasty-Api-Mode so you can assert which one you are talking to.

Test mode is read-only on production. Write endpoints reject test-mode keys with 403 test_mode_forbidden; run writes against the sandbox deployment instead. Test-mode reads return only isTest records rather than live data — that isolation is enforced at the query layer, not by filtering after the fact.

Versions are dated calendar quarters in the path, as in https://api.slabdynasty.com/2026-07/…. The scheme is Shopify's and Stripe's, and it is chosen over a /v1/-style counter for one reason: a date tells you how old a version is and therefore how long it has left, where a number tells you neither.

Pin a version explicitly in every request. There is no “latest” alias and there will not be one — an unpinned integration is one that changes behaviour on a date nobody chose. An unrecognised version returns 404 with the supported list, each entry carrying its status and sunset date, in the body.

Every version is supported for at least 12 months from its release. Once a version is deprecated its responses carry Sunset (RFC 8594) and Deprecation (RFC 9745) headers, plus a Link; rel="sunset" pointing at that version's docs — so you find out from your own logs rather than from an outage.

2026-07CurrentReleased July 1, 2026 · supported through at least July 1, 2027First public release — authentication, the read path and cursor paging.

You are reading 2026-07 The version to pin for new integrations. Fully supported and will not change incompatibly. See what changed in each release.

A leaky bucket per credential: 40 requests of burst capacity refilling at 2 per second in live mode, and 20 / 1 in test mode. Every response carries X-SlabDynasty-Api-Call-Limit: used/capacity in the same shape Shopify uses, so existing backoff logic ports directly.

Exceeding it returns 429 with a Retry-After header. Honour it rather than retrying immediately — the bucket is charged before the scope check, so a hot retry loop keeps itself throttled.

A key carries an explicit scope list chosen when it is created, and each endpoint names the scope it requires. A write_ scope implies its read_ counterpart, matching Shopify's semantics. Calls missing the required scope return 403 insufficient_scope.

read_listings
View listings.See your listings, prices and status.
write_listings
Manage listings.Create, edit, end and relist. Can reprice or end every listing you have.
read_inventory
View inventory.See your cards and their availability.
write_inventory
Manage inventory.Mark cards available or unavailable. Can deactivate every listing you have.
read_orders
View orders.See buyer orders, including partially redacted shipping details.
write_orders
Manage orders.Cancel orders and request buyer cancellations.
read_fulfillments
View shipments.See shipping status and tracking for your orders.
write_fulfillments
Manage shipments.Mark orders shipped to our hub, attach tracking and buy labels.
read_catalog
Look up cards.Look up PSA certs, card metadata and price recommendations.
read_seller
View seller status.See your seller tier, limits, fees and payout status.

Error envelopes match Shopify's two shapes — a bare string for auth and not-found failures, a field map for validation failures — plus an additive slab_error object carrying a stable machine-readable code and the request id. Branch on the code, never on the message.

422 — validation

{
  "errors": { "price": ["must be greater than 0"] },
  "slab_error": {
    "code": "validation_error",
    "request_id": "req_01J8ZC4Q7N2M5V8W1X3Y6Z9B0C"
  }
}

Quote the request_id when contacting support — it identifies the exact request in our logs.

Separately from those envelopes, an individual record can come back in degraded form, carrying only its id and a slab_serialization_error. That is a 200, and it is deliberate: the rest of the response is complete. Rather than fail a whole page because one stored record is malformed — which would also block every later page, since the cursor cannot advance past it — we return the record's id and mark it. Skip those records, keep processing the rest, and send us the id. It is always our defect, never something your request can cause.

Every code the API can emit:

401invalid_api_key
The token is missing, malformed, revoked, expired, or was sent in the query string. Every authentication failure returns this one code — distinguishing them would let a caller probe which keys exist. Send the key in X-SlabDynasty-Access-Token or Authorization: Bearer. If it was working yesterday, check whether it was rotated or revoked in the dashboard.
403insufficient_scope
The key is valid but was not granted the scope this endpoint requires. Edit the key's scopes in the dashboard, or create a key with the required scope. The required scope is listed on each endpoint in the reference.
403test_mode_forbidden
A test-mode key called a write endpoint on production, where test mode is read-only. Use a live key for writes, or run the write against the sandbox deployment, where test keys can write.
403seller_not_approved
The seller account behind the key has not completed approval, so it cannot transact yet. Finish seller onboarding at slabdynasty.com/profile/settings/seller-setup.
403seller_frozen
The seller account behind the key is temporarily frozen. Contact support with the request_id.
403seller_banned
The seller account behind the key is not permitted to use the Partner API. Contact support.
403seller_not_found
The account behind this API key no longer exists. Create a key from an active seller account.
403seller_not_eligible
The seller account behind the key cannot use the Partner API right now. Contact support with the request_id.
403seller_paused
Partner API access for this seller account is temporarily paused by Slab Dynasty. Keys are intact and access resumes automatically when the pause is lifted. Pause and retry with backoff, or contact support with the request_id. Do not rotate or re-create keys — they are not the problem.
404not_found
No such resource. A resource belonging to another seller returns the same 404 as one that does not exist. Check the id. Ids come from list endpoints and are opaque strings.
404unknown_api_version
The version segment in the path is not a supported API version. The response body lists every supported version with its status and sunset date. Pin a version from slab_error.supported_versions.
400invalid_page_info
The page_info cursor is malformed or was not issued by this API. Send the cursor exactly as it appeared in the previous response's Link header. Never construct or edit one.
400page_info_sort_mismatch
The page_info cursor was issued under a different sort order. Restart the walk from page one after changing the sort.
400page_info_resource_mismatch
The page_info cursor was issued by a different endpoint. Cursors are only valid on the endpoint that issued them. Walk each collection with its own cursors from its own Link headers.
400page_info_filter_mismatch
A filter changed mid-walk. Cursors embed a hash of the filter set, so changing a filter between pages is rejected rather than silently skipping rows. slab_error.changed_params names the offending parameters. Restart the walk from page one after changing any filter.
422validation_error
One or more field values are invalid. The errors object is keyed by field, matching Shopify's 422 shape. Fix the named fields and retry.
422invalid_json
The request body is not valid JSON. Send a JSON body and a matching Content-Type.
413body_too_large
The request body exceeds the limit. slab_error.max_bytes carries the exact cap. Split the request. No documented endpoint needs a body near the cap.
403listing_write_forbidden
The seller account cannot perform this listing write right now — paused, frozen, restricted, capped, or the card is blocklisted. The message carries the specific rule. Resolve the named account issue in the dashboard, or contact support with the request_id.
409listing_write_conflict
The write conflicts with current marketplace state: the card is already actively listed, reserved by a pending order, or the listing has sold. Re-read the resource (inventory_levels.json shows why a card is unavailable) and retry against current state.
422card_not_listable
The card itself cannot be listed — sold, archived, shipped, in a trade, or awaiting submission review. slab_error.reason carries the same reason code inventory_levels.json publishes. Check the card's inventory_level for the reason. Most states resolve on their own; archived cards can be unarchived in the dashboard.
422cert_not_found
No grader recognizes this cert number (with grader omitted, PSA, BGS, SGC and CGC were all tried). The card was not created and nothing was listed. Retry with card details plus grader and images to list it from your own details, pending admin review. Check the cert number, and send grader if you know it. Newly graded slabs can take time to appear in the grader's database; you can also add the card with full details in the dashboard.
503cert_lookup_unavailable
The grader's lookup service is temporarily at capacity or unreachable, so the cert could not be verified. Nothing was created. Retry later, or resend with card details plus grader and images to list from your own details, pending admin review. Honor Retry-After and retry the same request (reuse the Idempotency-Key).
409idempotency_key_reuse
The Idempotency-Key was already used with a DIFFERENT request payload. Keys pin one request's outcome; reusing one for different work is a client bug. Generate a fresh key per logical request. Reuse a key only to retry the identical request.
409idempotency_request_in_flight
A request with this Idempotency-Key is still executing. The retry arrived before the first attempt finished. Wait Retry-After seconds and retry with the SAME key to receive the stored outcome.
429rate_limited
The leaky bucket for this credential is full. The bucket is charged before anything else, so hot retry loops keep themselves throttled. Wait Retry-After seconds. Watch X-SlabDynasty-Api-Call-Limit and slow down as used/capacity approaches 1.
503partner_api_writes_disabled
Write endpoints are temporarily disabled while Slab Dynasty resolves an incident. Read endpoints are unaffected. Honor Retry-After and retry the write later. Keep reads flowing normally.
503partner_api_disabled
The Partner API is temporarily unavailable while Slab Dynasty resolves an incident. Nothing is wrong with your key or your request. Honor Retry-After and retry with backoff. Follow status updates from support.
500internal_error
Something failed on our side. The response never carries internal detail — the request_id is the support handle. Retry with backoff. If it persists, contact support and quote slab_error.request_id.

Instead of polling, register an HTTPS endpoint under Profile → Developers → Webhooks and we'll POST you a signed JSON snapshot when something you subscribe to happens. The payload is the same resource the corresponding GET returns — one deserializer covers both — sent bare, with the topic in a header.

Topics

products/create
A listing went up — created through the API, the dashboard, or an import. Payload is the product resource as GET /products/{id}.json returns it.
products/update
A listing changed: price, title, description, offer settings, reactivation, or a status change you didn't make yourself (expiry, admin review). Also fires when a listing sells — status becomes archived with slab_unavailable_reason: "sold".
products/delete
A listing was taken off the marketplace (soft deactivation — the same thing DELETE /products/{id}.json does). The payload still serializes the full resource so you can see its final state.
orders/create
A buyer bought one of your cards — marketplace checkout, accepted offer, or any other sale path. Payload is the order resource as GET /orders/{id}.json returns it.
orders/updated
An order's lifecycle moved: shipped to the hub, authenticated, shipped to the buyer, delivered, refunded, payout released. Fires on every status transition, so consume it idempotently.
orders/cancelled
An order was cancelled or failed — buyer cancellation, admin cancellation, or payment failure. cancelled_at is set and financial_status reflects the refund state.

Request headers

X-SlabDynasty-Webhook-Signature
t=<unix-seconds>,v1=<hex> — HMAC-SHA256 of "t.rawBody" with your endpoint's signing secret. Verify EVERY request.
X-SlabDynasty-Webhook-Topic
The topic, e.g. orders/create.
X-SlabDynasty-Webhook-Id
The event id (evt_…). Stable across retries — deduplicate on it, because a slow 2xx can race a retry.
X-SlabDynasty-Webhook-Attempt
1-based attempt counter, up to 8.
X-SlabDynasty-Api-Version
The API version whose serializers shaped the payload.
X-SlabDynasty-Api-Mode
live or test, matching the endpoint's environment.
X-SlabDynasty-Webhook-Test
Present ("true") only on deliveries fired from the dashboard's Send test button.

Answer with any 2xx within 10 seconds — ack first, process async. Anything else (including a timeout) is retried with backoff, up to 8 attempts over roughly a day; each retry is re-signed with a fresh timestamp. An endpoint that does nothing but fail is automatically disabled, and one click in the dashboard re-enables it. Every delivery, every attempt and every response code is visible in the dashboard's webhook log, which can also redeliver any event on demand.

The signing secret (slab_whsec_…) is per endpoint, revealed in the dashboard whenever you need it, and rotatable in one click — rotation takes effect on the next delivery. Verification is ~15 lines:

Verify (Node / Express)

import crypto from "node:crypto";
import express from "express";

const app = express();

function verifySlabDynastyWebhook(rawBody, signatureHeader, secret) {
  // Header shape: "t=<unix-seconds>,v1=<hex>"
  const params = new Map(
    signatureHeader.split(",").map((part) => {
      const i = part.indexOf("=");
      return [part.slice(0, i).trim(), part.slice(i + 1).trim()];
    })
  );
  const t = params.get("t");
  const v1 = params.get("v1");
  if (!t || !v1) return false;

  // Replay guard: reject stamps older than an hour. Retries are re-signed
  // at send time, so a legitimate delivery is never near this boundary.
  if (Math.abs(Date.now() / 1000 - Number(t)) > 60 * 60) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  return (
    v1.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
  );
}

app.post(
  "/webhooks/slabdynasty",
  // The RAW body, before any JSON parsing — the signature covers the exact
  // bytes we sent, and a re-serialized body will not match.
  express.raw({ type: "application/json" }),
  (req, res) => {
    const ok = verifySlabDynastyWebhook(
      req.body.toString("utf8"),
      req.get("X-SlabDynasty-Webhook-Signature") ?? "",
      process.env.SLAB_WEBHOOK_SECRET // from the dashboard: slab_whsec_...
    );
    if (!ok) return res.status(401).end();

    // Ack fast (any 2xx within 10s), then process asynchronously.
    res.status(200).end();

    const topic = req.get("X-SlabDynasty-Webhook-Topic");     // e.g. "orders/create"
    const eventId = req.get("X-SlabDynasty-Webhook-Id"); // dedupe on this — retries reuse it
    const resource = JSON.parse(req.body.toString("utf8"));
    // handle(topic, eventId, resource)
  }
);
GET/2026-07/pingno scope required▸ Try it

Verify a credential end to end

Returns the identity behind the presented key without requiring any scope. This is the first call to make when wiring up an integration: a 200 proves the token, the version segment, the rate limiter and your seller's eligibility all line up. A 401 means the token is wrong or revoked; a 403 means the seller cannot currently transact.

Request

curl https://api.slabdynasty.com/2026-07/ping \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY"

Response

{
  "ping": {
    "ok": true,
    "api_version": "2026-07",
    "mode": "live",
    "seller_id": "68d0000000000000000000aa",
    "key_id": "ABCDEFGH12345678",
    "scopes": ["read_listings"],
    "request_id": "req_01J8ZC4Q7N2M5V8W1X3Y6Z9B0C",
    "server_time": "2026-08-02T12:00:00.000Z"
  }
}

List your listings

Your marketplace listings, oldest first. A listing is A CARD PLUS SALE TERMS, and the response is shaped that way: the sale terms (price, status, offers, window) sit on the listing, and the card being sold is nested under card — same field names as the write body's card object, with card.id being the card's own id (the /inventory_items.json record) and card.url its page on the site. An ascending walk appends newly created listings at the tail, so a long-running sync never has rows shuffle underneath its cursor. One variant per listing — even for a lot, where only the whole-lot price is a truthful number. Paging is cursor-based and lives entirely in the Link header: follow rel="next" until it stops appearing. There is no offset paging, because over thousands of listings it drifts as rows sell or expire mid-walk. Shopify-shaped clients can use /products.json instead — a permanent alias serving the identical resource under the {"products": […]} envelope.

Query parameters

limit
integerRows per page, 1–250. Defaults to 50.
fields
stringComma-separated top-level keys to return, as in `id,title,status`. Omit for the full resource.
updated_at_min
stringISO 8601 date or datetime. Only rows updated at or after this moment are returned — the incremental-sync filter. Walk the full catalog once, then poll with the timestamp of your last successful sync instead of re-walking everything.
page_info
stringOpaque cursor from the previous response's Link header. Send it verbatim and alone — changing a filter mid-walk is rejected with 400 page_info_filter_mismatch.

Request

curl "https://api.slabdynasty.com/2026-07/listings.json?limit=10" \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY"

Response

{
  "listings": [
    {
      "id": "68d0000000000000000000ff",
      "title": "2018 Panini Prizm Luka Doncic #280 PSA 10",
      "status": "active",
      "price": "1250.00",
      "card": {
        "id": "68d0000000000000000000cd",
        "title": "2018 PANINI PRIZM LUKA DONCIC",
        "cert_number": "82736451",
        "grader": "PSA",
        "grade": "10",
        "year": 2018,
        "set": "Panini Prizm",
        "category": "basketball"
      },
      "is_lot": false,
      "card_count": 1,
      "accepts_offers": true,
      "created_at": "2026-07-30T18:04:11.000Z",
      "listed_at": "2026-07-30T18:04:11.000Z"
    }
  ]
}

Fetch one listing

The same listing resource as the collection endpoint, for a single listing. The trailing .json is optional. A listing belonging to another seller returns the same 404 as one that does not exist — distinguishing them would make this route an oracle for which listing ids exist platform-wide. The response's url field is the listing's page on the site — open or share that rather than reconstructing a path from handle. Also served at /products/{id}.json under the {"product": …} envelope.

Path parameters

id*
stringThe listing id, as returned in a listing's `id` field.

Query parameters

fields
stringComma-separated top-level keys to return. Omit for the full resource.

Request

curl https://api.slabdynasty.com/2026-07/listings/68d0000000000000000000ff.json \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY"

Response

{
  "listing": {
    "id": "68d0000000000000000000ff",
    "title": "2018 Panini Prizm Luka Doncic #280 PSA 10",
    "status": "active",
    "price": "1250.00",
    "card": {
      "id": "68d0000000000000000000cd",
      "title": "2018 PANINI PRIZM LUKA DONCIC",
      "cert_number": "82736451",
      "grader": "PSA",
      "grade": "10",
      "url": "https://slabdynasty.com/card/basketball-2018-luka-doncic-psa-10-82736451-68d0000000000000000000cd"
    },
    "unavailable_reason": null,
    "can_reactivate": false,
    "url": "https://slabdynasty.com/marketplace/listing/2018-panini-prizm-luka-doncic-280-psa-10-68d0000000000000000000ff"
  }
}

List a card for sale

Creates a marketplace listing for one of your cards, with the same options as the seller's listing form in the app. Name the card ONE of two ways: `inventory_item_id` for a card already in your inventory, or `sku` with a grading cert number — we look the cert up with the grader (send grader: PSA, BGS, SGC or CGC when you know it; omit it and we try all four in that order), create the card in your inventory populated with the grader's own data (title, player, year, set, grade, and for PSA the official slab scans), and list it. If the grader cannot resolve the cert (cert_not_found, or the lookup service is down), send card with your own details plus at least one photo in images — the card is created from them and the listing goes to admin review instead of straight live; grader is required in that case. Cert + price is a complete request; everything else is optional and named after the app's form: title and description, images (up to 5 photo URLs we rehost — the app's listing photos), duration_hours (168, 336, 504 or 720; default 720), accepts_offers (default true) with minimum_offer as the dollar floor, accepts_trades, auto_relist (renew at expiry instead of ending), promoted (the app's Promote Listing: featured placement for an extra 3% seller fee when it sells; one-way, like the app), and scheduled_go_live_at (ISO 8601 — a future instant creates the listing paused until then, with the window counted from go-live). eBay cross-listing and multi-card lots stay dashboard-only. PSA and BGS data is grader-verified and the listing goes live immediately; SGC and CGC data is parsed from a description, so those listings are created pending review. Every dashboard rule — seller standing, card availability, counterfeit blocklist, pending-order reservations, duplicate-listing conflicts, tier caps — applies identically here. The original Shopify-flavored spellings are still accepted as aliases — body_html, the slab_-prefixed field names, the variants[0] wrapper, the {"product": …} envelope, and the /products.json path all keep working. Send an Idempotency-Key so a retried create replays the original outcome. Requires the write_listings scope on a live key, which needs write approval.

Request

curl -X POST https://api.slabdynasty.com/2026-07/listings.json \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 4d2c1f4e-8a4b-4c1d-9e21-create-luka" \
  -d '{"listing": {"title": "2018 Panini Prizm Luka Doncic #280 PSA 10", "description": "Pack-fresh, centered, ships in a one-touch.", "grader": "PSA", "duration_hours": 720, "accepts_offers": true, "minimum_offer": "1000.00", "accepts_trades": false, "auto_relist": true, "scheduled_go_live_at": "2026-09-01T16:00:00Z", "sku": "82736451", "price": "1250.00"}}'

Response

{
  "listing": {
    "id": "68d0000000000000000000ff",
    "title": "2018 Panini Prizm Luka Doncic #280 PSA 10",
    "description": "Pack-fresh, centered, ships in a one-touch.",
    "status": "active",
    "price": "1250.00",
    "card": {
      "id": "68d0000000000000000000cd",
      "cert_number": "82736451",
      "grader": "PSA"
    },
    "url": "https://slabdynasty.com/marketplace/listing/2018-panini-prizm-luka-doncic-280-psa-10-68d0000000000000000000ff"
  }
}

Edit a listing

Updates price, title, description, status, the listing window (duration_hours), a pending schedule (scheduled_go_live_at), or the offer/trade/relist/promotion settings (accepts_offers, minimum_offer, accepts_trades, auto_relist, promoted). Send only the fields you want to change — omitted fields keep their current values. Status uses Shopify's vocabulary: "draft" takes the listing off the marketplace (soft and reversible), "active" relists it — re-running the full reactivation rules with a fresh listing window. A listing that has not gone live yet can be rescheduled with a new scheduled_go_live_at, or released immediately with scheduled_go_live_at: null. Content edits to a listing backed by a user-submitted card re-enter review exactly like a dashboard edit; the listing stays live while reviewed. A sold listing can no longer be edited (409). The original Shopify-flavored spellings are still accepted as aliases — body_html, the slab_-prefixed field names, the variants[0] wrapper, the {"product": …} envelope, and the /products/{id}.json path all keep working.

Path parameters

id*
stringThe listing id, as returned in a listing's `id` field.

Request

curl -X PUT https://api.slabdynasty.com/2026-07/listings/68d0000000000000000000ff.json \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"listing": {"title": "2018 Panini Prizm Luka Doncic #280 PSA 10 — PRICE DROP", "accepts_offers": true, "minimum_offer": "950.00", "price": "1195.00"}}'

Response

{
  "listing": {
    "id": "68d0000000000000000000ff",
    "title": "2018 Panini Prizm Luka Doncic #280 PSA 10",
    "status": "active",
    "price": "1195.00",
    "minimum_offer": "950.00",
    "accepts_offers": true
  }
}

Take a listing off the marketplace

A SOFT deactivation, deliberately: the listing reads deactivated_by_seller with can_reactivate true, and PUT {"listing": {"status": "active"}} puts it back with a fresh window. Nothing is destroyed and your card stays in your inventory. Idempotent — deleting an already-inactive listing succeeds. Works on a pending-approval listing too (it is withdrawn). Responds with the listing as it now stands so you can confirm the change — status archived, unavailable_reason deactivated_by_seller.

Path parameters

id*
stringThe listing id, as returned in a listing's `id` field.

Request

curl -X DELETE https://api.slabdynasty.com/2026-07/listings/68d0000000000000000000ff.json \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY"

Response

{
  "listing": {
    "id": "68d0000000000000000000ff",
    "title": "2018 Panini Prizm Luka Doncic #280 PSA 10",
    "status": "archived",
    "unavailable_reason": "deactivated_by_seller",
    "can_reactivate": true,
    "price": "1250.00"
  }
}

List your sales as orders

Every sale, in Shopify's order shape, oldest first. This is where a sold listing's story lives: listings.json keeps the listing itself (status archived, slab_unavailable_reason sold), while the order carries the SD-XXXXXX code, what the buyer paid, and how far the sale has progressed — financial_status moves authorized → paid → (partially_)refunded or voided, fulfillment_status flips to fulfilled when the card ships to the buyer, and slab_seller_shipment is YOUR leg: ship_by, tracking, whether our hub has received it and slab_authentication its verification result. One line item per order; a lot is one line priced as a whole, with every member card in slab_inventory_item_ids. Nothing about the buyer is included — you ship to our hub, never to them. Same Link-header cursor walk and updated_at_min delta sync as every other collection.

Query parameters

limit
integerRows per page, 1–250. Defaults to 50.
fields
stringComma-separated top-level keys to return, as in `id,title,status`. Omit for the full resource.
updated_at_min
stringISO 8601 date or datetime. Only rows updated at or after this moment are returned — the incremental-sync filter. Walk the full catalog once, then poll with the timestamp of your last successful sync instead of re-walking everything.
page_info
stringOpaque cursor from the previous response's Link header. Send it verbatim and alone — changing a filter mid-walk is rejected with 400 page_info_filter_mismatch.
status
string`open`, `closed`, `cancelled` or `any`. Defaults to `any` — unlike Shopify, whose default of `open` would hide every completed sale from an endpoint that exists to show them. `open` is sold and in progress; `closed` is paid out or delivered; `cancelled` covers seller, buyer and timeout cancellations.

Request

curl "https://api.slabdynasty.com/2026-07/orders.json?limit=10" \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY"

Response

{
  "orders": [
    {
      "id": "68d00000000000000000ab01",
      "name": "SD-7K3M9Q",
      "order_number": 158892939,
      "created_at": "2026-08-01T09:15:03.000Z",
      "closed_at": null,
      "cancelled_at": null,
      "financial_status": "authorized",
      "fulfillment_status": null,
      "currency": "USD",
      "subtotal_price": "1250.00",
      "total_tax": "0.00",
      "total_price": "1262.50",
      "total_refunded": "0.00",
      "test": false,
      "line_items": [
        {
          "id": "li_68d00000000000000000ab01",
          "product_id": "68d0000000000000000000ff",
          "title": "2018 Panini Prizm Luka Doncic #280 PSA 10",
          "sku": "82736451",
          "quantity": 1,
          "price": "1250.00",
          "inventory_item_id": "68d0000000000000000000cd",
          "slab_inventory_item_ids": ["68d0000000000000000000cd"],
          "slab_is_lot": false
        }
      ],
      "slab_order_code": "SD-7K3M9Q",
      "slab_sold_at": "2026-08-01T09:15:03.000Z",
      "slab_sale_channel": "marketplace",
      "slab_delivery_method": "ship",
      "slab_seller_shipment": {
        "status": "pending_shipment",
        "tracking_number": null,
        "ship_by": "2026-08-04T09:15:03.000Z",
        "cancel_after": "2026-08-06T09:15:03.000Z",
        "label_url": null
      },
      "slab_authentication": {
        "status": "pending",
        "completed_at": null
      },
      "slab_payout": {
        "status": "pending",
        "amount": null,
        "paid_at": null
      }
    }
  ]
}

Fetch one order

One sale, by order id or by its SD-XXXXXX code — the code is what is printed on the packing slip, so it is accepted directly rather than forcing a list-and-search. An order that belongs to another seller returns the same 404 as one that does not exist.

Path parameters

id*
stringThe order id as returned in an order's `id` field, or its `SD-XXXXXX` order code (the `name` field).

Query parameters

fields
stringComma-separated top-level keys to return. Omit for the full resource.

Request

curl https://api.slabdynasty.com/2026-07/orders/SD-7K3M9Q.json \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY"

Response

{
  "order": {
    "id": "68d00000000000000000ab01",
    "name": "SD-7K3M9Q",
    "order_number": 158892939,
    "created_at": "2026-08-01T09:15:03.000Z",
    "closed_at": "2026-08-09T16:40:00.000Z",
    "cancelled_at": null,
    "financial_status": "paid",
    "fulfillment_status": "fulfilled",
    "currency": "USD",
    "subtotal_price": "1250.00",
    "total_price": "1262.50",
    "total_refunded": "0.00",
    "test": false,
    "line_items": [
      {
        "id": "li_68d00000000000000000ab01",
        "product_id": "68d0000000000000000000ff",
        "title": "2018 Panini Prizm Luka Doncic #280 PSA 10",
        "sku": "82736451",
        "quantity": 1,
        "price": "1250.00",
        "fulfillment_status": "fulfilled"
      }
    ],
    "slab_order_code": "SD-7K3M9Q",
    "slab_seller_shipment": {
      "status": "delivered",
      "tracking_number": "9400111899223197428490",
      "carrier_code": "usps",
      "shipped_at": "2026-08-02T14:02:11.000Z",
      "delivered_to_hub_at": "2026-08-05T18:30:00.000Z"
    },
    "slab_authentication": {
      "status": "passed",
      "completed_at": "2026-08-06T10:12:45.000Z"
    },
    "slab_buyer_delivery": {
      "status": "delivered",
      "shipped_at": "2026-08-06T20:00:00.000Z",
      "delivered_at": "2026-08-09T16:40:00.000Z"
    },
    "slab_payout": {
      "status": "paid",
      "amount": "1137.50",
      "platform_fee": "112.50",
      "paid_at": "2026-08-09T16:40:00.000Z"
    }
  }
}

List your cards as inventory items

The card is the counted unit here, and its grading cert is the sku — so an existing inventory reconciler can point at this endpoint essentially unchanged. Every item is tracked and requires shipping. A card appears whether or not it is currently listed; ask inventory_levels.json whether it is actually sellable.

Query parameters

limit
integerRows per page, 1–250. Defaults to 50.
fields
stringComma-separated top-level keys to return, as in `id,title,status`. Omit for the full resource.
updated_at_min
stringISO 8601 date or datetime. Only rows updated at or after this moment are returned — the incremental-sync filter. Walk the full catalog once, then poll with the timestamp of your last successful sync instead of re-walking everything.
page_info
stringOpaque cursor from the previous response's Link header. Send it verbatim and alone — changing a filter mid-walk is rejected with 400 page_info_filter_mismatch.

Request

curl "https://api.slabdynasty.com/2026-07/inventory_items.json?limit=10" \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY"

Response

{
  "inventory_items": [
    {
      "id": "68d0000000000000000000cd",
      "sku": "82736451",
      "cost": "900.00",
      "tracked": true,
      "requires_shipping": true,
      "created_at": "2026-06-02T15:22:40.000Z",
      "slab_cert_number": "82736451",
      "slab_grader": "PSA",
      "slab_grade": "10",
      "slab_player": "Luka Doncic",
      "slab_year": "2018",
      "slab_card_set": "Panini Prizm"
    }
  ]
}

Per-card availability, with the reason

The endpoint a channel reconciler polls. `available` is 0 or 1, and because a zero is lossy — sold, expired, seller-deactivated, flagged, shipped and pulled-into-a-pack all look identical — it always travels with slab_unavailable_reason, slab_reason_detail and slab_can_reactivate. Without those you cannot tell "gone forever" from "relist it". Lot members are resolved even when the sibling card falls on another page.

Query parameters

limit
integerRows per page, 1–250. Defaults to 50.
fields
stringComma-separated top-level keys to return, as in `id,title,status`. Omit for the full resource.
updated_at_min
stringISO 8601 date or datetime. Only rows updated at or after this moment are returned — the incremental-sync filter. Walk the full catalog once, then poll with the timestamp of your last successful sync instead of re-walking everything.
page_info
stringOpaque cursor from the previous response's Link header. Send it verbatim and alone — changing a filter mid-walk is rejected with 400 page_info_filter_mismatch.

Request

curl "https://api.slabdynasty.com/2026-07/inventory_levels.json?limit=10" \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY"

Response

{
  "inventory_levels": [
    {
      "inventory_item_id": "68d0000000000000000000cd",
      "location_id": "loc_vault",
      "available": 0,
      "updated_at": "2026-08-01T09:15:03.000Z",
      "slab_unavailable_reason": "sold",
      "slab_reason_detail": "Sold on 2026-08-01.",
      "slab_can_reactivate": false
    }
  ]
}

The two places a card can be

Reads no database: locations are a pure function of who is asking. A card is either in your hands or at our hub, so unlike Shopify — where locations are merchant-created — neither is writable. connect, set, adjust and DELETE all return 422, and every location says so in slab_writable rather than leaving you to discover it by trying. No addresses are published for either.

Request

curl https://api.slabdynasty.com/2026-07/locations.json \
  -H "X-SlabDynasty-Access-Token: $SLAB_API_KEY"

Response

{
  "locations": [
    {
      "id": "loc_seller_68d0000000000000000000aa",
      "name": "Seller Inventory",
      "active": true,
      "slab_location_type": "seller",
      "slab_seller_controlled": true,
      "slab_writable": false
    },
    {
      "id": "loc_vault",
      "name": "Slab Dynasty Vault",
      "country_code": "US",
      "active": true,
      "slab_location_type": "vault",
      "slab_seller_controlled": false,
      "slab_writable": false
    }
  ]
}