Zum Inhalt springen
EnglishZugang anfragen

Splats per Schnittstelle bestellen

Ein fremdes System beauftragt eine Aufnahme, verfolgt sie und importiert das fertige Paket — ohne dass ein Mensch durch unsere Oberfläche geht. Diese Seite beschreibt den Vertrag: Ablauf, Zustände, Webhooks und das, was am Ende herauskommt.

Gedacht ist sie für Plattformen, Digital-Twin-Software und Viewer, die Räume ihrer eigenen Kundschaft zeigen. Die Schnittstelle ist ein Nebenweg, nicht der Hauptweg: Wer selbst mit dem Handy aufnimmt, braucht sie nicht.

Der Ablauf in vier Schritten

Anlegen, hochladen, abschließen, verfolgen. Jeder Aufruf braucht einen Bearer-Token deines Zugangs; die Beispiele schreiben ihn als sk_…

  1. Anlegen

    POST /api/v1/captures mit Dateiname und Größe, optional Standort, Name, eigener Referenz (clientRef), Qualität und Ablauf. Die Antwort enthält presignte Teile für den Upload.

  2. Direkt in den Bucket hochladen

    Jeder Teil geht per PUT an seine presignte URL — an uns vorbei. Die Steuerungsebene sieht die Bytes nie; sie erfährt nur, dass sie angekommen sind.

  3. Abschließen

    POST /api/v1/captures/:id/complete mit den ETags der Teile. Ab hier läuft es von selbst: Vorab-Check, Start, Prüfschritte, Training, Kollisionsmesh, Paket.

  4. Verfolgen und abholen

    GET /api/v1/captures/:id pollen, bis der Zustand done ist — oder einen Webhook registrieren. Das Paket liefert GET /api/v1/captures/:id/result.

1 · Aufnahme anlegen
curl -X POST https://splatastic.com/api/v1/captures \
  -H "Authorization: Bearer sk_…" -H "Content-Type: application/json" \
  -d '{
    "filename": "rundgang.mp4",
    "sizeBytes": 214748364,
    "siteName": "Halle 12",
    "clientRef": "acme:asset-42",
    "quality": "standard",
    "flow": "headless"
  }'
Antwort 201
{
  "id": "cap_…",
  "siteId": "site_…",
  "clientRef": "acme:asset-42",
  "state": "uploading",
  "upload": {
    "uploadId": "up_…",
    "key": "captures/cap_…/sources/….mp4",
    "partSize": 16777216,
    "partCount": 13,
    "parts": [{ "partNumber": 1, "url": "https://…" }, "…"]
  }
}
3 · Upload abschließen
curl -X POST https://splatastic.com/api/v1/captures/cap_…/complete \
  -H "Authorization: Bearer sk_…" -H "Content-Type: application/json" \
  -d '{
    "uploadId": "up_…",
    "parts": [{ "partNumber": 1, "etag": "\"…\"" }, "…"]
  }'
4 · Zustand abfragen
{
  "id": "cap_…", "name": "Halle 12", "siteId": "site_…",
  "clientRef": "acme:asset-42", "flow": "headless",
  "state": "processing",
  "stage": "training",
  "progress": 0.62,
  "etaSeconds": 480,
  "needsReview": null,
  "error": null,
  "hasResult": false,
  "createdAt": 1757500000000,
  "updatedAt": 1757500600000
}

Der Zustandsautomat: sieben Werte

state ist der Vertrag. stage daneben nennt die interne Stufe und ist rein informativ — verlass dich nicht darauf.

stateBedeutung
uploadingNoch keine Quelle vollständig registriert — der Upload läuft oder hat nicht begonnen.
checkingDas Material ist da, der Vorab-Check läuft oder der automatische Start steht unmittelbar bevor.
queuedEin Schritt wartet auf einen Platz in der Warteschlange oder auf einen startenden GPU-Pod.
processingEin Schritt rechnet tatsächlich.
needs-reviewEin Mensch wird gebraucht: ein Prüfschritt wartet, der automatische Start konnte nicht, oder der laufende Schritt wurde angehalten. needsReview nennt Grund, Text und einen Deep-Link.
doneAusgeliefert — GET …/result gibt die Dateien heraus.
failedEin Schritt ist fehlgeschlagen; error trägt die Meldung.

Abläufe: wie viel Mensch der Auftrag braucht

flow beim Anlegen entscheidet, ob ein gelbes oder rotes Prüfurteil anhält oder durchläuft.

flowBedeutung
headlessVorgabe. Startet von selbst, sobald der Vorab-Check nicht rot ist; eine grüne Ampel läuft durch, gelb oder rot hält an und wird needs-review.
unattendedFür ein System, das das Ergebnis ohnehin selbst prüft: jede Ampel läuft durch, angehalten wird nur bei einem Fehler. Das Urteil steht trotzdem in den Messwerten der Aufnahme.
autoWie headless, hält aber zusätzlich am Splat-Gate an.
guidedStartet erst auf Klick und hält an jedem Prüfschritt — der Weg für einen Menschen an der Oberfläche.

unattended ist für ein System gedacht, das das Ergebnis ohnehin selbst prüft und nur Splat und Kollisionsnetz will: Es tauscht die Ampel gegen Durchsatz. Ein Lauf, dessen Kamerabahn rot war, liefert trotzdem — und ist an seinen Messwerten als solcher zu erkennen.

Webhooks statt Pollen

Jeder Zustandswechsel als signierter POST. Pollen bleibt daneben immer möglich — ein Webhook ist die Ergänzung, kein Ersatz.

Webhook registrieren
curl -X POST https://splatastic.com/api/v1/webhooks \
  -H "Authorization: Bearer sk_…" -H "Content-Type: application/json" \
  -d '{ "url": "https://dein-system.example/hooks/splatastic" }'
# → 201 { "id": "…", "url": "…", "secret": "…" }   ← das Secret steht nur hier im Klartext
Was ankommt (capture.state)
{
  "event": "capture.state",
  "occurredAt": "2026-09-10T13:40:00.000Z",
  "previousState": "processing",
  "capture": {
    "id": "cap_…", "name": "Halle 12", "clientRef": "acme:asset-42",
    "flow": "headless",
    "state": "needs-review",
    "needsReview": {
      "reason": "gate",
      "text": "Die Kamerabahn wartet auf deine Prüfung.",
      "url": "https://splatastic.com/aufnahmen/cap_…"
    },
    "error": null, "hasResult": false,
    "createdAt": 1757500000000, "updatedAt": 1757500600000
  }
}
KopfzeileBedeutung
X-Splatastic-EventDas Ereignis dieser Zustellung: capture.state, capture.reexported oder capture.retention. Danach wird verzweigt — die Nutzlasten haben verschiedene Formen.
X-Splatastic-DeliveryKennung dieser Zustellung, stabil über alle Versuche — damit kannst du beim Empfangen entdoppeln.
X-Splatastic-TimestampUnix-Sekunden dieses Versuchs; fließt in die Signatur ein.
X-Splatastic-Signaturesha256=<hex HMAC-SHA256(secret, timestamp + "." + body)>
Signatur prüfen
const crypto = require('node:crypto')

function verify(secret, timestamp, rawBody, header) {
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex')
  const a = Buffer.from(header)
  const b = Buffer.from(expected)
  // timingSafeEqual wirft bei ungleicher Länge, statt einfach false zu sein.
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

rawBody muss der unveränderte Request-Body sein, vor jedem JSON-Parsen, und timestamp der Wert aus X-Splatastic-Timestampdesselben Requests.

Antwortet dein Endpunkt nicht mit 2xx (oder gar nicht innerhalb von zehn Sekunden), wird wiederholt — nach etwa einer Minute, fünf Minuten, einer Viertelstunde, einer Stunde und sechs Stunden.X-Splatastic-Delivery bleibt dabei gleich, entdoppeln kannst du also darüber.

Verzweige immer über X-Splatastic-Event: Es gibt mehr als ein Ereignis, und die Nutzlasten haben verschiedene Formen. Ein Ereignis, das du nicht kennst, verwirfst du — dann bist du fertig, bevor es losgeht.

Der Liefervertrag

Was du bekommst, und was daran garantiert ist. Bricht ein Paket den Vertrag, wird es nicht ausgeliefert — statt einer Nutzlast, die dein Import nicht lesen kann.

Pflichtfelder in meta.json
FeldWertBedeutung
version1Die Fassung des Pakets.
sourceType"splatastic"Woher das Paket kommt.
upAxis"Y"Mehr als „Y zeigt nach oben": Die Lieferung ist zum Boden ausgerichtet. Ist die Schätzung unsicher — Treppe, Rampe, ein Gang, der nur eine Linie beschreibt —, bleibt die Szene so stehen, wie sie rekonstruiert wurde.
metersPerUnit1Die Lieferung ist metrisch: eine Einheit ist ein Meter. Eine Aufnahme ganz ohne Maßstabsmessung ist ehrlich nicht metrisch und wird nicht ausgeliefert.
identityTransformtrueEs ist nichts mehr umzurechnen — Splat und Kollisionsnetz tragen denselben Bake.
Im Paket
DateiInhalt
splat.plyDer Splat selbst.
collision.glbDas Kollisionsnetz, im selben Rahmen wie der Splat (auch als collision.ply).
meta.jsonDer Liefervertrag und die Kennzahlen des Laufs.
package.zipAlles zusammen in einer Datei.
GET /api/v1/captures/:id/result
{
  "deliveryId": "del_…",
  "builtAt": 1757500600000,
  "expiresAt": 1757504200000,
  "files": {
    "splatPly":     { "url": "https://…", "filename": "splat.ply",     "sizeBytes": 214748364 },
    "collisionGlb": { "url": "https://…", "filename": "collision.glb", "sizeBytes": 5242880 },
    "metaJson":     { "url": "https://…", "filename": "meta.json",     "sizeBytes": 512 },
    "packageZip":   { "url": "https://…", "filename": "package.zip",   "sizeBytes": 225000000 }
  },
  "meta": { "version": 1, "metersPerUnit": 1, "upAxis": "Y", "…": "…" },
  "metrics": { "psnr": 27.3, "splatCount": 812000, "…": "…" }
}

Dazu trägt meta.json alles, was sonst noch bekannt ist: Herkunft, Qualitätsstufe, Kennzahlen des Laufs und unter areaCrop die Stufe, mit der die gelieferte Fassung auf den erfassten Bereich beschnitten wurde. Seit dem 17.09.2026 ist generous die Vorgabe— wer die volle Szene will, schickt beim Anlegen ausdrücklich "areaCrop": "off" mit. Was tatsächlich angewandt wurde, steht in jedem Paket in meta.json; das ist die verlässliche Angabe.

Die URLs aus …/result sind Abholscheine und laufen nach einer Stunde ab: Ein dauerhaft gültiger Link wäre ein Passwort, das nie abläuft. Kopier dir die Bytes; ein erneuter Aufruf liefert jederzeit frische URLs.

Fehlercodes

Eine Fehlerantwort hat immer die Form { "error": "<Text>", "code": "<Fehlercode>" }error ist der Satz für Menschen, code das, was dein Programm abfragt.

codeWann
validationEine Angabe im Aufruf fehlt oder ist unbekannt.
unsupported_typeDateityp nicht unterstützt — dazu gehören Rohdateien einer 360°-Kamera (.insv, .360).
too_largeGrößer als das Upload-Limit dieser Instanz.
not_foundStandort, Aufnahme, Upload oder Webhook gibt es nicht — oder er gehört jemand anderem.
no_object_storeDiese Instanz hat keinen Objektspeicher; ohne ihn gibt es keinen Direkt-Upload.
stage_conflictDie Aufnahme ist nicht (mehr) im passenden Schritt — ein Upload lässt sich nur einmal abschließen, und gelöscht wird nicht, während etwas läuft.
not_runningcancel, obwohl gerade nichts läuft.
not_deliveredGET …/result, bevor die Aufnahme ausgeliefert ist, oder das Paket fehlt.
frame_staleDie deliveryId eines schreibenden Aufrufs gehört zu einem früheren trainierten Stand.
frame_mismatchDie mitgeschickten bounds liegen zurückgerechnet weit neben der Szene.
edit_conflictJemand hat seit deinem Stand gespeichert; die Antwort trägt den aktuellen Stand.
reexport_runningEin Re-Export dieser Aufnahme rechnet gerade.
reexport_unavailableDie Arbeitsdaten sind abgelaufen; ein erneuter Export wäre eine Neuberechnung aus dem Quellmaterial.
invalid_urlDie Webhook-url ist keine gültige https://-Adresse.

Du willst Splats aus deinem eigenen System bestellen?

Zugang anfragen