Abodio Video Processing API

Extract comprehensive home inventory from walkthrough videos using AI. Create a session, upload a video, and receive structured data about every room, appliance, fixture, piece of furniture, electronics, and decor detected.

Base URLhttps://api.abodio.com/v1
Content Typeapplication/json
API Versionv1

How it works

  1. Create a session — Your backend calls POST /v1/sessions with your API key. You receive a session ID, upload URL, and upload token.
  2. Upload a video — Either redirect the user to the hosted upload page, or upload directly via the signed URL or TUS protocol.
  3. Signal upload complete — Call POST /v1/sessions/:id/upload-complete to trigger processing.
  4. Receive results — Poll GET /v1/sessions/:id or receive a webhook when processing finishes. Fetch spaces and assets from the results endpoints.

Authentication

The Abodio API uses API keys for server-to-server authentication. Include your key in the Authorization header as a Bearer token.

Keep your API key secret. Never expose it in client-side code, public repositories, or frontend bundles. All API key-authenticated requests should originate from your backend server.
curl
curl https://api.abodio.com/v1/sessions \
  -H "Authorization: Bearer abodio_sk_test_xxxxxxxxxxxx"

Two authentication modes

ModeUsed byMechanism
API KeyYour backend serverAuthorization: Bearer sk_... header
Upload TokenClient-side upload page?token=... query parameter

The upload token is a single-use, session-scoped token returned when you create a session. It grants access only to that session's upload endpoints and is safe to pass to the client.

Session Lifecycle

Every session follows a predictable state machine. Understanding these states helps you build robust integrations.

created
processing
completed
failed
expired
StatusDescriptionTransitions to
createdSession created, waiting for video uploadprocessing, expired
processingVideo uploaded, AI analysis in progresscompleted, failed
completedAnalysis finished, results availableTerminal
failedProcessing error occurredTerminal
expiredSession timed out or was cancelledTerminal

API Reference

All session endpoints require API key authentication via the Authorization header.

POST/v1/sessions

Create a new processing session. Returns a session ID, a signed upload URL for the video, and an upload token for the client-side upload page.

Request body

ParameterTypeRequiredDefaultDescription
external_user_idstringRequiredYour unique identifier for the user. Max 255 characters.
external_home_idstringOptionalYour home/property ID. Returned in session responses and webhooks for correlation.
external_space_idsstring[]OptionalArray of your space/room IDs. Passed through for mapping detected spaces to your system.
external_asset_idsstring[]OptionalArray of your asset/item IDs. Passed through for mapping detected assets to your system.
max_videosintegerOptional1Maximum number of videos allowed in this session.
max_duration_secintegerOptional600Maximum video duration in seconds. Hard cap at 600 (10 minutes).
expires_inintegerOptional86400Session time-to-live in seconds. Default is 24 hours.
callback_urlstringOptionalWebhook URL. Receives POST when session completes, fails, or expires.
success_urlstringOptionalRedirect the user here after successful upload.
cancel_urlstringOptionalRedirect URL if the user cancels.
failure_urlstringOptionalRedirect URL on upload failure.
metadataobjectOptional{}Arbitrary key-value pairs attached to the session (max 10 KB serialized). Useful for linking to your internal records.
bubble_versionstringOptionalBubble app version for enum alignment. When provided, the API fetches your Bubble app's home types, space types, asset types, and subtypes so AI output conforms to your data model. Obtain the version from your Bubble deployment (e.g. "933ok").

Request

curl
curl -X POST https://api.abodio.com/v1/sessions \
  -H "Authorization: Bearer abodio_sk_test_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "external_user_id": "user_2kFj9x",
    "external_home_id": "home_abc123",
    "external_space_ids": ["space_kitchen_1", "space_living_2"],
    "external_asset_ids": ["asset_fridge_1", "asset_sofa_2"],
    "max_videos": 1,
    "max_duration_sec": 300,
    "callback_url": "https://yourapp.com/webhooks/abodio",
    "success_url": "https://yourapp.com/upload/success",
    "bubble_version": "933ok",
    "metadata": {
      "property_id": "prop_abc123"
    }
  }'

Response 201 Created

json
{
  "session_id": "f6414dd2-112b-43a8-9983-4570b29d195d",
  "upload_url": "https://xxx.supabase.co/storage/v1/object/upload/sign/videos/...",
  "upload_token": "44a0870c1b0adfdff123740af4b8dcc...",
  "expires_at": "2026-02-12T20:50:39.839Z",
  "success_url": "https://yourapp.com/upload/success",
  "cancel_url": null,
  "failure_url": null,
  "constraints": {
    "max_videos": 1,
    "max_duration_sec": 300,
    "max_file_size_bytes": 524288000,
    "accepted_types": [
      "video/mp4",
      "video/quicktime",
      "video/webm",
      "video/x-msvideo",
      "video/x-matroska"
    ]
  }
}
FieldTypeDescription
session_idstringUnique session identifier (UUID).
upload_urlstringSigned URL for single-PUT upload. Expires with the session.
upload_tokenstringToken for authenticating the client upload page. Safe to expose to the browser.
expires_atstringISO 8601 expiration timestamp.
constraintsobjectUpload limits: max file size, duration, accepted MIME types.
GET/v1/sessions/:id

Retrieve a session's current status and summary counts. Use this to poll for completion if you don't use webhooks.

Path parameters

ParameterTypeDescription
idstringSession UUID

Request

curl
curl https://api.abodio.com/v1/sessions/f6414dd2-112b-43a8-9983-4570b29d195d \
  -H "Authorization: Bearer abodio_sk_test_xxxxxxxxxxxx"

Response 200 OK

json
{
  "session_id": "d7c05bfb-6eaa-4abb-8c64-d2e6e8e34c15",
  "status": "completed",
  "external_user_id": "user_2kFj9x",
  "external_home_id": "home_abc123",
  "external_space_ids": ["space_kitchen_1", "space_living_2"],
  "external_asset_ids": ["asset_fridge_1", "asset_sofa_2"],
  "max_videos": 1,
  "max_duration_sec": 600,
  "videos_count": 1,
  "total_spaces": 12,
  "total_assets": 49,
  "success_url": "https://yourapp.com/upload/success",
  "cancel_url": null,
  "failure_url": null,
  "callback_url": "https://yourapp.com/webhooks/abodio",
  "metadata": { "property_id": "prop_abc123" },
  "expires_at": "2026-02-16T22:32:20.236Z",
  "created_at": "2026-02-15T22:32:20.279Z"
}
GET/v1/sessions/:id/spaces

Get all detected rooms and spaces from a completed session. Each space includes its type, confidence score, video timestamps, and detected features.

Request

curl
curl https://api.abodio.com/v1/sessions/f6414dd2-.../spaces \
  -H "Authorization: Bearer abodio_sk_test_xxxxxxxxxxxx"

Response 200 OK

json
{
  "spaces": [
    {
      "id": "803c7d95-a4c1-4b17-8af8-d88b7baf04f6",
      "video_id": "7c708f9f-3ec7-4fc2-adf4-32f6ec3ce35d",
      "space_type": "kitchen",
      "space_subtype": null,
      "label": "Kitchen",
      "condition": "excellent",
      "flooring_type": "tile",
      "wall_finish": "painted drywall",
      "confidence": null,
      "timestamp_start_sec": 60,
      "timestamp_end_sec": 70,
      "features": [
        "Island with dark countertop",
        "Stainless steel refrigerator",
        "Modern cabinetry",
        "Tile flooring",
        "Recessed lighting",
        "Open to sunroom",
        "Decorative items on counters",
        "Framed artwork on walls"
      ]
    },
    {
      "id": "3726ae0c-ed19-4998-8965-12cf45758736",
      "video_id": "7c708f9f-3ec7-4fc2-adf4-32f6ec3ce35d",
      "space_type": "living_room",
      "space_subtype": null,
      "label": "Living Room",
      "condition": "good",
      "flooring_type": "hardwood",
      "wall_finish": "painted drywall",
      "confidence": null,
      "timestamp_start_sec": 10,
      "timestamp_end_sec": 10,
      "features": [
        "Fireplace with dark surround",
        "Mantel with decorative items",
        "Large windows with curtains",
        "Hardwood flooring",
        "Upholstered furniture",
        "Framed artwork",
        "Ceiling with crown molding"
      ]
    },
    {
      "id": "697fa63c-4bb4-4c75-8a3e-e7cb81cb2bdc",
      "video_id": "7c708f9f-3ec7-4fc2-adf4-32f6ec3ce35d",
      "space_type": "laundry",
      "space_subtype": null,
      "label": "Laundry Room",
      "condition": "excellent",
      "flooring_type": "unknown",
      "wall_finish": "painted drywall",
      "confidence": null,
      "timestamp_start_sec": 90,
      "timestamp_end_sec": 110,
      "features": [
        "Thermador washer/dryer",
        "Stainless steel appliances",
        "Modern fixtures",
        "Utility space"
      ]
    }
  ]
}

Space object fields

FieldTypeDescription
idstringUnique space identifier (UUID).
video_idstringThe video this space was detected in.
space_typestringRoom type: kitchen, living_room, bedroom, bathroom, garage, laundry, office, sunroom, entryway, hallway, etc.
space_subtypestring?Optional qualifier: "media area", "master", "guest", "half", etc.
labelstringAI-generated human-readable label, e.g. "Kitchen", "Foyer/Entry Hall", "Living Room - Media Wall".
conditionstring?Overall condition: excellent, good, fair, or poor.
flooring_typestring?Detected flooring: "hardwood", "tile", "ceramic tile", "carpet", etc.
wall_finishstring?Detected wall finish: "painted drywall", "painted drywall with wainscoting", "painted drywall - blue accent walls", etc.
confidencenumber?AI confidence score (0.0 to 1.0). May be null.
timestamp_start_secnumberVideo timestamp (seconds) where this space first appears.
timestamp_end_secnumberVideo timestamp (seconds) where this space ends.
featuresstring[]Notable features detected as descriptive strings, e.g. "Stainless steel refrigerator", "Recessed lighting", "Crown molding".
GET/v1/sessions/:id/assets

Get all detected assets (appliances, fixtures, furniture, structural features, electronics, and decor) from a completed session. Each asset includes category, brand, model, condition, estimated value, and the space it belongs to.

Request

curl
curl https://api.abodio.com/v1/sessions/f6414dd2-.../assets \
  -H "Authorization: Bearer abodio_sk_test_xxxxxxxxxxxx"

Response 200 OK

json
{
  "assets": [
    {
      "id": "5a6bda63-67bd-4b09-8f1d-1e6048fc183a",
      "video_id": "7c708f9f-3ec7-4fc2-adf4-32f6ec3ce35d",
      "space_id": "db03956e-1ce7-4430-8218-4c4819b04fd7",
      "category": "decor",
      "subcategory": "rug",
      "name": "Area Rug",
      "brand": null,
      "model": null,
      "material": "wool or synthetic",
      "color": "red with traditional pattern",
      "dimensions": null,
      "condition_rating": "good",
      "condition_notes": null,
      "estimated_value_usd": null,
      "confidence": "high",
      "detected_by": "claude-haiku-4.5",
      "features": null,
      "source": "ai_detected"
    },
    {
      "id": "51a275bb-d8f8-45bc-a4c0-87facc781ae1",
      "video_id": "7c708f9f-3ec7-4fc2-adf4-32f6ec3ce35d",
      "space_id": "db03956e-1ce7-4430-8218-4c4819b04fd7",
      "category": "structural",
      "subcategory": "fireplace",
      "name": "Fireplace",
      "brand": null,
      "model": null,
      "material": "stone/brick with dark surround",
      "color": "dark",
      "dimensions": null,
      "condition_rating": "good",
      "condition_notes": null,
      "estimated_value_usd": null,
      "confidence": "high",
      "detected_by": "claude-haiku-4.5",
      "features": null,
      "source": "ai_detected"
    },
    {
      "id": "a89f2c41-3b7e-4d12-9e56-7f8a1b2c3d4e",
      "video_id": "7c708f9f-3ec7-4fc2-adf4-32f6ec3ce35d",
      "space_id": "803c7d95-a4c1-4b17-8af8-d88b7baf04f6",
      "category": "appliance",
      "subcategory": "refrigerator",
      "name": "Stainless Steel Refrigerator",
      "brand": null,
      "model": null,
      "material": "stainless steel",
      "color": "silver",
      "dimensions": null,
      "condition_rating": "excellent",
      "condition_notes": null,
      "estimated_value_usd": null,
      "confidence": "high",
      "detected_by": "claude-haiku-4.5",
      "features": null,
      "source": "ai_detected"
    }
  ]
}

Asset object fields

FieldTypeDescription
idstringUnique asset identifier (UUID).
video_idstringThe video this asset was detected in.
space_idstringThe space (room) this asset belongs to.
categorystringOne of: appliance, fixture, furniture, structural, electronics, decor.
subcategorystringSpecific type: "refrigerator", "fireplace", "rug", "door", "artwork", "staircase", etc.
namestringHuman-readable name, e.g. "Area Rug", "Stainless Steel Refrigerator", "Paneled Interior Door".
brandstring?Detected brand name, if identifiable.
modelstring?Detected model number, if visible.
materialstring?Primary material: "stainless steel", "wood", "wool or synthetic", etc.
colorstring?Detected color: "silver", "white", "red with traditional pattern", etc.
dimensionsstring?Estimated dimensions if determinable.
condition_ratingstring?One of: excellent, good, fair, poor.
condition_notesstring?Free-text description of condition details.
estimated_value_usdnumber?AI-estimated replacement value in USD. Null if not determinable.
confidencestringAI confidence: high, medium, or low.
detected_bystringWhich AI model produced the result: claude-haiku-4.5 or claude-sonnet-4.5.
featuresstring[]?Additional features if detected, e.g. "ice_maker", "self_cleaning".
sourcestringai_detected, user_added, or user_edited.
DELETE/v1/sessions/:id

Cancel a session and mark it as expired. Cannot be called on sessions that are already completed or expired.

Request

curl
curl -X DELETE https://api.abodio.com/v1/sessions/f6414dd2-... \
  -H "Authorization: Bearer abodio_sk_test_xxxxxxxxxxxx"

Response 200 OK

json
{ "session_id": "f6414dd2-...", "status": "expired" }
POST/v1/sessions/:id/upload-complete

Signal that the video upload is finished and trigger AI processing. This is the server-side endpoint — call it from your backend after the user completes the upload.

Request body

ParameterTypeRequiredDescription
upload_tokenstringRequiredThe upload token returned when the session was created.

Request

curl
curl -X POST https://api.abodio.com/v1/sessions/f6414dd2-.../upload-complete \
  -H "Authorization: Bearer abodio_sk_test_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "upload_token": "44a0870c1b0adfdf..." }'

Response 200 OK

json
{
  "session_id": "f6414dd2-...",
  "video_id": "9f867bc2-...",
  "status": "processing",
  "redirect_url": "https://yourapp.com/upload/success"
}

Upload Client API

These endpoints are used by the hosted upload page and are authenticated via the upload token (not the API key). They're designed for browser-side use.

GET/v1/upload/:sessionId?token=...

Fetch session data and TUS upload configuration for the frontend. Returns everything the browser needs to upload a video directly to storage.

Query parameters

ParameterTypeRequiredDescription
tokenstringRequiredThe upload token from session creation.

Request

curl
curl "https://api.abodio.com/v1/upload/f6414dd2-...?token=44a0870c..."

Response 200 OK

json
{
  "session_id": "f6414dd2-112b-43a8-9983-4570b29d195d",
  "status": "created",
  "constraints": {
    "max_videos": 1,
    "max_duration_sec": 300,
    "max_file_size_bytes": 524288000,
    "accepted_types": ["video/mp4", "video/quicktime", "video/webm", ...]
  },
  "upload": {
    "tus_endpoint": "https://xxx.supabase.co/storage/v1/upload/resumable",
    "auth_token": "eyJhbGciOiJIUzI1NiIs...",
    "bucket": "videos",
    "path": "f6414dd2-.../9f867bc2-.../video",
    "video_id": "9f867bc2-7b0a-4ef0-8f70-837ce052c8df"
  },
  "redirect_urls": {
    "success": "https://yourapp.com/upload/success",
    "cancel": null,
    "failure": null
  },
  "expires_at": "2026-02-12T20:50:39.839Z"
}

Upload object fields

FieldTypeDescription
tus_endpointstringSupabase Storage TUS endpoint for resumable uploads.
auth_tokenstringBearer token for the TUS upload (Supabase anon key).
bucketstringStorage bucket name.
pathstringObject path within the bucket.
video_idstringPre-created video record UUID.
POST/v1/upload/:sessionId/complete

Client-side endpoint to signal the TUS upload is finished. This is the frontend equivalent of POST /v1/sessions/:id/upload-complete. Authenticated via upload token in the request body.

Request

curl
curl -X POST "https://api.abodio.com/v1/upload/f6414dd2-.../complete" \
  -H "Content-Type: application/json" \
  -d '{ "upload_token": "44a0870c..." }'

Upload Flow Guide

There are two ways to handle video uploads:

Option A: Hosted upload page

The simplest integration. After creating a session, redirect the user to the hosted upload page. The page handles file selection, camera recording, TUS upload, and progress display.

url
https://video.abodio.com/upload/{session_id}?token={upload_token}

When the upload completes, the user is redirected to your success_url.

Option B: Direct upload via TUS

For custom UIs. Use the TUS protocol to upload directly to Supabase Storage with resumable, chunked uploads.

  1. Call GET /v1/upload/:sessionId?token=... to get TUS configuration.
  2. Use tus-js-client (or any TUS client) to upload the file with 6MB chunks.
  3. Set the required TUS metadata: bucketName, objectName, contentType.
  4. On completion, call POST /v1/upload/:sessionId/complete.
Why TUS? The TUS protocol enables resumable uploads. If a connection drops mid-upload, the client automatically resumes from where it left off. This is critical for large video files on mobile connections.

Option C: Simple PUT upload

For server-side uploads from your backend. Use the signed upload_url returned by session creation to PUT the file directly. Then call POST /v1/sessions/:id/upload-complete.

curl
curl -X PUT "https://xxx.supabase.co/storage/v1/object/upload/sign/videos/..." \
  -H "Content-Type: video/mp4" \
  --data-binary @walkthrough.mp4

Processing Pipeline

Once an upload is signalled complete, the video enters a multi-stage AI analysis pipeline. Each tier uses a different model optimized for its task.

1

Frame Extraction

The video is sent to a Python service running FFmpeg and PySceneDetect. It detects scene boundaries and extracts key frames at 1280x720 resolution.

2

Tier 1 — Full Inventory Scan

All frames are processed in batches of 15 by Claude Haiku 4.5. This fast vision model identifies all spaces, appliances, fixtures, furniture, structural features, electronics, and decor. Items with low confidence or in key categories (appliances, fixtures, electronics) are flagged for escalation.

3

Tier 2 — Detail Analysis

Flagged items are sent one frame at a time to Claude Haiku 4.5 for focused analysis. This tier identifies brands, models, materials, and provides more accurate condition assessments.

4

Tier 3 — Disagreement Resolution

When Tier 1 and Tier 2 disagree on an item's identity (brand, category, etc.), 1-3 frames are sent to Claude Sonnet 4.5 for a final determination with reasoning.

5

Deduplication & Storage

Items detected across multiple frames are clustered and merged. Final spaces and assets are stored in the database, and the session moves to completed.

AI Output Schema

Each tier in the processing pipeline produces structured JSON validated against a strict schema. The Tier 1 output is the primary schema — it generates the home summary, spaces, and items that ultimately become the spaces and assets you retrieve via the API.

Tier 1 output structure

The Tier 1 model receives batches of video frames and returns a complete inventory of the property. The output has three top-level objects:

home object

FieldTypeDescription
property_typestringOne of: single_family, condo, apartment, townhouse, multi_family, mobile_home, other.
property_subtypestring?More specific type: "colonial", "ranch", "split-level", "studio", etc.
overall_conditionstringOne of: excellent, good, fair, poor.
estimated_storiesinteger?Number of stories visible in the video.
notable_featuresstring[]High-level property features: "open floor plan", "hardwood throughout", etc.
summarystringOne-sentence summary of the property.

spaces array

FieldTypeDescription
space_indexintegerPosition in the spaces array (referenced by items).
space_typestringRoom type: kitchen, bathroom, bedroom, living_room, dining_room, office, garage, basement, attic, laundry, hallway, closet, patio, balcony, entryway, mudroom, pantry, sunroom, bonus_room, utility, exterior, other.
space_subtypestring?Specific qualifier: "master_bedroom", "half_bath", "walk-in_closet".
labelstringHuman-friendly label: "Primary Kitchen", "Upstairs Bathroom".
conditionstringRoom condition: excellent, good, fair, poor.
image_indicesinteger[]Which frame indices show this space.
featuresstring[]Notable room features: "granite countertops", "recessed lighting".
flooring_typestring?Floor material: "hardwood", "tile", "carpet".
wall_finishstring?Wall treatment: "painted drywall", "shiplap", "exposed brick".

items array

FieldTypeDescription
space_indexintegerReferences the space this item belongs to.
categorystringOne of: appliance, fixture, furniture, structural, electronics, decor.
subcategorystringSpecific type: "refrigerator", "ceiling_fan", "sofa", "window".
namestringDisplay name: "French Door Refrigerator", "Pendant Light Fixture".
brandstring?Brand if identifiable.
materialstring?Primary material.
colorstring?Primary color.
conditionstringItem condition: excellent, good, fair, poor.
confidencestringDetection confidence: high, medium, low.
needs_reviewbooleanFlagged for Tier 2 review (brand/model/condition uncertain).
image_indexintegerFrame index showing this item best.

Example Tier 1 output

json
{
  "home": {
    "property_type": "single_family",
    "property_subtype": "colonial",
    "overall_condition": "good",
    "estimated_stories": 2,
    "notable_features": [
      "open floor plan",
      "hardwood throughout",
      "updated kitchen"
    ],
    "summary": "Well-maintained two-story colonial with modern kitchen, hardwood floors, and spacious living areas."
  },
  "spaces": [
    {
      "space_index": 0,
      "space_type": "kitchen",
      "space_subtype": null,
      "label": "Primary Kitchen",
      "condition": "excellent",
      "image_indices": [3, 4, 5],
      "features": [
        "granite countertops",
        "stainless steel appliances",
        "island with seating"
      ],
      "flooring_type": "tile",
      "wall_finish": "painted drywall"
    },
    {
      "space_index": 1,
      "space_type": "living_room",
      "space_subtype": null,
      "label": "Living Room",
      "condition": "good",
      "image_indices": [0, 1],
      "features": [
        "fireplace",
        "crown molding",
        "large windows"
      ],
      "flooring_type": "hardwood",
      "wall_finish": "painted drywall"
    }
  ],
  "items": [
    {
      "space_index": 0,
      "category": "appliance",
      "subcategory": "refrigerator",
      "name": "French Door Refrigerator",
      "brand": "Samsung",
      "material": "stainless steel",
      "color": "silver",
      "condition": "excellent",
      "confidence": "high",
      "needs_review": false,
      "image_index": 3
    },
    {
      "space_index": 1,
      "category": "structural",
      "subcategory": "fireplace",
      "name": "Gas Fireplace",
      "brand": null,
      "material": "stone",
      "color": "gray",
      "condition": "good",
      "confidence": "high",
      "needs_review": false,
      "image_index": 0
    }
  ]
}

Tier 2 enrichment fields

Tier 2 receives flagged items for focused analysis. It adds or corrects the following fields:

FieldTypeDescription
item_indexintegerIndex of the item in the Tier 1 items array.
brandstring?Confirmed or corrected brand.
modelstring?Identified model name/number.
materialstring?Confirmed or corrected material.
colorstring?Confirmed or corrected color.
dimensionsstring?Approximate dimensions if discernible.
conditionstringConfirmed or corrected condition.
condition_notesstring?Detailed condition notes: "minor scratches on door handle".
estimated_value_usdnumber?Estimated replacement value in USD.
confidencestringUpdated confidence after focused analysis.
featuresstring[]Additional features: "ice_maker", "self_cleaning".
still_uncertainbooleanTrue if item should escalate to Tier 3.

Tier 3 resolution fields

Tier 3 makes a final determination when Tier 1 and Tier 2 disagree. It provides explicit reasoning for its decisions.

FieldTypeDescription
item_indexintegerIndex of the item in the Tier 1 items array.
brandstring?Final brand determination.
modelstring?Final model determination.
confidencestringFinal confidence after expert review.
reasoningstringExplanation of the decision: "Logo matches Samsung RF28 series based on handle style".
final_categorystringFinal category (may override Tier 1).
final_subcategorystringFinal subcategory.
final_namestringFinal display name.
estimated_value_usdnumber?Final estimated value (may override Tier 2).
Bubble enum integration. When a bubble_version is provided at session creation, the Tier 1 schema dynamically validates categories and subcategories against your Bubble app's configured asset types. This ensures all detected items map to valid entries in your data model.

Webhooks

If you provide a callback_url when creating a session, we'll send a POST request to that URL when the session reaches a terminal state.

Event types

EventTrigger
session.completedProcessing finished successfully. Results are available.
session.failedProcessing encountered an error.
session.expiredSession timed out before the video was uploaded.

session.completed payload

json
{
  "id": "b3f7a2e1-9c84-4d56-a1b2-8e3f5d7c9a01",
  "type": "session.completed",
  "created_at": "2026-02-15T22:38:22.000Z",
  "data": {
    "session_id": "d7c05bfb-6eaa-4abb-8c64-d2e6e8e34c15",
    "external_user_id": "user_2kFj9x",
    "total_spaces": 12,
    "total_assets": 49,
    "processing_duration_seconds": 362
  }
}

session.failed payload

json
{
  "id": "c4e8b3f2-1a95-4e67-b2c3-9f4a6e8d0b12",
  "type": "session.failed",
  "created_at": "2026-02-15T22:40:00.000Z",
  "data": {
    "session_id": "d7c05bfb-6eaa-4abb-8c64-d2e6e8e34c15",
    "external_user_id": "user_2kFj9x",
    "error_message": "Frame extraction failed: video codec not supported"
  }
}

Delivery & retries

Webhooks are delivered with up to 3 automatic retries using exponential backoff (1s, 2s, 4s delays). Each attempt has a 10-second timeout. Retries occur on 5xx responses and network errors. 4xx responses are treated as permanent failures and are not retried.

Webhook payload fields

FieldTypeDescription
idstringUnique event identifier.
typestringEvent type (see table above).
created_atstringISO 8601 timestamp.
data.session_idstringThe session this event belongs to.
data.external_user_idstringYour user identifier.
data.total_spacesnumberNumber of rooms/spaces detected (completed only).
data.total_assetsnumberNumber of assets detected (completed only).
data.processing_duration_secondsnumberTotal processing time (completed only).
data.error_messagestringError description (failed only).

Error Reference

All errors follow a consistent format:

json
{
  "error": {
    "code": "INVALID_SESSION",
    "message": "Session not found or invalid token"
  }
}
HTTP StatusError CodeDescription
400MISSING_TOKENUpload token not provided in the request.
400FILE_NOT_FOUNDNo file found in storage. Upload the video before signalling completion.
401MISSING_API_KEYNo API key in the Authorization header.
401INVALID_API_KEYAPI key is invalid or has been revoked.
404NOT_FOUNDThe requested session does not exist.
404INVALID_SESSIONSession not found or the upload token doesn't match.
404NO_VIDEONo pending video record found for this session.
409INVALID_STATUSSession is not in the expected state for this operation.
410SESSION_EXPIREDSession has passed its expiration time.
500SESSION_CREATE_FAILEDDatabase error while creating the session.
500VIDEO_CREATE_FAILEDDatabase error while creating the video record.
409ALREADY_PROCESSINGSession upload was already completed and processing has started.
500PROCESSING_FAILEDFailed to start the video processing workflow.
429Rate limit exceededToo many requests. Check Retry-After header for seconds to wait. See rate limit headers below.
503BUBBLE_ENUMS_INVALIDCould not fetch or validate Bubble enums for the provided bubble_version. The Bubble API may be unavailable or returned empty data.

Constraints & Limits

Video requirements

ConstraintValue
Maximum file size500 MB
Maximum duration600 seconds (10 minutes)
Accepted formatsvideo/mp4, video/quicktime, video/webm, video/x-msvideo, video/x-matroska
Frame extraction resolution1280 x 720 px
Frames per scene2

Session limits

ConstraintValue
Default session TTL24 hours
Maximum videos per sessionConfigurable (default: 1)
Upload token length64 hex characters
Metadata fieldArbitrary JSON object (max 10 KB serialized)
external_user_id max length255 characters
URL fieldsMust be public HTTP(S) endpoints (private/internal IPs rejected)
bubble_version formatAlphanumeric with hyphens/underscores, max 50 characters

Rate limits

ConstraintValue
Write endpoints (POST, DELETE)60 requests per minute per API key
Read endpoints (GET)120 requests per minute per API key

Rate-limited responses return 429 Too Many Requests with the following headers:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the window.
X-RateLimit-RemainingRequests remaining in the current window.
Retry-AfterSeconds to wait before retrying (included on 429 responses).

AI processing tiers

TierModelBatch SizePurpose
Tier 1Claude Haiku 4.515 framesFull inventory scan
Tier 2Claude Haiku 4.51 frameDetail analysis (brand, model)
Tier 3Claude Sonnet 4.51-3 framesDisagreement resolution

Escalation criteria

Items are escalated from Tier 1 to Tier 2 when:

  • Confidence is low
  • Category is appliance, fixture, or electronics (brand/model identification important)
  • Brand is null despite being in a brandable category

Items are escalated from Tier 2 to Tier 3 when Tier 1 and Tier 2 disagree on the item's identity (category, brand, or name).