API Reference
/
Overview
API Reference Overview
The shared conventions every Moshpit endpoint follows — read this once, then jump to a specific endpoint.
Base URL
Plain
https://moshpit.studioAll endpoints below are relative to this base.
Authentication
Two authentication schemes:
| Scheme | Used by | Headers |
|---|---|---|
| Secret Bearer | POST /api/editor/embed-sessions | Authorization: Bearer msk_... |
| Public + Secret | /api/v1/* backend API calls | Authorization: Bearer msk_... + X-Moshpit-Public-Key: mpk_... |
| Session Bearer | All requests from inside an embed iframe | Authorization: Bearer {sessionToken} (the JWT) |
The session bearer is automatic — the SDK injects it; the iframe carries it as a query param. You only deal with the secret + public scheme when calling the API directly from your own backend.
X-Moshpit-External-User-Id
Many endpoints accept an optional X-Moshpit-External-User-Id header. Its value
is your own identifier for one of your customers (an external user), not a
Moshpit login. When present it scopes the request to that customer — splat
reads/writes are limited to scenes that customer owns, and a minted session is
scoped to them.
The header is required by the whole /api/v1/external-user surface, where it
names the customer to provision, allocate, meter, or delete. See the
External Users API and the
reseller guide.
Plan access
All plans can mint viewer embed sessions with POST /api/editor/embed-sessions; viewer mints are unmetered. Editor Embed sessions and the /api/v1/* REST endpoints are also available on every plan.
Pro accounts can mint watermark-free viewer sessions and call /api/v1/*. Enterprise adds higher included limits and pay-as-you-go overage on storage and Scene usage. REST calls themselves are not metered.
Bearer token always wins inside an iframe
When an iframe hits a Moshpit API and the user happens to have a logged-in Moshpit Studio session, both the session JWT (Bearer) and the studio cookie arrive on the request. The Bearer header is preferred, so the embed always authenticates as the integration owner — never as the visitor.
Session token format
TypeScript
{
typ: 'moshpit_embed_session';
sub: string; // owner ID
identityUserId: string;
integrationId: string;
publicKey: string; // mpk_...
capabilities: Array<'editor' | 'viewer'>;
iat: number;
exp: number; // iat + 900 seconds
}HS256-signed JWT. 15-minute lifetime. You don't need to verify it — Moshpit does that on every request.
Error envelope
All errors share the same JSON shape:
JSON
{
"status": "error",
"message": "Human-readable description",
"issues": ["Optional field-level validation messages"]
}issues is only present on 400-level validation errors.
HTTP status codes
| Status | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad request — JSON parse failure, schema validation, etc. |
| 401 | Unauthorized — missing or invalid credentials |
| 403 | Forbidden — plan, capability, or ownership check failed |
| 404 | Not found — resource doesn't exist or isn't owned by your integration |
| 410 | Gone — integration has been revoked |
| 413 | Payload too large — quota exceeded |
| 500 | Server error |
404 is also returned when a resource exists but doesn't belong to your integration. This is intentional — Moshpit doesn't disclose existence.
CORS
The /api/v1/* endpoints allow cross-origin calls:
Plain
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, X-Moshpit-Public-Key, X-Moshpit-External-User-Id, Content-TypeOPTIONS preflight requests succeed for any origin.
CORS does not protect your secret
CORS controls which browsers may call the API. It does not make the secret
safe to expose. Browsers can still send any header they want from page JS.
Always proxy /api/v1/* calls through your own backend on user-facing sites —
the secret never goes near the browser.
Endpoint catalog
| Method | Path | Use |
|---|---|---|
| POST | /api/editor/embed-sessions | Mint a viewer or editor session token |
| POST | /api/v1/uploads/splat-file | Create a signed upload URL |
| POST | /api/v1/uploads/lod-folder | Create signed LOD-folder upload URLs |
| POST | /api/v1/uploads/splat-images | Create signed thumbnail/depth URLs |
| POST | /api/v1/splats | Commit an uploaded Scene |
| GET | /api/v1/splats | List Integration Splats |
| GET | /api/v1/splats/{splatId} | Fetch one splat with levelData |
| POST | /api/v1/splats/{splatId}/clone | Duplicate or remix a scene |
| PUT | /api/v1/external-user | Provision or update a reseller customer |
| GET | /api/v1/external-user | Fetch one customer's record |
| GET | /api/v1/external-user/usage | Per-customer usage and allocation |
| DELETE | /api/v1/external-user | Soft-delete a customer and their scenes |
Paginated list responses use JSON body metadata. See the Pagination Guide for cursor, numbered page, and offset examples.
Rate limits
There are no per-endpoint request-rate limits documented today. REST calls and viewer session mints are not metered. Treat the API as best-effort and design for retries with exponential backoff on 5xx.