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_KEYThis 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_IDWith that header:
- Public Integration Splats remain visible.
- Private splats owned by that external user are included.
isOwner: truemarks 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.
| Field | Type | Description |
|---|---|---|
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.
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.
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.
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
| Field | Type | Description |
|---|---|---|
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
| Status | Cause |
|---|---|
| 400 | Invalid filter, sort, pagination value, cursor, missing external user for owned=true, or mixed pagination styles |
| 401 | Missing or invalid Bearer / public-key combination |
| 403 | Plan 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
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
- Pagination Guide — client and backend patterns.
- GET /v1/splats/{splatId} — fetch one splat with
levelData. - PATCH /v1/splats/{splatId} — update title, description, visibility.
- DELETE /v1/splats/{splatId} — permanently delete a splat owned by the external user.
- POST embed-sessions — mint a session for the splat you picked.