V2 API
- Base URL
https://api.samsar.one- Route prefix
/v2 · alias /api/v2- Authentication
- Internal, app-key, or external-user actor
- Execution
- Unified synchronous and asynchronous workflows
- Billing
- Delegates to the canonical route's existing billing rules
- Content type
application/json
/v2 is an additive API surface. /v1 is not deprecated. The same router is also available under /api/v2 for clients that use the longer API prefix.
The /v2 routes are designed for clients where either an internal Samsar account or an external user can be authenticated in the same UI. Each route resolves the authenticated actor and routes the request through the existing internal or external-user handling path so the same validation, pricing, and credit deduction rules continue to apply.
Route map
The tables favor canonical paths. Compatibility aliases are called out only when they are useful during a migration; use the endpoint sections below for request contracts and status behavior.
Core session and request routes
| Route | Purpose |
|---|---|
GET /v2/health | Check V2 service health without authentication. |
POST /v2/session | Create a V2 session. |
POST /v2/create_login_token | Create a login token for authenticated flows. |
GET /v2/requests | List requests visible to the authenticated actor. |
GET /v2/status | Poll request or session status. |
GET /v2/status_detailed | Poll status plus normalized video-session preview data. |
POST /v2/status_detailed | POST alias for detailed status checks. |
Credits and billing routes
| Route | Purpose |
|---|---|
GET /v2/credits | Fetch resolved actor credits. |
POST /v2/credits/recharge | Create an internal recharge flow. |
POST /v2/credits/grant | Grant credits to an external user. |
GET /v2/payment_status | Fetch payment status for a resolved actor. |
POST /v2/payment_status | Check payment status with a request body. |
User account routes
| Route | Purpose |
|---|---|
POST /v2/user/recharge_credits | Create or top up a Samsar user by email. |
GET /v2/user/recharge_credits | GET alias for user credit recharge. |
POST /v2/user/create_external_user | Create or update an external user mapped to the authenticated internal user. |
POST /v2/user/refresh_token | Rotate a user refresh token. |
GET /v2/user/refresh_token | GET alias for user refresh token rotation. |
GET /v2/user/credits | Fetch authenticated user credits. |
GET /v2/user/usage/logs | Fetch authenticated user usage logs. |
GET /v2/user/payment_status | Fetch user payment status. |
POST /v2/user/payment_status | Check user payment status with a request body. |
App key routes
| Route | Purpose |
|---|---|
POST /v2/users/app_key | Create a long-running app key. |
GET /v2/users/app_key | Fetch active app key metadata. |
DELETE /v2/users/app_key | Revoke the active app key. |
POST /v2/users/app_key/refresh | Rotate an app key. |
GET /v2/users/app_key/refresh | GET alias for app key rotation. |
Assistant and utility routes
| Route | Purpose |
|---|---|
POST /v2/assistant/set_system_prompt | Set an assistant system prompt. |
POST /v2/assistant/completion | Create an assistant completion. |
POST /v2/external/chat | Create an OpenAI-compatible external inference completion. |
POST /v2/external/chat/completions | Alias for external inference completions. |
POST /v2/external/assistant | Assistant-named alias for external chat completions. |
POST /v2/external/assistant/completions | Assistant-named OpenAI-compatible completion alias. |
GET /v2/external/chat/status | Poll an external assistant request by request_id or client_request_id. |
GET /v2/external/chat/completions/status | Status alias for external assistant requests. |
GET /v2/external/assistant/status | Assistant-named status alias. |
POST /v2/external/moderation | Create a no-charge external moderation check. |
POST /v2/external/moderations | Alias for external moderation checks. |
GET /v2/external/api_key/validate | Validate a Samsar provider API key. |
GET /v2/external/providers/capabilities | Read provider, model, and action capabilities. |
POST /v2/external/providers/validate | Validate deployment provider credentials. |
POST /v2/external/narrative/create_single | Create a singular structured narrative. |
POST /v2/external/narrative/create_branching | Create a branching structured narrative. |
GET /v2/external/narrative/status | Poll a singular or branching narrative request. |
POST /v2/external/audio/text_to_speech | Create hosted speech for 1 credit/request. |
POST /v2/external/audio/text_to_music | Create an API-key billed hosted text-to-music request. |
POST /v2/external/audio/text_to_sound_effect | Create a hosted sound effect for 1 credit/request. |
POST /v2/external/audio/transcript_align | Align transcript audio with usage-based per-minute billing. |
GET /v2/external/audio/status | Poll hosted external audio request status. |
POST /v2/external/embeddings | Create 1536-dimension external embeddings with text-embedding-3-small. |
POST /v2/external/image/text_to_image | Generate hosted images; /generate and /generations are aliases. |
POST /v2/external/image/assign_title | Generate an image title with usage-based billing. |
POST /v2/external/image/enhance | Enhance a hosted image. |
POST /v2/external/image/remove_branding | Remove branding from a hosted image. |
POST /v2/external/image/add_image_set | Extend a hosted image set. |
GET /v2/external/image/status | Poll a hosted image request. |
POST /v2/utils/assistant_session | Create or resolve an assistant session. |
POST /v2/utils/usage_charge | Apply a usage charge. |
POST /v2/generate_embeddings_from_plain_text | Create embeddings from cleaned plain text. |
Media generation routes
| Route | Purpose |
|---|---|
POST /v2/upload_image_data | Upload image data for later generation routes. |
POST /v2/image/assign_title | Assign a short SEO-friendly image title. Returns { "content": "..." } synchronously and uses usage-based credits. |
POST /v2/images/assign_title | Alias for POST /v2/image/assign_title. |
POST /v2/image/enhance | Enhance or upscale an image using the same pricing as /v1/image/enhance. |
POST /v2/image/remove_branding | Remove watermark, logo, or brand text using the same pricing as /v1/image/remove_branding. |
POST /v2/image/add_image_set | Extend an image set using the same pricing as /v1/image/add_image_set. |
POST /v2/image/text_to_image | Generate images for 15 credits per output. |
POST /v2/text_to_video | Create a video from text input. |
POST /v2/image_list_to_video | Create a video from image URLs. |
POST /v2/video/step/text_to_video | Create a step-controlled text-to-video request. |
POST /v2/video/step/image_to_video | Create a step-controlled image-to-video request. |
POST /v2/video/step/lip_sync | Create a step-controlled lip-sync request. |
POST /v2/video/step/sound_effect | Create a step-controlled sound-effect request. |
GET /v2/video/step/:request_id/status | Poll step-controlled request status and completed resources. |
GET /v2/video/step/:request_id/status_detailed | Poll step status plus normalized preview data for completed media. |
POST /v2/video/step/:request_id/status_detailed | POST alias for detailed step status checks. |
POST /v2/video/step/:request_id/process_next | Continue a completed step-controlled request to the next stage. |
POST /v2/external/video/text_to_video | Create an API-key billed hosted text-to-video request for remote deployments. |
POST /v2/external/video/image_to_video | Create an API-key billed hosted image-to-video request for remote deployments. |
POST /v2/external/video/direct_image_to_video | Generate hosted video directly from a public image URL. |
POST /v2/external/video/narrative_to_video | Start hosted media generation from a completed singular or branched NarrativeRequest. |
POST /v2/external/video/lip_sync | Create an API-key billed hosted lip-sync request from public media URLs. |
POST /v2/external/video/sound_effect | Create an API-key billed hosted sound-effect request from a public video URL. |
GET /v2/external/video/status | Poll hosted external video request status. |
GET /v2/external/video/status_detailed | Poll hosted external video detailed status and preview data. |
POST /v2/text_to_interactive_video | Generate an interactive video workflow. |
POST /v2/external/video/text_to_interactive_video | External-provider alias for interactive video generation. |
POST /v2/text_to_interactive_video/session | Create an interactive-video draft session. |
POST /v2/translate_video | Translate an existing video. |
POST /v2/retranslate_video | Alias for translating an existing video. |
POST /v2/add_outro_image | Add an outro image to an existing video. |
POST /v2/update_outro_image | Update an existing outro image. |
POST /v2/update_footer_image | Update or remove an existing video footer CTA. |
POST /v2/add_subtitles | Add subtitles; /v2/video/add_subtitles is an alias. |
POST /v2/remove_subtitles | Remove subtitles; /v2/video/remove_subtitles is an alias. |
POST /v2/join_videos | Join videos into one output. |
POST /v2/video/clone | Deep-copy a completed video session and render a new final video URL. |
POST /v2/regenerate_avatar | Regenerate the narrator avatar; /v2/video/regenerate_avatar is an alias. |
POST /v2/reroll-layers | Reroll selected 1-based layers from image generation, then rerender the session. |
POST /v2/video/reroll-layers | Alias for POST /v2/reroll-layers. |
POST /v2/cancel_render | Cancel active render work. |
POST /v2/pause_render | Pause active render work. |
POST /v2/resume_render | Resume paused render work. |
Publishing routes
| Route | Purpose |
|---|---|
POST /v2/archive | Archive a generated asset. |
POST /v2/publish | Publish a generated asset. |
POST /v2/interactive_publication/generate_meta | Generate interactive publication metadata at 1.5×; requires a user auth token. |
Authentication behavior
- Internal API key or internal auth token with no
external_userpayload uses the internal account and deducts internal generation credits. - APP_KEY authentication uses
Authorization: AppKey <APP_KEY>plusx-app-secret: <APP_SECRET>. It is a third internal-account auth method for/v2routes after the APP_KEY has been created with a normal API key or user auth token. - Internal API key/auth token plus an
external_userpayload uses the external-user path and deducts that external user's credits. - External-user auth tokens, internal API keys paired with an external-user API key, and customer sub-account API keys use the external-user path.
- Repeated video routes and status routes resolve
request_id/session_idthrough external request records andGlobalSessionrecords, so either returnedextreq_...ids, normalized external ids, or internal video session ids can be used. POST /v2/interactive_publication/generate_metais the exception to the general credential rules: it accepts a userauth_token, not an API key or APP_KEY.
External user registration
Use POST /v2/user/create_external_user to create or update an external user record under the authenticated internal Samsar user. Authenticate with an internal API key, internal user auth token, or APP_KEY. External-user auth tokens and customer sub-account auth cannot create new external users.
The route accepts a unique_key for the external user, such as a wallet address, email address, or your platform's user id. If unique_key is omitted, Samsar uses external_user_id as the unique_key. Future V2 calls can reference that external user with unique_key.
Request
curl -X POST "https://api.samsar.one/v2/user/create_external_user" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"unique_key": "wallet:0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"email": "member@example.com",
"display_name": "Member Name"
}'
You can also send provider, external_user_id, external_app_id, external_company_id, external_account_id, username, avatar_url, user_type, and metadata. If provider is omitted, Samsar stores the record under the default V2 provider.
Success response (201)
{
"unique_key": "wallet:0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"provider": "samsar_v2",
"external_user_id": "wallet:0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"external_app_id": null,
"external_user": {
"id": "65f...",
"provider": "samsar_v2",
"external_user_id": "wallet:0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"unique_key": "wallet:0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"email": "member@example.com",
"display_name": "Member Name",
"generation_credits": 0
},
"reference": {
"provider": "samsar_v2",
"unique_key": "wallet:0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"external_user_id": "wallet:0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"external_app_id": null
}
}
If a matching unique_key or external identity already belongs to another internal user, the route returns 409.
User credits and tokens
Use /v2/user/recharge_credits when a client needs to create or top up a Samsar user account by email without first holding an API key. The route accepts POST or GET; POST is recommended.
Request
curl -X POST "https://api.samsar.one/v2/user/recharge_credits" \
-H "Content-Type: application/json" \
-d '{
"amount": 25,
"email": "customer@example.com",
"redirect_url": "https://example.com/samsar/callback"
}'
Fields:
amountis a positive USD amount. Recharge credits use the deployment's configured credits-per-dollar rate, which defaults to 100 credits per USD.emailmust be a valid email address.redirect_urlmust be HTTPS. The payment success webhook sends the token callback to this URL.
Success response (200)
{
"url": "https://checkout.stripe.com/c/pay/cs_test_...",
"checkoutSessionId": "cs_test_...",
"paymentStatusEndpoint": "/v2/user/payment_status",
"amount": 25,
"amountCents": 2500,
"credits": 2500,
"currency": "USD",
"redirectUrl": "https://example.com/samsar/callback"
}
On Stripe payment success, Samsar:
- creates a user for
emailif one does not already exist, - sends the new user a Samsar Studio email to confirm email and set a password,
- applies the purchased credits idempotently,
- mints a 10-day
authToken, - mints a rotating
refreshToken, - calls
redirect_urlwithauthToken,refreshToken, andexpiryDatequery parameters.
The webhook first attempts GET <redirect_url>?authToken=...&refreshToken=...&expiryDate=.... If that returns 404, it retries POST to the same URL with the same query parameters and a JSON body containing the token and payment metadata.
Clients should store authToken, refreshToken, and expiryDate. Poll for a replacement token when the app opens within 3 days of expiryDate.
Refresh token rotation
curl -X POST "https://api.samsar.one/v2/user/refresh_token" \
-H "Content-Type: application/json" \
-d '{"refreshToken": "srf_..."}'
Success response (200)
{
"tokenType": "Bearer",
"authToken": "eyJ...",
"refreshToken": "srf_new...",
"expiryDate": "2026-05-08T12:00:00.000Z",
"expiresInSeconds": 864000,
"refreshTokenExpiresAt": "2026-07-27T12:00:00.000Z"
}
/v2/user/refresh_token consumes the old refresh token and returns both a new 10-day auth token and a new refresh token. Reusing the previous refresh token returns 401.
Use the returned authToken as Authorization: Bearer <authToken> for /v2/user/credits, /v2/user/usage/logs, /v2/user/payment_status, and the rest of the internal /v2 routes.
App key credentials
Use /v2/users/app_key to mint one long-running APP_KEY for a user after the user is already authenticated with a Samsar API key or user auth token. /v2/user/app_key is accepted as an alias.
The client supplies a custom secret. Samsar returns the APP_KEY once, stores only an HMAC hash of the APP_KEY, and stores only a bcrypt hash of the secret. If an active APP_KEY already exists for the user, creation returns 409.
Create APP_KEY
curl -X POST "https://api.samsar.one/v2/users/app_key" \
-H "Authorization: Bearer $SAMSAR_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"secret": "a-client-generated-secret-at-least-32-chars"
}'
Success response (201)
{
"app_key": "sapp_...",
"appKey": "sapp_...",
"token_type": "AppKey",
"expires_at": "2027-04-28T12:00:00.000Z",
"app_key_record": {
"id": "appkey_...",
"appKeyPrefix": "sapp_abc12",
"appKeyLast4": "9xyz",
"status": "active",
"expiresAt": "2027-04-28T12:00:00.000Z",
"authScheme": "AppKey",
"authHeader": "Authorization: AppKey <APP_KEY>",
"secretHeader": "x-app-secret"
}
}
Use the APP_KEY on authenticated /v2 routes:
curl "https://api.samsar.one/v2/user/credits" \
-H "Authorization: AppKey $SAMSAR_APP_KEY" \
-H "x-app-secret: $SAMSAR_APP_SECRET"
If the APP_KEY expires, rotate it with the APP_KEY and APP_SECRET. The old APP_KEY is consumed and replaced; the APP_SECRET stays the same.
curl -X POST "https://api.samsar.one/v2/users/app_key/refresh" \
-H "Content-Type: application/json" \
-d '{
"app_key": "sapp_...",
"secret": "a-client-generated-secret-at-least-32-chars"
}'
GET /v2/users/app_key returns APP_KEY metadata for an authenticated user without returning the secret or full key. DELETE /v2/users/app_key revokes the active APP_KEY so the user can create a replacement.
Media generation
POST /v2/text_to_video and POST /v2/image_list_to_video accept the corresponding /v1/video/* request shapes, either nested under input or sent as a flat JSON payload.
| Setting | Accepted values |
|---|---|
inference_model / inferenceModel | gpt-5.6-sol (default), gemini-3.1-pro, QWEN3.8 (Qwen 3.8 Max for text and vision), kimi-k3 |
backingtrack_model / music_provider | LYRIA3, ELEVENLABS_MUSIC |
tts_model / tts_provider | ELEVENLABS, OPENAI, GOOGLE |
speakerOptions / speaker_options | Provider-specific speaker lists and Google speaker details |
video_model_sub_type is not part of the V2 request shape; put style direction in prompt. See Models & pricing for current model keys, per-second rates, and runtime-availability guidance.
Internal billing example
curl -X POST "https://api.samsar.one/v2/image_list_to_video" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"image_urls": ["https://cdn.example.com/a.png", "https://cdn.example.com/b.png"],
"video_model": "RUNWAYML",
"express_cta_generation": true,
"cta_url": "https://example.com/book",
"limit_single_narrator": true
}
}'
External-user billing example
curl -X POST "https://api.samsar.one/v2/image_list_to_video" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"external_user": {
"provider": "guidestination",
"unique_key": "traveler_123",
"external_user_id": "traveler_123"
},
"input": {
"image_urls": ["https://cdn.example.com/a.png", "https://cdn.example.com/b.png"],
"video_model": "KLINGIMGTOVID3PRO",
"add_narrator_avatar": true,
"add_footer_animation": true,
"footer_metadata": [
{ "url": "https://example.com/stop/1", "title": "Stop 1" },
{ "url": "https://example.com/stop/2", "title": "Stop 2" }
]
}
}'
For /v2/image_list_to_video, choose one outro mode and keep its fields together:
| Mode | Fields | Notes |
|---|---|---|
| QR CTA outro | cta_url | Samsar generates the centered QR outro. |
| Center-image CTA | outro_cta_image | Use { "top_text", "middle_image", "bottom_text" }. |
| Express CTA | express_cta_generation: true, one cta_url | Generates per-scene footer titles and outro text; adds 1 credit/sec. |
add_outro_animation defaults to true. limit_single_narrator keeps one narrator identity; add_narrator_avatar enables it automatically and adds 4 credits/sec. The V2 route uses the same model defaults and rates as the V1 image-list route, summarized in the video model rate table.
V2 image edit routes
The /v2/image/enhance, /v2/image/remove_branding, and /v2/image/add_image_set routes are thin aliases over the existing image API handlers and use the same image edit queue, validation, credit headers, and status polling behavior as /v1/image/*. Request bodies may be sent either directly or inside the standard V2 { "input": { ... } } wrapper.
Pricing is unchanged from the Image API: POST /v2/image/enhance charges 11 credits at 0.5k, 15 credits at 1k, 22 credits at 2k, and 29 credits at 4k; POST /v2/image/remove_branding charges 15 credits per request; POST /v2/image/add_image_set charges 15 × requested_images.
Step-controlled video generation
Use the /v2/video/step/* routes when a client needs step-aware generation and detailed timeline preview data. Step requests use the same express video models and stage billing rules as /v2/text_to_video and /v2/image_list_to_video.
Step routes support two render modes:
- 1-step express render: set
auto_render_full_video: trueandmanual_step_stages: []. The request generates the full video without waiting for user approval beforeai_video_generation. - 2-step render: set
auto_render_full_video: falseandmanual_step_stages: ["ai_video_generation"]. The request pauses after image/audio assets are ready, then waits forprocess_nextbefore the image-to-video stage.
When your request body uses the standard { "input": { ... } } wrapper, include these fields inside input. Clients may also send the same fields at the top level for compatibility. Only call process_next when the status response says waiting_for_process_next, requires_user_action, or can_process_next is true.
Step-controlled routes are internal-account routes. Authenticate with a Samsar API key, user auth token, or APP_KEY.
Create from text
curl -X POST "https://api.samsar.one/v2/video/step/text_to_video" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"auto_render_full_video": true,
"manual_step_stages": [],
"input": {
"prompt": "A 20 second launch teaser for a new travel app",
"image_model": "GPTIMAGE2",
"video_model": "RUNWAYML",
"duration": 20,
"aspect_ratio": "16:9",
"enable_subtitles": true,
"auto_render_full_video": true,
"manual_step_stages": []
},
"webhookUrl": "https://example.com/video-webhook"
}'
Create from an image
curl -X POST "https://api.samsar.one/v2/video/step/image_to_video" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"auto_render_full_video": false,
"manual_step_stages": ["ai_video_generation"],
"input": {
"image_url": "https://cdn.example.com/product-frame.png",
"prompt": "Turn this product frame into a cinematic ad",
"video_model": "KLINGIMGTOVID3PRO",
"aspect_ratio": "16:9",
"enable_subtitles": true,
"auto_render_full_video": false,
"manual_step_stages": ["ai_video_generation"]
}
}'
The response includes the normal request_id / session_id plus a step object. For 1-step requests, poll the detailed step status route until the final video is complete. For 2-step requests, poll until the response asks for user action, inspect current_step_resources, optionally edit resources through the relevant session/resource APIs, then call process_next.
curl "https://api.samsar.one/v2/video/step/65f1234567890abcdef12345/status" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
{
"request_id": "65f1234567890abcdef12345",
"session_id": "65f1234567890abcdef12345",
"status": "COMPLETED",
"step_status": "COMPLETED",
"current_step": "image_generation",
"current_step_label": "Images",
"next_step": "ai_video_generation",
"waiting_for_process_next": true,
"requires_user_action": true,
"can_process_next": true,
"manual_step_stages": ["ai_video_generation"],
"process_next_url": "/v2/video/step/process_next",
"current_step_resources": {
"step": "image_generation",
"label": "Images",
"status": "COMPLETED",
"completed_at": "2026-05-07T10:30:00.000Z",
"resources": {
"layers": [
{
"index": 0,
"prompt": "Wide product shot with cinematic lighting",
"selected_image_url": "https://cdn.samsar.one/generated/layer-0.png"
}
]
}
},
"completed_step_resources": {
"prompt_generation": {
"completed_at": "2026-05-07T10:27:00.000Z",
"resources": {
"input_prompt": "A 20 second launch teaser for a new travel app",
"scenes": []
}
},
"image_generation": {
"completed_at": "2026-05-07T10:30:00.000Z",
"resources": {
"layers": []
}
}
}
}
For preview playback before the final render, use status_detailed. It returns the same top-level status fields plus status_detail_schema and a normalized session object. The session payload includes only the data needed to reconstruct the current playable stage on the client: scene layers, audioLayers, globalAudioLayers, globalVideos, and final result metadata when available. Layer timing is normalized to startTime and endTime; the response does not expose internal fields such as durationOffset.
curl "https://api.samsar.one/v2/video/step/65f1234567890abcdef12345/status_detailed" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
Example response after ai_video_generation has completed, before lip sync, effects, or the final render:
Full detailed-status payloadLayers, audio, stages, and preview metadata
{
"request_id": "65f1234567890abcdef12345",
"session_id": "65f1234567890abcdef12345",
"status": "COMPLETED",
"type": "video",
"provider": "RUNWAYML",
"step_status": "COMPLETED",
"current_step": "ai_video_generation",
"current_step_label": "AI video",
"next_step": "lip_sync_generation",
"waiting_for_process_next": true,
"process_next_url": "/v2/video/step/process_next",
"status_detail_schema": "video_session_preview.v1",
"session": {
"id": "65f1234567890abcdef12345",
"requestId": "65f1234567890abcdef12345",
"type": "video",
"routeType": "step",
"aspectRatio": "16:9",
"framesPerSecond": 30,
"duration": 12,
"language": "en",
"hasSubtitles": true,
"hasFooter": false,
"inputPrompt": "A 12 second launch teaser for a new travel app",
"generationType": "text_to_video",
"provider": "RUNWAYML",
"currentStage": "lip_sync_generation",
"previewStage": "ai_video_generation",
"completedStages": [
"prompt_generation",
"image_generation",
"speech_generation",
"music_generation",
"audio_generation",
"ai_video_generation"
],
"stages": {
"prompt_generation": "COMPLETED",
"image_generation": "COMPLETED",
"speech_generation": "COMPLETED",
"music_generation": "COMPLETED",
"audio_generation": "COMPLETED",
"ai_video_generation": "COMPLETED",
"lip_sync_generation": "INIT",
"sound_effect_generation": "INIT",
"video_generation": "INIT"
},
"layers": [
{
"index": 0,
"id": "65f1234567890abcdef0001",
"startTime": 0,
"endTime": 6,
"duration": 6,
"prompt": "Wide product shot with cinematic lighting",
"videoPrompt": "Slow push-in over the mobile booking screen",
"image": {
"status": "COMPLETED",
"url": "https://cdn.samsar.one/generated/scene-0.png"
},
"aiVideo": {
"status": "COMPLETED",
"url": "https://cdn.samsar.one/generated/scene-0-ai.mp4"
},
"preview": {
"stage": "ai_video_generation",
"type": "video",
"url": "https://cdn.samsar.one/generated/scene-0-ai.mp4"
}
},
{
"index": 1,
"id": "65f1234567890abcdef0002",
"startTime": 6,
"endTime": 12,
"duration": 6,
"prompt": "Traveler checking into a hotel from the app",
"videoPrompt": "Handheld travel lifestyle shot with warm daylight",
"image": {
"status": "COMPLETED",
"url": "https://cdn.samsar.one/generated/scene-1.png"
},
"aiVideo": {
"status": "COMPLETED",
"url": "https://cdn.samsar.one/generated/scene-1-ai.mp4"
},
"preview": {
"stage": "ai_video_generation",
"type": "video",
"url": "https://cdn.samsar.one/generated/scene-1-ai.mp4"
}
}
],
"audioLayers": [
{
"index": 0,
"type": "speech",
"status": "COMPLETED",
"startTime": 0,
"endTime": 6,
"duration": 6,
"url": "https://cdn.samsar.one/generated/scene-0-speech.mp3",
"speaker": "alloy",
"connectedLayerIndex": 0
},
{
"index": 1,
"type": "speech",
"status": "COMPLETED",
"startTime": 6,
"endTime": 12,
"duration": 6,
"url": "https://cdn.samsar.one/generated/scene-1-speech.mp3",
"speaker": "alloy",
"connectedLayerIndex": 1
}
],
"globalAudioLayers": [
{
"index": 0,
"type": "music",
"status": "COMPLETED",
"startTime": 0,
"endTime": 12,
"duration": 12,
"url": "https://cdn.samsar.one/generated/music-bed.mp3",
"volume": 0.25
}
],
"result": {
"hasSubtitles": true,
"hasFooter": false,
"language": "en"
}
}
}
Continue to the next stage:
curl -X POST "https://api.samsar.one/v2/video/step/65f1234567890abcdef12345/process_next" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
Step status values:
INIT: the step request exists but has not started its first tracked stage.PENDING: the current stage is running.COMPLETED: the final video stage is complete, or a 2-step request is paused at a manual checkpoint and waiting forprocess_next.FAILED: the request failed.
The stage sequence is prompt_generation, image_generation, speech_generation, music_generation, ai_video_generation, lip_sync_generation, sound_effect_generation, narrator_avatar_generation, and video_generation.
Translate example
curl -X POST "https://api.samsar.one/v2/translate_video" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"language": "es",
"enable_subtitles": false,
"translate_outro": true,
"translate_footer": true
},
"webhookUrl": "https://example.com/video-webhook"
}'
/v2/translate_video and /v2/retranslate_video accept the same payload as /v1/video/translate_video. enable_subtitles defaults to false; when true, Samsar regenerates translated transcript/subtitle text. translate_outro and translate_footer default to true and localize stored generated-outro CTA text and footer CTA text when those metadata fields exist. The translate route no longer accepts outro_image_url; use /v2/update_outro_image for outro replacement.
Footer update example
curl -X POST "https://api.samsar.one/v2/update_footer_image" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"cta_text": "Scan to book",
"cta_logo": "https://cdn.example.com/logo-white.png",
"cta_url": "https://example.com/book"
}
}'
Set remove_footer: true in the input object to clone the session and remove all scene footers instead. Like /v1/video/update_footer_image, the V2 route queues only footer/frame/video work and charges 75 credits/request.
Deep clone example
curl -X POST "https://api.samsar.one/v2/video/clone" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4"
},
"webhookUrl": "https://example.com/video-webhook"
}'
/v2/video/clone is free for an authenticated user who owns the source session. It copies the session data and local generated assets into a new session, marks all upstream generation stages complete, and queues only the final video_generation stage so the clone receives its own rendered video path and URL. Poll /v2/status?request_id=<new_session_id> until it returns the standard completed video payload with result_url, has_subtitles, and result_language.
Reroll layers example
curl -X POST "https://api.samsar.one/v2/reroll-layers" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"layer_indexes": [3, 6, 11]
},
"webhookUrl": "https://example.com/video-webhook"
}'
/v2/reroll-layers and /v2/video/reroll-layers update the same owned session in place. The selected 1-based layers restart from image generation, then required AI-video and lip-sync/sound-effect work runs for those layers before frame/outro/final video generation reruns for the whole session. Credits are the selected image-generation costs plus the selected layers' AI-video stage credits; send quote_only: true in input to estimate without charging.
External video provider routes
Use /v2/external/video/* when a remote deployment needs hosted Samsar generation through a Samsar API key. These are standalone provider-style endpoints, separate from /v2/video/step/*.
Supported routes:
POST /v2/external/video/text_to_videoPOST /v2/external/video/image_to_videoPOST /v2/external/video/direct_image_to_videoPOST /v2/external/video/narrative_to_videoPOST /v2/external/video/text_to_interactive_videoPOST /v2/external/video/lip_syncPOST /v2/external/video/sound_effectGETorPOST /v2/external/video/statusGETorPOST /v2/external/video/{request_id}/statusGETorPOST /v2/external/video/status_detailedGETorPOST /v2/external/video/{request_id}/status_detailed
text_to_video accepts the same express text input shape as /v2/text_to_video. image_to_video requires exactly one public image_url (or a one-item image_urls array) plus video_model. narrative_to_video accepts a completed singular or branched narrative_request_id, inherits its prompt and duration, and skips prompt generation; see Render a narrative as video. lip_sync requires public video_url and audio_url values. sound_effect requires a public video_url and prompt. Raw media bytes, data URLs, local filesystem paths, localhost URLs, and private network URLs are not accepted for hosted external video-to-video routes.
For a branched narrative-to-video request, both status routes return a normalized branching manifest. The normal status response is compact and contains tree choices, aggregate path progress, per-path frame/video status, and any completed path result. Detailed status adds path timelines that reference the shared session.layers and session.audioLayers catalogs by ID; it does not return internal frame lists. A top-level COMPLETED result is published only after every required leaf video is ready.
Assistant and utilities
Assistant and utility routes use the same actor resolution behavior as the rest of /v2. Use them when a mixed internal/external-user client needs assistant completions, assistant sessions, usage charges, or cleaned-text embeddings through one API prefix.
Supported assistant and utility routes:
POST /v2/assistant/set_system_promptPOST /v2/assistant/completionPOST /v2/external/chatPOST /v2/external/chat/completionsPOST /v2/external/assistantPOST /v2/external/assistant/completionsGET /v2/external/chat/statusGET /v2/external/chat/completions/statusGET /v2/external/assistant/statusPOST /v2/external/moderationPOST /v2/external/moderationsPOST /v2/external/audio/text_to_speechPOST /v2/external/audio/text_to_musicPOST /v2/external/audio/text_to_sound_effectPOST /v2/external/audio/transcript_alignGETorPOST /v2/external/audio/statusPOST /v2/external/embeddingsPOST /v2/utils/assistant_sessionPOST /v2/utils/usage_chargePOST /v2/generate_embeddings_from_plain_text
External chat and assistant aliases accept OpenAI-compatible text and vision messages and use the Chat API external inference pricing table. They execute synchronously by default. Set response_mode: "polling" for an HTTP 202 queued response, persist request_id, and poll one of the status routes until COMPLETED or FAILED. A stable client_request_id makes submission retry-safe after a network reset and can also be used for status lookup. The default execution timeout is 10 minutes and request records are retained for 24 hours. See External assistant and chat completions for the complete request and response contract.
External moderation routes accept OpenAI-compatible moderation input and do not charge credits. Hosted audio charges 1 credit/request for text to speech or sound effects and 2 credits/request for text to music; music accepts ELEVENLABS_MUSIC and LYRIA3. Transcript alignment is usage based, and external embeddings cost 100 credits per 1M tokens.
Request status and publishing
Use GET /v2/status with any request_id returned by a V2 generation route. Status resolution accepts external request ids, normalized external ids, and internal video session ids where available.
Use GET /v2/status_detailed or POST /v2/status_detailed when a video client needs preview assets before the final render finishes. Detailed status is additive: every existing /v2/status field remains unchanged, and video requests also include status_detail_schema: "video_session_preview.v1" with a normalized session preview payload. Use session.previewStage to choose the furthest playable stage, then render scene clips from session.layers[].preview, speech from session.audioLayers, background audio from session.globalAudioLayers, and final output metadata from session.result once available.
When image, AI-video, lip-sync, sound-effect, or final video generation reaches a terminal failure, /v2/status and /v2/status_detailed promote the exact stored provider/model message to generationError, expressGenerationError, error, and message. Detailed status also keeps the error on the failed layer asset (session.layers[].imageSession.generationError, aiVideoGenerationError, lipSyncVideoGenerationError, or soundEffectVideoGenerationError). Clients should display this message as-is for the terminal failure. Stale errors from a layer that is actively retrying or has since completed are not treated as terminal.
Branched video sessions additionally include branching.schema: "branched_video_status.v1" on both status responses. Use branching.summary for aggregate leaf-render progress, branching.tree.choice_points for interactive choices, and branching.paths for path-specific frame/video state. On detailed status, each path also includes a normalized timeline and audio_timeline; resolve their layer_id and audio_layer_id references against the shared assets in session.layers and session.audioLayers. This keeps preview responses normalized even when several paths share the same parent scenes.
When all leaf renders complete, branching.outputs.ready becomes true, branching.outputs.paths maps each leaf path_id to its final URL, top-level result_url identifies the default path, and ordered result_urls contains all leaf videos. The older branch_results mapping remains available for compatibility. See Render a narrative as video for the complete response and client-switching flow.
Publishing and archive routes operate on the resolved actor's accessible assets:
POST /v2/archivePOST /v2/publish