Getting Started
/
External Users
External Users
If you embed Moshpit and resell it to your own customers, external users let you give each customer their own storage and scene allocation, keep their scenes private to them, and bill them however you like — while Moshpit bills your Studio account for the total.
Plan access
The external-user API and all /api/v1/* endpoints are available on Free,
Pro, and Enterprise. Account quotas and per-customer allocations still apply.
Who is an external user?
An external user is your customer, not a Moshpit login. Picture a digital-twin
platform — call it Unreal Twin — that embeds Moshpit and sells it onward. Unreal
Twin is the reseller: it holds one integration (mpk_ / msk_).
Each company that signs up with Unreal Twin is an external user: a tenant
provisioned through that one integration.
- Moshpit never authenticates your customers. You bring your own customer auth.
- You identify each customer with your own string in the
X-Moshpit-External-User-Idheader. Moshpit treats it as opaque. - Each customer gets an isolated library: scenes a customer creates are private to that customer.
How billing works
Moshpit bills your Moshpit account for the aggregate storage and scene usage of all your customers, plus any overage allowed by your plan. Moshpit does not bill your customers — you set their prices and bill them yourself, externally.
Per-customer limitBytes (storage) and maxSplats (scenes) are allocation
knobs you control. Because billing is on your account total, you can
over-allocate: the sum of every customer's allocation may exceed your Enterprise
bundle. The per-customer caps simply decide which customer gets blocked when they
fill what you gave them.
The end-to-end flow
- Provision a customer.
PUT /api/v1/external-userwith your customer id in the header. This upserts the customer record. - Allocate storage and scenes. Set
limitBytes(per-customer storage) andmaxSplats(per-customer scene cap) on that samePUT. - Mint a scoped session. Call
POST /api/editor/embed-sessionswith theX-Moshpit-External-User-Idheader so the viewer/editor session is scoped to that customer. Scenes the customer creates are private to them. - Caps enforce automatically. Uploads past
limitBytesget413; scenes pastmaxSplatsget403. These are hard per-customer caps — they fire even when your Studio account still has room. - Raise a limit when a customer hits it.
PUTagain with a higherlimitBytesormaxSplatsto unblock them. - Read per-customer usage.
GET /api/v1/external-user/usageto drive a usage meter in your own dashboard. - Mirror your billing state. When your customer downgrades, cancels, or is
suspended,
PUTtheir new caps plusbillingStateand optionalkeepSplatIds. Studio locks overflow scenes and owns the retained-archive maintenance.
1. Provision and allocate
A single PUT both creates the customer and sets their allocation. Run it from
your backend — the msk_ secret never goes near a browser.
Bash
curl -X PUT https://moshpit.studio/api/v1/external-user \
-H "Authorization: Bearer msk_YOUR_SECRET_KEY" \
-H "X-Moshpit-Public-Key: mpk_YOUR_PUBLIC_KEY" \
-H "X-Moshpit-External-User-Id: customer_42" \
-H "Content-Type: application/json" \
-d '{
"limitBytes": 5368709120,
"maxSplats": 10,
"displayName": "Acme Robotics"
}'This gives customer_42 a 5 GB storage allocation and a cap of 10 scenes.
Defaults and partial updates
Every field is optional. Omit a field on a later PUT to leave it untouched.
If you omit limitBytes on a brand-new customer, they default to your Moshpit
account storage bundle. Set maxSplats to null for an uncapped customer.
2. Mint a session scoped to the customer
When that customer opens your app, mint a session with their id in the header. The session is scoped to the customer, so scenes they create are private to them, and their uploads count against their allocation.
Bash
curl -X POST https://moshpit.studio/api/editor/embed-sessions \
-H "Authorization: Bearer msk_YOUR_SECRET_KEY" \
-H "X-Moshpit-External-User-Id: customer_42" \
-H "Content-Type: application/json" \
-d '{ "publicKey": "mpk_YOUR_PUBLIC_KEY", "type": "editor" }'In practice you forward the header from your own session endpoint. For example, a Next.js route handler:
TypeScript
// app/api/moshpit/editor-session/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
const body = await request.json().catch(() => ({}));
// Resolve the signed-in customer from YOUR auth, not from the client.
const customerId = await getCurrentCustomerId(request);
const upstream = await fetch(
'https://moshpit.studio/api/editor/embed-sessions',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.MOSHPIT_SECRET_KEY}`,
'X-Moshpit-External-User-Id': customerId,
},
body: JSON.stringify({
publicKey: process.env.MOSHPIT_PUBLIC_KEY,
type: 'editor',
splatId: body.splatId ?? null,
}),
},
);
return NextResponse.json(await upstream.json(), { status: upstream.status });
}Always resolve the customer id on your server
Derive X-Moshpit-External-User-Id from your own authenticated session —
never trust a customer id sent from the browser. Otherwise one customer could
mint a session scoped to another.
3. Per-customer caps in action — a worked example
Say customer_42 has the 5 GB / 10-scene allocation from step 1, and they fill
it up.
They hit the storage cap. Their next upload would cross 5 GB, so the commit is
rejected with 413 — even though your Moshpit account still has plenty of
storage:
JSON
{
"status": "error",
"code": "STORAGE_QUOTA_EXCEEDED",
"usedBytes": 5368709120,
"limitBytes": 5368709120,
"remainingBytes": 0,
"requestedBytes": 268435456
}They hit the scene cap. Creating an 11th scene is rejected with 403:
JSON
{
"status": "error",
"code": "plan_limit",
"dimension": "splatCount",
"limit": 10,
"current": 10,
"message": "This customer's scene limit (10) has been reached.",
"upgradeUrl": "/account/billing?upgrade=plan"
}(upgradeUrl comes from the shared plan-limit envelope and points at your own
Moshpit billing — for a per-customer cap, just raise the customer's
maxSplats.)
You raise the limit. Bump the allocation with another PUT — say to 20 GB and
25 scenes — and the customer is immediately unblocked:
Bash
curl -X PUT https://moshpit.studio/api/v1/external-user \
-H "Authorization: Bearer msk_YOUR_SECRET_KEY" \
-H "X-Moshpit-Public-Key: mpk_YOUR_PUBLIC_KEY" \
-H "X-Moshpit-External-User-Id: customer_42" \
-H "Content-Type: application/json" \
-d '{ "limitBytes": 21474836480, "maxSplats": 25 }'This is the natural hook for your own upsell: when a customer hits a cap, charge them on your side, then raise their allocation.
4. Read per-customer usage
Drive a usage meter for each customer from your dashboard:
Bash
curl "https://moshpit.studio/api/v1/external-user/usage" \
-H "Authorization: Bearer msk_YOUR_SECRET_KEY" \
-H "X-Moshpit-Public-Key: mpk_YOUR_PUBLIC_KEY" \
-H "X-Moshpit-External-User-Id: customer_42"JSON
{
"status": "success",
"usage": {
"storage": {
"usedBytes": 4294967296,
"limitBytes": 5368709120,
"remainingBytes": 1073741824,
"percentUsed": 80
},
"splats": { "used": 7, "limit": 10 }
}
}storage.limitBytes and splats.limit are this customer's allocation —
splats.limit is null for an uncapped customer.
5. Handle customer downgrade or cancellation
When your customer changes plan in your product, Studio should receive the same state so hosted assets stay in sync with your billing system.
For a downgrade, send the lower caps and optionally the scenes the customer chose to keep active:
Bash
curl -X PUT https://moshpit.studio/api/v1/external-user \
-H "Authorization: Bearer msk_YOUR_SECRET_KEY" \
-H "X-Moshpit-Public-Key: mpk_YOUR_PUBLIC_KEY" \
-H "X-Moshpit-External-User-Id: customer_42" \
-H "Content-Type: application/json" \
-d '{
"billingState": "active",
"limitBytes": 1073741824,
"maxSplats": 3,
"keepSplatIds": ["65f1a2b3c4d5e6f7a8b9c0d1"]
}'For a cancellation where your free state has no quota, set zero caps:
Bash
curl -X PUT https://moshpit.studio/api/v1/external-user \
-H "Authorization: Bearer msk_YOUR_SECRET_KEY" \
-H "X-Moshpit-Public-Key: mpk_YOUR_PUBLIC_KEY" \
-H "X-Moshpit-External-User-Id: customer_42" \
-H "Content-Type: application/json" \
-d '{
"billingState": "canceled",
"limitBytes": 0,
"maxSplats": 0
}'Scenes outside the keeper/cap set become private and non-embeddable retained archive. They disappear from normal product surfaces and are released from user-facing storage counters while hosted assets remain stored.
Suspending and deleting a customer
- Temporarily suspend without retained archive:
PUTwith"status": "inactive". An inactive customer cannot mint scoped sessions or upload; their active scenes remain as-is. - Billing suspension/cancellation: use
billingStateso Studio applies retained archive rules. - Delete entirely:
DELETE /api/v1/external-usersoft-deletes the customer and cascade-deletes all of their scenes, returningdeletedSplatCount. This is the explicit destructive path, not the normal subscription-cancel path.
A note on capabilities
Whether a session can view or edit is decided per integration, not per customer. Per-customer allocation governs storage and scene counts; it does not change what a session is allowed to do.
What's next
- External Users API — full reference for all four endpoints.
- POST embed-sessions — how
X-Moshpit-External-User-Idscopes a session. - Credentials & Security — where the public key, secret, and session token fit.