API Reference

/

GET /v1/splats

GET /api/v1/splats

List Integration Splats attached to the authenticated Embed Integration. Studio-owned splats are not returned by this endpoint.

GET

https://moshpit.studio/api/v1/splats

Auth

Both headers are required:

Authorization: Bearer msk_YOUR_SECRET_KEY
X-Moshpit-Public-Key: mpk_YOUR_PUBLIC_KEY

This endpoint authenticates with both keys at once. The pairing proves the caller has access to the integration as a whole, not just a session for one embed.

REST API access

REST API access is included on Free, Pro, and Enterprise. Storage, Scene counts, and integration limits still apply.

Without an external-user header, the endpoint returns only public Integration Splats for the integration.

To list a delegated host user's library, include:

X-Moshpit-External-User-Id: YOUR_HOST_USER_ID

With that header:

  • Public Integration Splats remain visible.
  • Private splats owned by that external user are included.
  • isOwner: true marks items owned by that external user.

Use isOwner to decide whether your app should expose edit/delete actions.

Query parameters

All pagination metadata is returned in the JSON body. Moshpit does not emit HTTP Link headers.

FieldTypeDescription
cursor
string

Opaque cursor returned as `pagination.nextCursor`. Use with `limit` for Load more and infinite-scroll flows.

limit
number
default 20

Page size for cursor and offset modes. Minimum 1, maximum 100.

page
number

1-based page number. Use with `perPage`; cannot be mixed with `cursor`, `offset`, or `limit`.

perPage
number
default 20

Page size for numbered pagination. Minimum 1, maximum 100.

offset
number

0-based row offset. Use with `limit`; cannot be mixed with cursor or page pagination.

includeTotal
boolean

Cursor mode only. If `true`, include `total` and `totalPages` in `pagination`.

sort
"createdAt" | "updatedAt" | "featuredAt"
default "createdAt"

Stable sort field. `_id` is used as the tie-breaker. `featuredAt` requires `featured=true` (non-featured splats have no featured timestamp to sort by).

dir
"asc" | "desc"
default "desc"

Sort direction.

embeddable
boolean

If `true`, return only splats with `allowEmbed: true`. If `false`, return only splats where embedding is disabled. Omit to return both.

visibility
"public" | "private"

Filter by visibility. `visibility=public` is recommended for public galleries that still pass an external-user header for ownership flags.

owned
boolean

If `true`, return only splats owned by the supplied `X-Moshpit-External-User-Id`. Requires that header.

featured
boolean

If `true`, return only splats the integration has marked featured (see PATCH `featured`). If `false`, return only non-featured splats. Omit to return both. Combine with `sort=featuredAt&dir=desc` for a newest-curation-first rail.

Cursor example

Use cursor mode for galleries, Load more buttons, and on-scroll loading.

SH

Bash

curl "https://moshpit.studio/api/v1/splats?visibility=public&embeddable=true&limit=20" \
  -H "Authorization: Bearer msk_YOUR_SECRET_KEY" \
  -H "X-Moshpit-Public-Key: mpk_YOUR_PUBLIC_KEY" \
  -H "X-Moshpit-External-User-Id: host_user_123"
{}

JSON

{
  "status": "success",
  "splats": [
    {
      "id": "65f1a2b3c4d5e6f7a8b9c0d1",
      "title": "Living room scan",
      "description": "",
      "imageUrl": "https://...",
      "thumbnailDepthUrl": "https://...",
      "splatUrl": "https://...",
      "visibility": "public",
      "allowEmbed": true,
      "allowComments": true,
      "allowReactions": true,
      "allowClone": true,
      "remixedFrom": null,
      "splatType": "lod",
      "externalUserId": "host_user_123",
      "productSlug": "app.example.com",
      "isOwner": true,
      "viewCount": 42,
      "likeCount": 3,
      "fileSizeBytes": 17825792,
      "featured": false,
      "featuredAt": null,
      "createdAt": "2026-04-12T18:30:00.000Z",
      "updatedAt": "2026-04-15T09:14:21.000Z"
    }
  ],
  "pagination": {
    "mode": "cursor",
    "count": 1,
    "limit": 20,
    "hasMore": true,
    "nextCursor": "eyJ2IjoxLCJzb3J0IjoiY3JlYXRlZEF0IiwiZGlyIjoiZGVzYyIsInZhbHVlIjoiMjAyNi0wNC0xMlQxODozMDowMC4wMDBaIiwiaWQiOiI2NWYxYTJiM2M0ZDVlNmY3YThiOWMwZDEifQ",
    "sort": "createdAt",
    "dir": "desc"
  }
}

Fetch the next page by sending cursor=pagination.nextCursor.

Numbered page example

Use page mode for admin tables where users expect page numbers and total counts.

SH

Bash

curl "https://moshpit.studio/api/v1/splats?owned=true&page=2&perPage=25&sort=updatedAt&dir=desc" \
  -H "Authorization: Bearer msk_YOUR_SECRET_KEY" \
  -H "X-Moshpit-Public-Key: mpk_YOUR_PUBLIC_KEY" \
  -H "X-Moshpit-External-User-Id: host_user_123"
{}

JSON

{
  "status": "success",
  "splats": [],
  "pagination": {
    "mode": "page",
    "count": 0,
    "page": 2,
    "perPage": 25,
    "total": 25,
    "totalPages": 1,
    "hasMore": false,
    "sort": "updatedAt",
    "dir": "desc"
  }
}

Offset example

Use offset mode only when a table or data pipeline already works with absolute row offsets.

SH

Bash

curl "https://moshpit.studio/api/v1/splats?offset=40&limit=20" \
  -H "Authorization: Bearer msk_YOUR_SECRET_KEY" \
  -H "X-Moshpit-Public-Key: mpk_YOUR_PUBLIC_KEY" \
  -H "X-Moshpit-External-User-Id: host_user_123"
{}

JSON

{
  "status": "success",
  "splats": [],
  "pagination": {
    "mode": "offset",
    "count": 0,
    "offset": 40,
    "limit": 20,
    "total": 40,
    "totalPages": 2,
    "hasMore": false,
    "nextOffset": null,
    "sort": "createdAt",
    "dir": "desc"
  }
}

Response fields

FieldTypeDescription
id
string

MongoDB ObjectId. Use this anywhere we ask for `splatId`.

title
string

User-facing scene title.

description
string

User-facing description; may be empty.

imageUrl
string

Thumbnail URL.

thumbnailDepthUrl
string

Optional depth thumbnail URL.

splatUrl
string

Direct URL to the splat asset or LOD manifest.

visibility
"public" | "private"

`public` items are visible to all integration callers. `private` items are returned only to their matching external user.

allowEmbed
boolean

Whether the splat may be loaded in viewer embeds.

allowClone
boolean

`true` when the owner has turned on "Allow remixing" for this scene (default `false`). Controls whether other external users can Remix it via `POST /v1/splats/{splatId}/clone`.

remixedFrom
{ splatId: string; ownerName: string; clonedAt: string } | null

Present only when this scene was itself cloned from a **different** owner (a Remix). `null` for an original scene and for a same-owner Duplicate. Never discloses whether two owners are the same beyond that.

splatType
"file" | "lod"

`file` for single-asset splats; `lod` for level-of-detail folder uploads.

externalUserId
string | null

The delegated host user owner for this Integration Splat.

isOwner
boolean

`true` when the splat belongs to the supplied `X-Moshpit-External-User-Id`.

fileSizeBytes
number

Authoritative post-compression byte count in storage.

featured
boolean

`true` when the integration has marked this splat featured via PATCH.

featuredAt
string | null

ISO timestamp of when the splat was featured; `null` when not featured. Stable across repeat feature calls.

Error responses

StatusCause
400Invalid filter, sort, pagination value, cursor, missing external user for owned=true, or mixed pagination styles
401Missing or invalid Bearer / public-key combination
403Plan does not include REST API access

Mixed pagination styles return 400. For example, page=1&limit=20 is invalid because limit belongs to cursor and offset modes, while numbered pages use perPage.

Discovery to embed pattern

TS

TypeScript

const externalUserId = await getCurrentHostUserId(request); // optional
 
const list = await fetch(
  'https://moshpit.studio/api/v1/splats?visibility=public&embeddable=true&limit=20',
  {
    headers: {
      Authorization: `Bearer ${process.env.MOSHPIT_SECRET_KEY}`,
      'X-Moshpit-Public-Key': process.env.MOSHPIT_PUBLIC_KEY!,
      ...(externalUserId
        ? { 'X-Moshpit-External-User-Id': externalUserId }
        : {}),
    },
  },
).then((r) => r.json());
 
const splatId = list.splats[0].id;
 
const session = await fetch(
  'https://moshpit.studio/api/editor/embed-sessions',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.MOSHPIT_SECRET_KEY}`,
      'Content-Type': 'application/json',
      ...(externalUserId
        ? { 'X-Moshpit-External-User-Id': externalUserId }
        : {}),
    },
    body: JSON.stringify({
      publicKey: process.env.MOSHPIT_PUBLIC_KEY,
      type: 'viewer',
      splatId,
    }),
  },
).then((r) => r.json());

What's next