LLM_EXPORT_VERSION: 1
GENERATED_AT_UTC: 2026-07-16T15:11:43.608Z
DOC_COUNT: 12
=== DOCUMENT START ===
SOURCE_PATH: docs/api.mdx
METADATA:
title: Video API
description: Generate, translate, join, clone, and customize videos through SamsarOne's credit-aware video endpoints.
image: img/og/video-api.png
sidebar_position: 1
slug: /video
sidebar_label: Video API
PRIMARY_HEADING: Video API
CONTENT_START
---
title: Video API
description: "Generate, translate, join, clone, and customize videos through SamsarOne's credit-aware video endpoints."
image: img/og/video-api.png
sidebar_position: 1
slug: /video
sidebar_label: Video API
---
import DocTopActions from '@site/src/components/DocTopActions';
import PlaygroundRail from '@site/src/components/PlaygroundRail';
# Video API
Queue a sample video request with your API key and watch status updates without leaving the docs.
- Base URL: `https://api.samsar.one/v1`
- Auth: `Authorization: Bearer `
- Content-Type: `application/json`
- Webhooks are optional; otherwise poll `GET /status?request_id=...`.
- Credits are charged when a request is accepted; where available, responses include `x-credits-charged` and `x-credits-remaining`.
## Pricing
Credits are charged when a request is accepted. Standard `/video/text_to_video` and `/video/image_list_to_video` express models are billed per rendered second by model. Image-list `add_narrator_avatar` adds an optional **4 credits/sec** avatar generation stage, and image-list `express_cta_generation` adds an optional **1 credit/sec** CTA text-generation stage. Video model keys outside the documented supported values are not supported as default route model values. `/video/translate_video` and `/video/join_videos` are billed at a flat **3 credits/sec** based on the source/joined video duration. `/video/add_outro_image`, `/video/update_outro_image`, `/video/update_footer_image`, `/external_users/add_outro_image`, `/external_users/update_outro_image`, and `/external_users/update_footer_image` are billed at a flat **75 credits/request**. `/video/reroll-layers` bills the selected image-generation requests plus the selected layers' AI-video stage credits. `/video/clone` and `/v2/video/clone` do not charge additional credits.
| `video_model` | Text-to-video credits/sec | Unit options (seconds) |
| ------------------- | ------------------------- | ---------------------- |
| `VEO3.1I2V` | 60 | `6`, `8` |
| `VEO3.1I2VFAST` | 36 | `6`, `8` |
| `COSMOS3SUPERI2V` (Nvidia Cosmos 3) | 20 | `5`, `10`, `15` |
| `SEEDANCEI2V` (Seedance 1.5) | 30 | `5`, `10` |
| `KLINGIMGTOVID3PRO` | 36 | `5`, `10` |
| `KLINGIMGTOVIDTURBO` | 36 | `5`, `10` |
| `HAPPYHORSEI2V` | 36 | `5`, `10`, `15` |
| `RUNWAYML` | 30 | `5`, `10` |
For each listed standard model, the per-second price is split into these components:
| Component | Credits/sec |
| --- | ---: |
| Pipeline | 4 |
| Inference | 4 |
| Image Gen / Edit | 2 |
| Speech | 2 |
| Music | 2 |
| Effects and lipsync | 2 |
| Video | Remainder by model |
That gives this per-model distribution:
| `video_model` | Pipeline | Inference | Image Gen / Edit | Speech | Music | Effects and lipsync | Video | Total |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| `VEO3.1I2V` | 4 | 4 | 2 | 2 | 2 | 2 | 44 | 60 |
| `VEO3.1I2VFAST` | 4 | 4 | 2 | 2 | 2 | 2 | 20 | 36 |
| `COSMOS3SUPERI2V` | 4 | 4 | 2 | 2 | 2 | 2 | 4 | 20 |
| `SEEDANCEI2V` | 4 | 4 | 2 | 2 | 2 | 2 | 14 | 30 |
| `KLINGIMGTOVID3PRO` | 4 | 4 | 2 | 2 | 2 | 2 | 20 | 36 |
| `KLINGIMGTOVIDTURBO` | 4 | 4 | 2 | 2 | 2 | 2 | 20 | 36 |
| `HAPPYHORSEI2V` | 4 | 4 | 2 | 2 | 2 | 2 | 20 | 36 |
| `RUNWAYML` | 4 | 4 | 2 | 2 | 2 | 2 | 14 | 30 |
**Text-to-video formula**
`credits = duration_seconds × model_rate`
**Image list to video formula**
`credits = duration_seconds × model_rate`, using the same model rates as the text-to-video table above. Add `duration_seconds × 4` when `add_narrator_avatar` is true and `duration_seconds × 1` when `express_cta_generation` is true.
**Translate video formula**
`credits = duration_seconds × 3`
**Join videos formula**
`credits = duration_seconds × 3`
**Clone video**
`POST /video/clone` and `POST /v2/video/clone` currently do not charge additional credits.
**Reroll layers formula**
`credits = selected_layer_count × image_generation_price + selected_layer_duration_seconds × ai_video_stage_rate`. Quote-only requests do not charge credits.
**Add outro image formula**
`credits = 75`
**Update outro image formula**
`credits = 75`
**Update footer image formula**
`credits = 75`
**Remove subtitles**
`POST /video/remove_subtitles` currently does not charge additional credits.
**Add subtitles**
`POST /video/add_subtitles` currently does not charge additional credits.
**Cancel render**
`POST /video/cancel_render` currently does not charge additional credits.
Example: a 10-second `RUNWAYML` text-to-video render costs `300` credits.
## GET /video/supported_models
Fetch the currently supported `image_model` and `video_model` keys for `POST /video/text_to_video`.
**Request**
```bash
curl "https://api.samsar.one/v1/video/supported_models"
```
**Success response (200)**
```json
{
"IMAGE_MODELS": [
{ "label": "GPTIMAGE2", "value": "GPTIMAGE2", "basePrice": 46 },
{ "label": "NANOBANANA2", "value": "NANOBANANA2", "basePrice": 23 },
{ "label": "NanoBanana Pro", "value": "NANOBANANAPRO", "basePrice": 15 },
{ "label": "SEEDREAM", "value": "SEEDREAM", "basePrice": 15 }
],
"VIDEO_MODELS": [
{
"label": "VEO3.1I2V",
"value": "VEO3.1I2V",
"basePrice": 60,
"pricingDistribution": {
"pipeline": 4,
"inference": 4,
"image_gen_edit": 2,
"speech": 2,
"music": 2,
"effects_and_lipsync": 2,
"video": 44,
"total": 60,
"optionalAddons": {
"express_cta_generation": 1
}
}
},
{
"label": "VEO3.1I2VFAST",
"value": "VEO3.1I2VFAST",
"basePrice": 36,
"pricingDistribution": {
"pipeline": 4,
"inference": 4,
"image_gen_edit": 2,
"speech": 2,
"music": 2,
"effects_and_lipsync": 2,
"video": 20,
"total": 36,
"optionalAddons": {
"express_cta_generation": 1
}
}
},
{
"label": "Nvidia Cosmos 3",
"value": "COSMOS3SUPERI2V",
"basePrice": 20,
"pricingDistribution": {
"pipeline": 4,
"inference": 4,
"image_gen_edit": 2,
"speech": 2,
"music": 2,
"effects_and_lipsync": 2,
"video": 4,
"total": 20,
"optionalAddons": {
"express_cta_generation": 1
}
}
},
{
"label": "Seedance 1.5",
"value": "SEEDANCEI2V",
"basePrice": 30,
"pricingDistribution": {
"pipeline": 4,
"inference": 4,
"image_gen_edit": 2,
"speech": 2,
"music": 2,
"effects_and_lipsync": 2,
"video": 14,
"total": 30,
"optionalAddons": {
"express_cta_generation": 1
}
}
},
{
"label": "KLINGIMGTOVID3PRO",
"value": "KLINGIMGTOVID3PRO",
"basePrice": 36,
"pricingDistribution": {
"pipeline": 4,
"inference": 4,
"image_gen_edit": 2,
"speech": 2,
"music": 2,
"effects_and_lipsync": 2,
"video": 20,
"total": 36,
"optionalAddons": {
"express_cta_generation": 1
}
}
},
{
"label": "KLINGIMGTOVIDTURBO",
"value": "KLINGIMGTOVIDTURBO",
"basePrice": 36,
"pricingDistribution": {
"pipeline": 4,
"inference": 4,
"image_gen_edit": 2,
"speech": 2,
"music": 2,
"effects_and_lipsync": 2,
"video": 20,
"total": 36,
"optionalAddons": {
"express_cta_generation": 1
}
}
},
{
"label": "Happy Horse 1.1 I2V",
"value": "HAPPYHORSEI2V",
"basePrice": 36,
"pricingDistribution": {
"pipeline": 4,
"inference": 4,
"image_gen_edit": 2,
"speech": 2,
"music": 2,
"effects_and_lipsync": 2,
"video": 20,
"total": 36,
"optionalAddons": {
"express_cta_generation": 1
}
}
},
{
"label": "RunwayML Gen 4.5",
"value": "RUNWAYML",
"basePrice": 30,
"pricingDistribution": {
"pipeline": 4,
"inference": 4,
"image_gen_edit": 2,
"speech": 2,
"music": 2,
"effects_and_lipsync": 2,
"video": 14,
"total": 30,
"optionalAddons": {
"express_cta_generation": 1
}
}
}
]
}
```
- `IMAGE_MODELS` and `VIDEO_MODELS` are generated from the server-side text-to-video allowlist.
- Each standard priced item includes `label` (display text), `value` (request key), `basePrice` (lowest configured per-unit price for that model), and `pricingDistribution` in credits/sec. `pricingDistribution.optionalAddons.express_cta_generation` is the image-list express CTA surcharge in credits/sec. Custom image-to-video adapters are configured through the top-level custom model configuration, not as a standard `VIDEO_MODELS` item.
- Use each item’s `value` directly as `image_model` and `video_model` in `POST /video/text_to_video`.
- This endpoint does not require authentication.
## POST /video/text_to_video
Submit a prompt-only request to start video generation. Webhooks are optional.
**Request body**
```json
{
"input": {
"prompt": "Required text, max 1000 chars",
"image_model": "GPTIMAGE2 | NANOBANANA2 | NANOBANANAPRO | SEEDREAM",
"video_model": "VEO3.1I2V | VEO3.1I2VFAST | COSMOS3SUPERI2V | SEEDANCEI2V | KLINGIMGTOVID3PRO | KLINGIMGTOVIDTURBO | HAPPYHORSEI2V | RUNWAYML",
"duration": 30,
"tone": "grounded | cinematic",
"aspect_ratio": "16:9 | 9:16",
"language": "auto | en | es | fr | de | pt | it | nl | sv | hi | ar | ru | ja | ko | th | zh",
"backingtrack_model": "LYRIA3 | ELEVENLABS_MUSIC",
"tts_model": "ELEVENLABS | OPENAI | GOOGLE",
"speakerOptions": { "openAISpeakers": ["nova"] },
"inference_model": "gpt-5.5 | gemini-3.1-pro",
"enable_subtitles": true,
"font_key": "Poppins",
"outro_image_url": "https://cdn.example.com/outro.png",
"generate_outro_image": false,
"cta_url": "https://example.com/book",
"cta_text_top": "Scan to book",
"cta_text_bottom": "Limited availability",
"cta_logo": "https://cdn.example.com/logo-white.png",
"outro_cta_image": {
"top_text": "Shop the drop",
"middle_image": { "url": "https://cdn.example.com/drop-logo.png" },
"bottom_text": "Limited availability"
},
"add_footer_animation": true,
"footer_metadata": [
{ "url": "https://example.com/book", "title": "Book now" }
]
},
"webhookUrl": "https://example.com/webhook"
}
```
- `prompt`, `image_model`, `video_model`, `duration` are required; `duration` ≤ 240 seconds.
- `language` is optional; pass `auto` (default) or one of the listed language codes.
- `backingtrack_model` optional; selects the express backing track generator. Use `LYRIA3` for Google Lyria 3 Pro native music generation or `ELEVENLABS_MUSIC` for ElevenLabs Music. Compatibility aliases include `backing_track_model`, `backingTrackModel`, `music_provider`, and `musicProvider`; legacy `LYRIA2` requests are treated as `LYRIA3`.
- `tts_model` optional; selects the speaker library used for text-to-speech assignment. Use `ELEVENLABS`, `OPENAI`, or `GOOGLE`. Compatibility aliases include `ttsModel`, `tts_provider`, and `ttsProvider`. When omitted, Samsar uses the default speaker assignment behavior.
- `speakerOptions` optional; compatibility alias: `speaker_options`. Use it with `tts_model` to constrain assignment to `openAISpeakers`, `elevenLabsSpeakers`, or `googleSpeakers`. When provided, request speaker options override account speaker settings for that request. Google TTS requests should include `googleSpeakerDetails` entries with `value` or `voiceId`, `languageCode` or `languageCodes`, and gender metadata such as `Gender` or `ssmlGender`.
- `inference_model` optional; selects the inference model used for render planning. Use `gpt-5.5` for GPT 5.5 or `gemini-3.1-pro` for Gemini 3.1 Pro. Compatibility alias: `inferenceModel`. When omitted, Samsar uses the account default inference model.
- `enable_subtitles` optional; defaults to `true`. Set to `false` to skip subtitle generation entirely.
- `font_key` optional; must be supported for the chosen `language`. It sets both subtitle and speaker fonts. Use `GET /supported_fonts` or [/fonts](/fonts) to preview choices.
- Provided outro image mode: set `outro_image_url` to append a custom outro frame. `add_outro_animation` is optional; when true, it applies a fade overlay during the outro. `add_outro_focus_area` requires `add_outro_animation: true` and a valid `outro_focust_area`.
- Generated outro image mode: set `generate_outro_image: true` and provide either `cta_url` for a QR center image or `outro_cta_image` for a supplied center logo/CTA image. `outro_cta_image` uses `{ "top_text", "middle_image", "bottom_text" }`; `middle_image` accepts a public URL (`{ "url" }`, `{ "image_url" }`) or image data (`{ "data_url" }`, `{ "image_data", "mime_type" }`). Top-level `cta_text_top` and `cta_text_bottom` remain supported for generated outros. This mode automatically enables `add_outro_animation`, `add_outro_focus_area`, and computes `outro_focust_area`.
- Footer QR mode: set `add_footer_animation: true` and provide `footer_metadata` with `{ "url", "title" }` items. A single item is applied to every generated scene; multiple items map by generated scene index. The frame processor renders the bottom CTA section only on layers where this option is enabled.
- Use only one outro mode in a request. If `generate_outro_image` is true, omit `outro_image_url`, `add_outro_animation`, `add_outro_focus_area`, and `outro_focust_area` because Samsar derives those values.
- `outro_focust_area` / `outro_focus_area` values are `{ x, y, width, height }` within the output canvas (`16:9` is `1792x1024`, `9:16` is `1024x1792`).
- `video_model_sub_type` is not part of the request shape; describe style direction in `prompt` and `tone`.
- Request bodies can be nested under `input` (recommended) or sent as a flat JSON payload.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/video/text_to_video \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"prompt": "A timelapse of clouds over a futuristic city at sunrise",
"duration": 30,
"image_model": "GPTIMAGE2",
"video_model": "RUNWAYML",
"tone": "grounded",
"aspect_ratio": "16:9",
"language": "en",
"enable_subtitles": true,
"font_key": "Poppins",
"generate_outro_image": true,
"cta_url": "https://example.com/tour",
"cta_text_top": "Scan for the tour",
"cta_text_bottom": "Book a demo",
"add_footer_animation": true,
"footer_metadata": [
{ "url": "https://example.com/tour", "title": "Book a demo" }
]
}
}'
```
**Sample request: generate CTA image outro with a center logo**
```bash
curl -X POST https://api.samsar.one/v1/video/text_to_video \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"prompt": "A cinematic launch teaser for a new product line",
"duration": 20,
"image_model": "GPTIMAGE2",
"video_model": "RUNWAYML",
"aspect_ratio": "9:16",
"generate_outro_image": true,
"outro_cta_image": {
"top_text": "Shop the drop",
"middle_image": { "url": "https://cdn.example.com/drop-logo.png" },
"bottom_text": "Limited availability"
}
}
}'
```
**Success response (200)**
```json
{
"request_id": "vid_a1b2c3d4",
"session_id": "vid_a1b2c3d4"
}
```
**Error responses**
- `400` invalid or missing fields (empty prompt, unsupported models, duration > 240, etc.)
- `401` invalid API key
## POST /video/image_list_to_video
Create a video from an ordered list of images. Samsar builds a narrative from the images and optional metadata, then renders the video using `VEO3.1I2V`, `VEO3.1I2VFAST`, `COSMOS3SUPERI2V`, `SEEDANCEI2V`, `KLINGIMGTOVID3PRO`, `KLINGIMGTOVIDTURBO`, `HAPPYHORSEI2V`, or `RUNWAYML`. If `video_model` is omitted, it defaults to `RUNWAYML`.
**Request body**
```json
{
"input": {
"image_urls": [
"https://cdn.example.com/frame1.png",
"https://cdn.example.com/frame2.png"
],
"prompt": "Optional high-level direction",
"metadata": { "project": "launch_trailer" },
"video_model": "VEO3.1I2V | VEO3.1I2VFAST | COSMOS3SUPERI2V | SEEDANCEI2V | KLINGIMGTOVID3PRO | KLINGIMGTOVIDTURBO | HAPPYHORSEI2V | RUNWAYML",
"image_model": "GPTIMAGE2 | NANOBANANA2 | NANOBANANAPRO | SEEDREAM",
"aspect_ratio": "16:9 | 9:16",
"language": "auto | en | es | fr | de | pt | it | nl | sv | hi | ar | ru | ja | ko | th | zh",
"backingtrack_model": "LYRIA3 | ELEVENLABS_MUSIC",
"tts_model": "ELEVENLABS | OPENAI | GOOGLE",
"speakerOptions": { "openAISpeakers": ["nova"] },
"inference_model": "gpt-5.5 | gemini-3.1-pro",
"enable_subtitles": true,
"font_key": "Poppins",
"limit_single_narrator": false,
"add_narrator_avatar": false,
"generate_outro_image": false,
"express_cta_generation": false,
"cta_url": "https://example.com/book",
"cta_text_top": "Scan to book",
"cta_text_bottom": "Limited availability",
"cta_logo": "https://cdn.example.com/logo-white.png",
"outro_cta_image": {
"top_text": "Shop the drop",
"middle_image": { "url": "https://cdn.example.com/drop-logo.png" },
"bottom_text": "Limited availability"
},
"add_footer_animation": false,
"footer_metadata": [
{ "url": "https://example.com/blue-lagoon", "title": "Blue Lagoon Tour" }
]
},
"webhookUrl": "https://example.com/webhook"
}
```
- `image_urls` required; non-empty array of URL strings or objects containing `image_url`, `imageUrl`, `url`, `src`, `enhanced_url`, or `enhancedUrl`. Object entries may also include `title`, `image_text`, `activity_title`, `name`, or `label` for narration context.
- `metadata` optional plain object forwarded into the narrative builder.
- `prompt` optional text used to guide pacing/tone.
- `video_model` optional; supported values are `VEO3.1I2V`, `VEO3.1I2VFAST`, `COSMOS3SUPERI2V`, `SEEDANCEI2V`, `KLINGIMGTOVID3PRO`, `KLINGIMGTOVIDTURBO`, `HAPPYHORSEI2V`, and `RUNWAYML`. Defaults to `RUNWAYML`.
- `aspect_ratio` optional; supports `16:9` and `9:16`. Omitted or unsupported values fall back to `16:9`.
- `language` optional; defaults to `auto` for detection.
- `backingtrack_model` optional; selects the express backing track generator. Use `LYRIA3` for Google Lyria 3 Pro native music generation or `ELEVENLABS_MUSIC` for ElevenLabs Music. Compatibility aliases include `backing_track_model`, `backingTrackModel`, `music_provider`, and `musicProvider`; legacy `LYRIA2` requests are treated as `LYRIA3`.
- `tts_model` optional; selects the speaker library used for text-to-speech assignment. Use `ELEVENLABS`, `OPENAI`, or `GOOGLE`. Compatibility aliases include `ttsModel`, `tts_provider`, and `ttsProvider`. When omitted, Samsar uses the default speaker assignment behavior.
- `speakerOptions` optional; compatibility alias: `speaker_options`. Use it with `tts_model` to constrain assignment to `openAISpeakers`, `elevenLabsSpeakers`, or `googleSpeakers`. When provided, request speaker options override account speaker settings for that request. Google TTS requests should include `googleSpeakerDetails` entries with `value` or `voiceId`, `languageCode` or `languageCodes`, and gender metadata such as `Gender` or `ssmlGender`.
- `inference_model` optional; selects the inference model used for render planning. Use `gpt-5.5` for GPT 5.5 or `gemini-3.1-pro` for Gemini 3.1 Pro. Compatibility alias: `inferenceModel`. When omitted, Samsar uses the account default inference model.
- `enable_subtitles` optional; defaults to `true`. Set to `false` to skip subtitle generation entirely.
- `font_key` optional; must be supported for the chosen `language`. It sets both subtitle and speaker fonts.
- `limit_single_narrator` optional boolean; defaults to `false`. When true, Samsar keeps all narration under one narrator identity.
- `add_narrator_avatar` optional boolean; defaults to `false`. When true, Samsar automatically enables `limit_single_narrator`, generates an influencer-style human narrator avatar suitable for the image-list narrative, and overlays the avatar video bottom-center or centered in the footer row when footer metadata is present.
- Provided outro image mode: set `outro_image_url` to append a custom outro frame. `add_outro_animation` is optional; when true, it applies a fade-to-black overlay during the outro. `add_outro_focus_area` requires `add_outro_animation: true` and a valid `outro_focust_area`.
- Generated outro image mode: set `generate_outro_image: true` and provide either `cta_url` for a QR center image or `outro_cta_image` for a supplied center logo/CTA image. `outro_cta_image` uses `{ "top_text", "middle_image", "bottom_text" }`; `middle_image` accepts a public URL (`{ "url" }`, `{ "image_url" }`) or image data (`{ "data_url" }`, `{ "image_data", "mime_type" }`). Top-level `cta_text_top` and `cta_text_bottom` remain supported for generated outros. This mode automatically enables `add_outro_animation`, `add_outro_focus_area`, and computes `outro_focust_area`.
- Footer QR mode: set `add_footer_animation: true` and provide `footer_metadata` with one `{ "url", "title" }` item per image scene. Samsar generates one QR image per scene in the frame processor and renders a footer section on every frame with the title on the left and QR code on the right.
- Express CTA generation mode: set `express_cta_generation: true` with one `cta_url` and omit `cta_text_top`, `cta_text_bottom`, and `footer_metadata`. Samsar generates concise per-scene footer CTA titles from the image scene payload and clean outro CTA text from the session context, then normalizes the request into the same generated-outro and footer payload shape before rendering. Compatibility aliases include `expressCtaGeneration`, `auto_generate_cta_text`, `autoGenerateCtaText`, `generate_cta_texts`, and `generateCtaTexts`.
- Use only one outro mode in a request. If `generate_outro_image` or `express_cta_generation` is true, omit `outro_image_url`, `add_outro_animation`, `add_outro_focus_area`, and `outro_focust_area` because Samsar derives those values.
- `outro_focust_area` / `outro_focus_area` values are `{ x, y, width, height }` within the output canvas (`16:9` is `1792x1024`, `9:16` is `1024x1792`).
- `video_model_sub_type` is not part of the request shape; describe style direction in `prompt`.
- Credits for standard express models are billed using the render duration that results from the provided images/metadata: `VEO3.1I2V` is **60 credits/sec**, `VEO3.1I2VFAST` is **36 credits/sec**, `COSMOS3SUPERI2V` is **20 credits/sec**, `SEEDANCEI2V` is **30 credits/sec**, `KLINGIMGTOVID3PRO` is **36 credits/sec**, `KLINGIMGTOVIDTURBO` is **36 credits/sec**, `HAPPYHORSEI2V` is **36 credits/sec**, and `RUNWAYML` is **30 credits/sec**. Narrator avatar generation adds **4 credits/sec** when `add_narrator_avatar` is true. Express CTA generation adds **1 credit/sec** when `express_cta_generation` is true.
**Sample request: provided outro image**
```bash
curl -X POST https://api.samsar.one/v1/video/image_list_to_video \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"image_urls": [
"https://cdn.example.com/frame1.png",
"https://cdn.example.com/frame2.png"
],
"prompt": "Make this feel like a fast-paced teaser",
"metadata": { "project": "launch_trailer" },
"video_model": "KLINGIMGTOVID3PRO",
"aspect_ratio": "16:9",
"language": "en",
"enable_subtitles": true,
"font_key": "Poppins",
"limit_single_narrator": true,
"outro_image_url": "https://cdn.example.com/outro.png",
"add_outro_animation": true,
"add_outro_focus_area": true,
"outro_focust_area": { "x": 240, "y": 140, "width": 720, "height": 420 }
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Sample request: generate QR outro image server-side**
```bash
curl -X POST https://api.samsar.one/v1/video/image_list_to_video \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"image_urls": [
{ "image_url": "https://cdn.example.com/frame1.png", "title": "Blue Lagoon Tour" },
{ "image_url": "https://cdn.example.com/frame2.png", "title": "Sunset Dinner" }
],
"prompt": "Travel offer reel with a scannable booking outro",
"metadata": { "project": "destination_campaign" },
"video_model": "RUNWAYML",
"aspect_ratio": "9:16",
"language": "en",
"enable_subtitles": true,
"font_key": "Poppins",
"add_narrator_avatar": true,
"generate_outro_image": true,
"cta_url": "https://example.com/book",
"cta_text_top": "Scan to book",
"cta_text_bottom": "Limited availability",
"cta_logo": "https://cdn.example.com/logo-white.png",
"add_footer_animation": true,
"footer_metadata": [
{ "url": "https://example.com/blue-lagoon", "title": "Blue Lagoon Tour" },
{ "url": "https://example.com/sunset-dinner", "title": "Sunset Dinner" }
]
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Sample request: generate CTA image outro with a center logo**
```bash
curl -X POST https://api.samsar.one/v1/video/image_list_to_video \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"image_urls": [
"https://cdn.example.com/frame1.png",
"https://cdn.example.com/frame2.png"
],
"prompt": "Product launch teaser with a branded CTA outro",
"video_model": "RUNWAYML",
"aspect_ratio": "9:16",
"generate_outro_image": true,
"outro_cta_image": {
"top_text": "Shop the drop",
"middle_image": { "url": "https://cdn.example.com/drop-logo.png" },
"bottom_text": "Limited availability"
}
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Sample request: express CTA generation from one link**
```bash
curl -X POST https://api.samsar.one/v1/video/image_list_to_video \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"image_urls": [
{ "image_url": "https://cdn.example.com/frame1.png", "title": "Blue Lagoon Tour" },
{ "image_url": "https://cdn.example.com/frame2.png", "title": "Sunset Dinner" }
],
"prompt": "Travel offer reel with concise scene CTAs",
"metadata": { "brand": "Guidestination", "offer": "Bookable Bangkok experiences" },
"video_model": "RUNWAYML",
"aspect_ratio": "9:16",
"language": "en",
"enable_subtitles": true,
"font_key": "Poppins",
"express_cta_generation": true,
"cta_url": "https://example.com/book"
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Success response (200)**
```json
{
"request_id": "vid_img_a1b2c3",
"session_id": "vid_img_a1b2c3"
}
```
**Error responses**
- `400` invalid or missing `image_urls`.
- `401` invalid API key.
## POST /external_users/text_to_video and /external_users/image_list_to_video
External-user video creation routes accept the same `input` parameters as `/video/text_to_video` and `/video/image_list_to_video`, including generated CTA outros (`generate_outro_image` with `cta_url` for a QR center image or `outro_cta_image` for a supplied center logo/CTA image), image-list express CTA generation (`express_cta_generation` with `cta_url`), provided outro images, footer QR sections (`add_footer_animation`, `footer_metadata`), subtitle/font options, optional `speakerOptions`, optional `inference_model`, and image-list `video_model` / `aspect_ratio`, `limit_single_narrator`, and `add_narrator_avatar`.
Wrap the request with an `external_user` object. Responses return an external request/session id; either the returned `extreq_...` id or the normalized external id can be used with `/external_users/status`, `/external_users/add_outro_image`, `/external_users/update_outro_image`, `/external_users/update_footer_image`, `/external_users/translate_video`, and `/external_users/join_videos`.
External user records store a stable `unique_key`. If `unique_key` is not provided, Samsar uses `external_user_id` as the key. V2 clients can create and later reference an external user through `POST /v2/user/create_external_user`.
Repeated video routes also perform server-side compatibility routing. If a main `/video/*` route receives an external request id, normalized external id, or external-owned upstream session id, it is processed through the external-user billing path. If an `/external_users/*` route receives a normal internal `GlobalSession`/video session id owned by the authenticated account and no external user context is supplied, it is processed through the internal billing path.
```json
{
"external_user": {
"provider": "guidestination",
"unique_key": "traveler_123",
"external_user_id": "traveler_123"
},
"input": {
"image_urls": [
{ "image_url": "https://cdn.example.com/frame1.png", "title": "Blue Lagoon Tour" }
],
"video_model": "RUNWAYML",
"generate_outro_image": true,
"cta_url": "https://example.com/book",
"add_footer_animation": true,
"footer_metadata": [
{ "url": "https://example.com/book", "title": "Book now" }
]
},
"webhookUrl": "https://example.com/webhook"
}
```
## POST /video/add_outro_image
Clone an existing session, then add or replace the outro image in the cloned session and queue frame/video generation. Use a provided full outro image, or ask Samsar to generate the outro from existing session image layers with either a QR center image or supplied center logo/CTA image.
**Request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"outro_image_url": "https://cdn.example.com/outro.png",
"add_outro_animation": true,
"add_outro_focus_area": true,
"outro_focust_area": { "x": 240, "y": 140, "width": 720, "height": 420 }
},
"webhookUrl": "https://example.com/webhook"
}
```
- `videoSessionId` required; must belong to the API key.
- `outro_image_url` required unless `generate_outro_image` is true; URL (or data URL) for a full outro image.
- `generate_outro_image` optional boolean; when true, provide either `cta_url` or `outro_cta_image` and omit `outro_image_url`.
- `cta_url` optional for generated outro mode; when provided, it is converted into a scannable QR code in the center image area.
- `outro_cta_image` optional for generated outro mode; uses `{ "top_text", "middle_image", "bottom_text" }` and places `middle_image` directly in the same center area used by QR outros, resized without changing aspect ratio.
- `add_outro_animation` optional boolean; when true, adds a fade-to-black overlay in the final outro layer.
- `add_outro_focus_area` optional boolean; requires `add_outro_animation` and valid `outro_focust_area`.
- `outro_focust_area` optional; `{ x, y, width, height }`, validated against the session canvas.
- Behavior:
- If the last layer is already the outro, it replaces that outro.
- If the last layer is not an outro, it appends a new outro layer.
- In both cases, a `0.5s` fade-in transition of the outro image is applied to the end of the prior layer so it leads into the outro start frame.
- Credits are billed at **75 credits per request**.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/video/add_outro_image \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"outro_image_url": "https://cdn.example.com/outro.png",
"add_outro_animation": true,
"add_outro_focus_area": true,
"outro_focust_area": { "x": 240, "y": 140, "width": 720, "height": 420 }
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Sample request: add generated CTA image outro**
```bash
curl -X POST https://api.samsar.one/v1/video/add_outro_image \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"generate_outro_image": true,
"outro_cta_image": {
"top_text": "Shop the drop",
"middle_image": { "url": "https://cdn.example.com/drop-logo.png" },
"bottom_text": "Limited availability"
}
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Success response (200)**
```json
{
"request_id": "vid_outro_a1b2c3",
"session_id": "vid_outro_a1b2c3"
}
```
**Error responses**
- `400` invalid or missing fields.
- `401` invalid API key.
- `402` insufficient credits.
- `404` session not found.
## POST /video/update_outro_image
Clone an existing session that already has an outro layer, update that outro layer, and queue only the frame/video regeneration stages. This route does not rerun prompt, image, speech, music, or AI video generation.
Use one of two modes:
- Provided outro image mode: pass `outro_image_url` (or `outroImageUrl`, `new_outro_image_url`, `newOutroImageUrl`) as an HTTP(S) URL or image data URL.
- Generated CTA outro mode: pass `generate_outro_image: true` with either `cta_url` or `outro_cta_image`, or provide `cta_url` / `outro_cta_image` without an `outro_image_url`. With `cta_url`, Samsar builds a QR-code outro. With `outro_cta_image`, Samsar places the supplied logo/CTA image in the same center area, resized without changing aspect ratio. In both cases it derives the focus area server-side and keeps optional top/bottom CTA text above the fade overlay.
**Provided outro image request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"outro_image_url": "https://cdn.example.com/outro.png",
"add_outro_animation": true,
"add_outro_focus_area": true,
"outro_focust_area": { "x": 240, "y": 140, "width": 720, "height": 420 }
},
"webhookUrl": "https://example.com/webhook"
}
```
**Generated CTA outro request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"generate_outro_image": true,
"cta_url": "https://example.com/book",
"cta_text_top": "Scan to book",
"cta_text_bottom": "Limited availability",
"add_outro_animation": true
},
"webhookUrl": "https://example.com/webhook"
}
```
**Generated CTA image outro request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"generate_outro_image": true,
"outro_cta_image": {
"top_text": "Shop the drop",
"middle_image": { "url": "https://cdn.example.com/drop-logo.png" },
"bottom_text": "Limited availability"
}
},
"webhookUrl": "https://example.com/webhook"
}
```
- `videoSessionId` required; must belong to the API key.
- `outro_image_url` required unless `generate_outro_image` is true.
- `generate_outro_image` optional boolean; when true, `cta_url` or `outro_cta_image` is required and `outro_image_url` must be omitted. If `cta_url` or `outro_cta_image` is provided without `outro_image_url`, generated CTA outro mode is inferred.
- `cta_url` optional for generated CTA outro mode; must be HTTP(S). It is converted into a scannable QR code.
- `outro_cta_image` optional for generated CTA outro mode; uses `{ "top_text", "middle_image", "bottom_text" }`. `middle_image` accepts a public URL (`{ "url" }`, `{ "image_url" }`) or image data (`{ "data_url" }`, `{ "image_data", "mime_type" }`).
- `cta_text_top` optional; rendered near the top of the outro layer above the fade overlay.
- `cta_text_bottom` optional; rendered near the bottom of the outro layer above the fade overlay.
- `cta_logo` optional and accepted for compatibility with QR generated-outro requests.
- `add_outro_animation` optional boolean. In generated CTA outro mode it defaults to true; when true, Samsar adds a black overlay that fades from 60% opacity to 0% while the generated center image and CTA text stay above it.
- `add_outro_focus_area` optional for provided outro image mode; requires `add_outro_animation` and valid `outro_focust_area`. Generated CTA outro mode calculates the focus area server-side.
- Credits are billed at **75 credits per request**.
**Sample request: update with generated CTA outro**
```bash
curl -X POST https://api.samsar.one/v1/video/update_outro_image \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"generate_outro_image": true,
"cta_url": "https://example.com/book",
"cta_text_top": "Scan to book",
"cta_text_bottom": "Limited availability"
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Success response (200)**
```json
{
"request_id": "vid_outro_update_a1b2c3",
"session_id": "vid_outro_update_a1b2c3"
}
```
## POST /video/update_footer_image
Clone an existing video session, update or remove the scene footer CTA, and queue only footer image creation when needed plus frame/video regeneration. This route does not rerun prompt, image, speech, music, or AI video generation.
Use `remove_footer: true` to remove the footer from all scenes in the cloned rerender. Otherwise provide at least one CTA field and Samsar applies the same minimal footer layout to each scene.
**Update footer request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"cta_text": "Scan to book",
"cta_logo": "https://cdn.example.com/logo-white.png",
"cta_url": "https://example.com/book"
},
"webhookUrl": "https://example.com/webhook"
}
```
**Remove footer request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"remove_footer": true
},
"webhookUrl": "https://example.com/webhook"
}
```
- `videoSessionId` required; must belong to the API key.
- `remove_footer` optional boolean; defaults to false. When true, `cta_text`, `cta_logo`, and `cta_url` are ignored and all scene footer metadata is cleared.
- `cta_text` optional string; rendered in the bottom-left footer area.
- `cta_logo` optional HTTP(S) URL or image data URL; rendered below the CTA text in the bottom-left footer area.
- `cta_url` optional HTTP(S) URL; converted into a scannable QR code on the right side of the footer.
- At least one of `cta_text`, `cta_logo`, or `cta_url` is required unless `remove_footer` is true.
- Credits are billed at **75 credits per request**.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/video/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"
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Success response (200)**
```json
{
"request_id": "vid_footer_update_a1b2c3",
"session_id": "vid_footer_update_a1b2c3"
}
```
## POST /external_users/add_outro_image
External-user equivalent of `/video/add_outro_image`. Pass the external user identity and an external request/session id such as `extreq_df04419ab0cf426e9b30891da71a9293`; Samsar resolves it to the upstream video session, charges the external user's credits, clones the session, and queues only frame/video regeneration.
**Request body**
```json
{
"external_user": {
"provider": "guidestination",
"external_user_id": "traveler_123"
},
"input": {
"request_id": "extreq_df04419ab0cf426e9b30891da71a9293",
"outro_image_url": "https://cdn.example.com/outro.png",
"add_outro_animation": true,
"add_outro_focus_area": true,
"outro_focust_area": { "x": 240, "y": 140, "width": 720, "height": 420 }
},
"webhookUrl": "https://example.com/webhook"
}
```
- `request_id` / `session_id` required; use the external request id returned by `/external_users/text_to_video` or `/external_users/image_list_to_video`.
- Supports the same provided-outro and generated CTA outro modes as `/video/add_outro_image`.
- Generated CTA outro mode accepts `cta_url` for a QR center image or `outro_cta_image` for a supplied center logo/CTA image.
- `add_outro_animation`, `add_outro_focus_area`, and `outro_focust_area` match `/video/add_outro_image`.
- Credits are billed at **75 credits per request** against the external user.
## POST /external_users/update_outro_image
External-user equivalent of `/video/update_outro_image`. Use this endpoint when the source video was created through `/external_users/text_to_video` or `/external_users/image_list_to_video`; pass either the returned `extreq_...` id or the normalized external id.
**Generated CTA outro request body**
```json
{
"external_user": {
"provider": "guidestination",
"external_user_id": "traveler_123"
},
"input": {
"request_id": "extreq_df04419ab0cf426e9b30891da71a9293",
"generate_outro_image": true,
"outro_cta_image": {
"top_text": "Shop the drop",
"middle_image": { "url": "https://cdn.example.com/drop-logo.png" },
"bottom_text": "Limited availability"
},
"add_outro_animation": true
},
"webhookUrl": "https://example.com/webhook"
}
```
- `request_id` / `session_id` required; use the external request id returned by an external-user video route.
- Supports the same provided-outro and generated CTA outro modes as `/video/update_outro_image`.
- Generated CTA outro mode accepts `cta_url` for a QR center image or `outro_cta_image` for a supplied center logo/CTA image, then derives `add_outro_focus_area` and `outro_focust_area` server-side.
- Credits are billed at **75 credits per request** against the external user.
**Success response (200)**
```json
{
"request_id": "extreq_9b30891da71a9293df04419ab0cf426e",
"session_id": "extreq_9b30891da71a9293df04419ab0cf426e",
"external_request_id": "extreq_9b30891da71a9293df04419ab0cf426e",
"status_endpoint": "/v1/external_users/status?request_id=extreq_9b30891da71a9293df04419ab0cf426e",
"creditsCharged": 75
}
```
## POST /external_users/update_footer_image
External-user equivalent of `/video/update_footer_image`. Use this endpoint when the source video was created through `/external_users/text_to_video` or `/external_users/image_list_to_video`; pass either the returned `extreq_...` id or the normalized external id.
**Request body**
```json
{
"external_user": {
"provider": "guidestination",
"external_user_id": "traveler_123"
},
"input": {
"request_id": "extreq_df04419ab0cf426e9b30891da71a9293",
"cta_text": "Scan to book",
"cta_logo": "https://cdn.example.com/logo-white.png",
"cta_url": "https://example.com/book"
},
"webhookUrl": "https://example.com/webhook"
}
```
- `request_id` / `session_id` required; use the external request id returned by an external-user video route.
- Supports the same `remove_footer`, `cta_text`, `cta_logo`, and `cta_url` fields as `/video/update_footer_image`.
- Credits are billed at **75 credits per request** against the external user.
**Success response (200)**
```json
{
"request_id": "extreq_9b30891da71a9293df04419ab0cf426e",
"session_id": "extreq_9b30891da71a9293df04419ab0cf426e",
"external_request_id": "extreq_9b30891da71a9293df04419ab0cf426e",
"status_endpoint": "/v1/external_users/status?request_id=extreq_9b30891da71a9293df04419ab0cf426e",
"creditsCharged": 75
}
```
## POST /video/clone
Deep-copy a completed video session into a new session and queue only the final video render stage. Use this when you need a distinct session id and a distinct rendered video URL, but the same timeline, resources, audio, overlays, subtitle settings, footer/outro metadata, and generated assets.
`POST /v2/video/clone` exposes the same operation on the V2 route surface.
**Request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4"
},
"webhookUrl": "https://example.com/webhook"
}
```
- `videoSessionId` required; must be a completed video session that belongs to the authenticated user.
- The clone copies the source session data and local generated resources into the new session.
- Upstream generation stages are marked complete; only `video_generation` is queued so the renderer creates a new `video/output/video-_*.mp4` file and new CDN URL.
- This endpoint currently does not charge additional credits.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/video/clone \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4"
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Success response (200)**
```json
{
"request_id": "vid_clone_a1b2c3",
"session_id": "vid_clone_a1b2c3",
"status": "PENDING",
"type": "video",
"creditsCharged": 0
}
```
Poll `GET /status?request_id=` or `GET /v2/status?request_id=` until the standard video status response returns `status: "COMPLETED"` with `result_url`, `result_urls`, `has_subtitles`, and `result_language`.
**Error responses**
- `400` invalid/missing fields or source session has no completed video URL
- `401` invalid API key
- `404` session not found
## POST /video/reroll-layers
Reroll selected 1-based layers in an owned completed video session. The selected layers restart at image generation, then AI-video and lip-sync/sound-effect work runs as needed for those layers. Frame generation, generated outro tiles when present, and final video generation rerun for the whole session.
`POST /v2/reroll-layers` and `POST /v2/video/reroll-layers` expose the same operation on the V2 route surface.
**Request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"layer_indexes": [3, 6, 11]
},
"webhookUrl": "https://example.com/webhook"
}
```
- `videoSessionId` required; must be a video session that belongs to the authenticated user and is not currently rendering.
- `layer_indexes` required; accepts positive 1-based layer indexes.
- The route updates the same session in place and does not clone the video.
- Quote-only requests with `quote_only: true` return the credit estimate without charging or queuing work.
**Quote-only request**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"layer_indexes": [3, 6],
"quote_only": true
}
}
```
**Success response (200)**
```json
{
"request_id": "vid_a1b2c3d4",
"session_id": "vid_a1b2c3d4",
"status": "PENDING",
"layer_indexes": [3, 6, 11],
"creditQuote": {
"layerCount": 3,
"totalCredits": 164,
"imageCredits": 92,
"aiVideoCredits": 72
},
"creditsCharged": 164
}
```
**Error responses**
- `400` invalid/missing fields, unsupported layer indexes, or missing image prompts
- `401` invalid API key
- `402` insufficient credits
- `404` session not found
- `409` session is currently rendering or paused
## POST /video/translate_video
Translate an existing video session into a new language. This creates a new session and runs only the required stages (speech translation, lip sync, optional transcription, final render) to produce a translated video.
`POST /video/retranslate_video` is available as an alias for the same operation.
**Request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4",
"language": "en | es | fr | ja | th | zh | bn | hi | sa | la",
"enable_subtitles": false,
"translate_outro": true,
"translate_footer": true
},
"webhookUrl": "https://example.com/webhook"
}
```
- `videoSessionId` required; must be a video session that belongs to the API key.
- `language` required; pass a supported language code.
- `enable_subtitles` optional boolean; defaults to `false`. When `true`, Samsar generates translated transcript/subtitle text for the cloned session.
- `translate_outro` optional boolean; defaults to `true`. When enabled, stored generated-outro top and bottom CTA text are translated and reused in the cloned outro.
- `translate_footer` optional boolean; defaults to `true`. When enabled, stored footer CTA text is translated per footer-enabled layer and reused in the cloned footer overlay.
- `outro_image_url` is no longer part of the translate payload. Use `POST /video/update_outro_image` before or after translation when the outro image itself needs to change.
- Credits are billed at **3 credits/sec** using the duration of the source session.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/video/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"
}'
```
**Success response (200)**
```json
{
"request_id": "vid_tr_a1b2c3",
"session_id": "vid_tr_a1b2c3"
}
```
**Error responses**
- `400` invalid/missing fields (unknown session id, unsupported language, etc.)
- `401` invalid API key
- `402` insufficient credits
## POST /video/join_videos
Join multiple completed video sessions into a single session by appending their timelines in order. This creates a new session and queues transcription, frame generation, and final render to produce the joined video.
**Request body**
```json
{
"input": {
"session_ids": ["vid_a1b2c3d4", "vid_e5f6g7h8"],
"blend_scenes": true
},
"webhookUrl": "https://example.com/webhook"
}
```
- `session_ids` required; array of **2+** unique video session ids.
- `blend_scenes` optional boolean (default `false`). When `true`, joins apply scene-boundary blending:
previous outro image is carried into the next scene with a 1s fade-out, and background music layers are adjusted around boundaries (`+0.5s`) for smoother transitions.
- All sessions must belong to the API key, and must have the same `aspect_ratio` and `framesPerSecond`.
- Credits are billed at **3 credits/sec** using the joined duration (sum of session durations).
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/video/join_videos \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"session_ids": ["vid_a1b2c3d4", "vid_e5f6g7h8"],
"blend_scenes": true
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Success response (200)**
```json
{
"request_id": "vid_join_a1b2c3",
"session_id": "vid_join_a1b2c3"
}
```
**Error responses**
- `400` invalid/missing fields (unknown session id, incompatible sessions, etc.)
- `401` invalid API key
- `402` insufficient credits
- `404` one or more sessions not found
## POST /video/remove_subtitles
Clone an existing session, remove all subtitle/transcript text overlays (`type: "text"` items in layer `activeItemList`), and queue only frame + video generation for the new session.
**Request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4"
},
"webhookUrl": "https://example.com/webhook"
}
```
- `videoSessionId` required; must be a video session that belongs to the API key.
- Creates a new `session_id`; the source session remains unchanged.
- The cloned session is configured to skip transcription and other generation stages, and run only frame/video generation.
- This endpoint currently does not charge additional credits.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/video/remove_subtitles \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4"
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Success response (200)**
```json
{
"request_id": "vid_nosub_a1b2c3",
"session_id": "vid_nosub_a1b2c3"
}
```
**Error responses**
- `400` invalid/missing fields (invalid session id format, missing session id, etc.)
- `401` invalid API key
- `404` session not found
## POST /video/add_subtitles
Clone an existing session, add subtitle/transcript text overlays, and queue the stages required to produce a subtitled video in a new session.
**Request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4"
},
"webhookUrl": "https://example.com/webhook"
}
```
- `videoSessionId` required; must be a video session that belongs to the API key.
- Creates a new `session_id`; the source session remains unchanged.
- The cloned session is saved with `has_subtitles: true` and preserves the session language/aspect ratio.
- This endpoint currently does not charge additional credits.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/video/add_subtitles \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4"
},
"webhookUrl": "https://example.com/video-webhook"
}'
```
**Success response (200)**
```json
{
"request_id": "vid_sub_a1b2c3",
"session_id": "vid_sub_a1b2c3"
}
```
**Error responses**
- `400` invalid/missing fields (invalid session id format, missing session id, etc.)
- `401` invalid API key
- `404` session not found
## POST /video/cancel_render
Cancel an in-progress render for an existing session. This sets the session to a cancelled state and stops queued render tasks for that session.
**Request body**
```json
{
"input": {
"videoSessionId": "vid_a1b2c3d4"
}
}
```
- `videoSessionId` required; must belong to the API key.
- This endpoint does not create a new session.
- This endpoint currently does not charge additional credits.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/video/cancel_render \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"videoSessionId": "vid_a1b2c3d4"
}
}'
```
**Success response (200)**
```json
{
"request_id": "vid_a1b2c3d4",
"session_id": "vid_a1b2c3d4",
"status": "CANCELLED",
"cancelled": true,
"message": "Render cancellation requested."
}
```
**Error responses**
- `400` invalid/missing session id
- `401` invalid API key
- `404` session not found
## GET /video/fetch_latest_version
Fetch the latest available render URL for a given video `session_id`.
**Request**
```bash
curl "https://api.samsar.one/v1/video/fetch_latest_version?session_id=vid_a1b2c3d4" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
**Completed (200)**
```json
{
"session_id": "vid_a1b2c3d4",
"result_url": "https://cdn.samsar.one/videos/vid_a1b2c3d4.mp4",
"has_subtitles": true,
"result_language": "en"
}
```
- `has_subtitles` is `true`, `false`, or `null` when subtitle state is unknown for older sessions.
- `result_language` is the language code saved on the video session.
**Pending (202)**
```json
{
"session_id": "vid_a1b2c3d4",
"status": "PENDING",
"message": "Render not ready yet."
}
```
**Failed (200)**
```json
{
"session_id": "vid_a1b2c3d4",
"status": "FAILED",
"message": "Video generation failed."
}
```
**Cancelled (200)**
```json
{
"session_id": "vid_a1b2c3d4",
"status": "CANCELLED",
"message": "Render was cancelled."
}
```
**Error responses**
- `400` missing/invalid `session_id`
- `401` invalid API key
- `403` session does not belong to the API key
- `404` session not found
## GET /video/list_completed_video_sessions
List completed video sessions for the authenticated API key, including each session language and final URL.
**Request**
```bash
curl "https://api.samsar.one/v1/video/list_completed_video_sessions" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
Optional query params:
- `limit` (number): maximum number of sessions to return (server-capped to `5000`).
**Success response (200)**
```json
[
{
"session_id": "vid_a1b2c3d4",
"langauge": "en",
"result_language": "en",
"has_subtitles": true,
"result_url": "https://static.samsar.one/video/vid_a1b2c3d4/final.mp4"
},
{
"session_id": "vid_e5f6g7h8",
"langauge": "es",
"result_language": "es",
"has_subtitles": false,
"result_url": "https://static.samsar.one/video/vid_e5f6g7h8/final.mp4"
}
]
```
- Returns only sessions that have a final video URL available.
- `langauge` is returned as a normalized language code (lowercase).
- `result_language` is the same normalized language code returned with the corrected field name.
- `has_subtitles` is `true`, `false`, or `null` when subtitle state is unknown for older sessions.
**Error responses**
- `400` missing API key/auth header
- `401` invalid API key
## GET /status
Poll the status for any `request_id` returned by the video endpoints (or `/image/*` endpoints).
**Request**
```bash
curl "https://api.samsar.one/v1/status?request_id=vid_a1b2c3d4" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
**Queued / processing**
```json
{
"session_id": "vid_a1b2c3d4",
"request_id": "vid_a1b2c3d4",
"status": "PENDING",
"type": "video",
"provider": "RUNWAYML",
"expressGenerationStatus": {
"prompt_generation": "PENDING",
"video_generation": "IN_PROGRESS"
}
}
```
**Completed**
```json
{
"session_id": "vid_a1b2c3d4",
"request_id": "vid_a1b2c3d4",
"status": "COMPLETED",
"type": "video",
"provider": "RUNWAYML",
"result_url": "https://cdn.samsar.one/videos/vid_a1b2c3d4.mp4",
"result_urls": [
"https://cdn.samsar.one/videos/vid_a1b2c3d4.mp4"
],
"has_subtitles": true,
"result_language": "en",
"videoLink": "https://cdn.samsar.one/videos/vid_a1b2c3d4.mp4",
"remoteURL": "https://cdn.samsar.one/videos/vid_a1b2c3d4.mp4"
}
```
- `result_urls` may be present when multiple assets are produced; `videoLink` or `remoteURL` can also contain the download URL.
- Completed video responses include `has_subtitles` and `result_language` when session metadata is available.
- `expressGenerationStatus` is present while individual stages are running; `expressGenerationError`/`message` is present on failure.
- `404` is returned when the `request_id` is unknown; `401` when the API key is invalid.
## GET /supported_fonts
Fetch the supported subtitle fonts by language code. Use this to validate `font_key` or text overlay choices before submitting video requests. This endpoint does not require authentication.
**Request**
```bash
curl "https://api.samsar.one/v1/supported_fonts"
```
**Success response (200)**
```json
{
"fontsByLanguage": {
"default": ["Rampart One", "Montserrat", "Arial", "sans-serif"],
"en": ["Poppins", "Montserrat", "Arial", "sans-serif"],
"ja": ["Noto Sans JP", "M PLUS Rounded 1c", "Hiragino Sans", "Yu Gothic UI", "sans-serif"],
"ko": ["Noto Sans KR", "Pretendard", "Apple SD Gothic Neo", "Malgun Gothic", "sans-serif"]
}
}
```
- `default` is used when a language code has no explicit mapping.
- Font previews: visit [`/fonts`](/fonts) for light/dark samples with keys.
## V2 API
The `/v2` omni routes now live in [V2 API](/v2), with left-nav sections for authentication, user credits, app keys, media generation, assistant utilities, status, and publishing routes.
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/assistant-api.mdx
METADATA:
title: Assistant API
description: Call OpenAI-compatible assistant completions tied to Samsar sessions, system prompts, and multimodal outputs.
image: img/og/assistant-api.png
sidebar_position: 4
slug: /assistant-api
sidebar_label: Assistant API
PRIMARY_HEADING: Assistant API
CONTENT_START
---
title: Assistant API
description: "Call OpenAI-compatible assistant completions tied to Samsar sessions, system prompts, and multimodal outputs."
image: img/og/assistant-api.png
sidebar_position: 4
slug: /assistant-api
sidebar_label: Assistant API
---
import DocTopActions from '@site/src/components/DocTopActions';
# Assistant API
- Base URL: `https://api.samsar.one/v1`
- Auth: `Authorization: Bearer `
- Compatible alias: `/api/v1/assistant/*` is also available if your integration expects that prefix.
- Response style: synchronous, OpenAI-compatible Responses payloads, including text messages and multimodal tool outputs such as generated images.
- Pricing:
- `POST /assistant/completion` is usage-based.
- Credits are calculated from the actual request size and generated output, converted using **100 credits = $1**, then multiplied by **2.5x**.
- The exact charge depends on conversation length, answer length, and whether you include richer multimodal inputs such as images.
- Session assistant behavior: the `/v1/assistant/*` response is returned in the same request and does not use status polling.
Use this API when you want OpenAI-style assistant behavior tied to an existing Samsar `session_id`. The assistant uses the customer account's configured assistant model and the account-level system prompt if one is set. If no custom system prompt exists, Samsar applies a minimal default assistant prompt. Reuse the same `session_id` across turns and Samsar will preserve the underlying OpenAI response chain, including image-generation turns.
For provider-style OpenAI-compatible chat completions that can be persisted and polled across network resets, use the separate [`/v2/external/assistant/*` aliases](/external-requests#external-assistant-and-chat-completions). Those aliases do not use this page's session-bound Responses chain.
## POST /assistant/set_system_prompt
Store or clear the account-level system prompt used for assistant completions.
**Request body**
```json
{
"system_prompt": "You are the launch assistant for Acme. Keep answers concise, on-brand, and commercially practical."
}
```
- `system_prompt` optional string.
- Pass `null` or an empty string to clear the custom prompt and revert to Samsar's default prompt.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/assistant/set_system_prompt \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"system_prompt": "You are the launch assistant for Acme. Keep answers concise, on-brand, and commercially practical."
}'
```
**Success response (200)**
```json
{
"system_prompt": "You are the launch assistant for Acme. Keep answers concise, on-brand, and commercially practical.",
"model": "gpt-5.5",
"selected_assistant_model": "gpt-5.5"
}
```
**Common errors**
- `400` invalid payload.
- `401` invalid API key.
---
## POST /assistant/completion
Create a synchronous assistant response for an existing Samsar session. The request returns an OpenAI-compatible Responses payload so it can fit cleanly into modern OpenAI client flows.
You must provide a `session_id`. You can optionally send fresh user input in the same request. If you omit `input`, the assistant responds from the existing session history.
**Request body**
```json
{
"session_id": "67df0d8ebc4f9d0b7f4fd123",
"input": [
{
"role": "user",
"content": [
{ "type": "input_text", "text": "Write a short product launch caption for this session." }
]
}
],
"max_output_tokens": 300
}
```
- `session_id` required.
- `input` optional:
- a string
- a single message object
- an array of message objects
- a multimodal content array such as `input_text` and `input_image`
- `max_output_tokens`, `temperature`, `top_p`, `reasoning`, `metadata`, `tools`, and `tool_choice` are accepted when supported by the configured assistant model.
- To enable assistant-generated images, pass `tools: [{ "type": "image_generation" }]`. If you want to force an image response for the turn, set `tool_choice` to `{ "type": "image_generation" }`.
**Multimodal example**
```json
{
"session_id": "67df0d8ebc4f9d0b7f4fd123",
"input": [
{
"role": "user",
"content": [
{ "type": "input_text", "text": "Describe the hero image and turn it into a headline." },
{ "type": "input_image", "image_url": "https://cdn.example.com/launch-hero.png" }
]
}
]
}
```
**Image generation example**
```json
{
"session_id": "67df0d8ebc4f9d0b7f4fd123",
"input": "Create a clean launch-poster image for this session.",
"tools": [
{
"type": "image_generation",
"size": "1024x1024",
"quality": "high"
}
],
"tool_choice": {
"type": "image_generation"
}
}
```
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/assistant/completion \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "67df0d8ebc4f9d0b7f4fd123",
"input": [
{
"role": "user",
"content": [
{ "type": "input_text", "text": "Write a short product launch caption for this session." }
]
}
],
"max_output_tokens": 300
}'
```
**Success response (200)**
```json
{
"id": "resp_123",
"object": "response",
"created_at": 1773651000,
"status": "completed",
"model": "gpt-5.5",
"output_text": "Meet the new drop: sharp design, fast story, and a launch-ready look in one line.",
"output": [
{
"id": "resp_123_message",
"type": "message",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Meet the new drop: sharp design, fast story, and a launch-ready look in one line.",
"annotations": []
}
]
}
],
"usage": {
"input_tokens": 482,
"output_tokens": 43,
"total_tokens": 525
}
}
```
**Image response example**
```json
{
"id": "resp_456",
"object": "response",
"created_at": 1773651300,
"status": "completed",
"model": "gpt-5.5",
"output": [
{
"id": "ig_456",
"type": "image_generation_call",
"status": "completed",
"revised_prompt": "A clean launch poster with a premium layout, strong product focal point, and minimal copy.",
"result": "iVBORw0KGgoAAAANSUhEUgAA..."
}
],
"usage": {
"input_tokens": 521,
"output_tokens": 22,
"total_tokens": 543
}
}
```
The `result` field contains a base64-encoded image. For follow-up image edits, keep using the same `session_id`; Samsar preserves the latest response chain automatically.
**Headers**
- `x-credits-charged`
- `x-credits-remaining`
**Common errors**
- `400` missing or invalid `session_id`, or invalid `input`.
- `401` invalid API key.
- `402` insufficient credits.
- `403` session does not belong to the authenticated account.
- `404` session not found.
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/chat-api.mdx
METADATA:
title: Chat API
description: Improve copy and manage embeddings with synchronous chat endpoints for app and content workflows.
image: img/og/chat-api.png
sidebar_position: 3
slug: /chat-api
sidebar_label: Chat API
PRIMARY_HEADING: Chat API
CONTENT_START
---
title: Chat API
description: "Improve copy and manage embeddings with synchronous chat endpoints for app and content workflows."
image: img/og/chat-api.png
sidebar_position: 3
slug: /chat-api
sidebar_label: Chat API
---
import DocTopActions from '@site/src/components/DocTopActions';
import PlaygroundRail from '@site/src/components/PlaygroundRail';
# Chat API
Send a sample prompt with your API key in the Playground to see how the `/chat/enhance` response is formatted.
- Base URL: `https://api.samsar.one/v1`
- External managed-inference base URL: `https://api.samsar.one/v2/external`
- Auth: `Authorization: Bearer `
- Pricing:
- `/chat/enhance`: **30 credits** per request.
- `/v2/external/chat` and `/v2/external/chat/completions` are usage-based external inference endpoints. Text and vision inputs use the selected adapter token rates shown below.
- `/v2/external/moderation` and `/v2/external/moderations`: no credit charge.
- Embedding routes are usage-based. Credits scale with the amount of text processed and use the standard **100 credits = $1** conversion.
- URL embedding routes add crawl cost before credit conversion and apply a **2.5x** ingestion multiplier because they include both crawling and embedding work.
- `/chat/generate_embeddings_from_plain_text` applies a **2.5x** multiplier to embedding-token cost only. It does not add crawl charges.
- `/chat/delete_embedding` and `/chat/delete_embeddings`: no token charges.
- `/chat/enhance` and embedding routes are synchronous. External chat defaults to synchronous execution and also supports durable queued requests with status polling.
## POST /chat/enhance
Improve a message while keeping its intent. `metadata` is optional contextual data that influences the rewrite.
Provide `language` (ISO 639-1 code or language name) if you want the output in a specific language; otherwise detection is automatic.
**Request body**
```json
{
"message": "Draft caption text",
"metadata": {
"audience": "gen z",
"tone": "playful"
},
"language": "FR"
}
```
- `message` required, non-empty string.
- `metadata` optional object.
- `language` optional string, ISO 639-1 code (e.g., `EN`, `FR`, `TH`) or language name; defaults to auto-detect.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/chat/enhance \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "launching soon, stay tuned!",
"metadata": { "channel": "instagram" },
"language": "FR"
}'
```
**Success response (200)**
```json
{
"content": "We’re launching soon—follow along for first looks and drop reminders!"
}
```
**Common errors**
- `400` missing or empty `message`.
- `401` invalid API key.
- `402` insufficient credits.
---
## POST /v2/external/chat/completions
Create an OpenAI-compatible chat completion billed through Samsar credits. Use this endpoint when an external deployment or integration needs managed inference without supplying native OpenAI or Google credentials. The same handler is available at `/v2/external/chat`, `/v2/external/assistant`, and `/v2/external/assistant/completions`; `/api/v2/external/*` is also supported.
This endpoint is synchronous by default. For durable submission, idempotent retries, request correlation, and polling, see [External assistant and chat completions](/external-requests#external-assistant-and-chat-completions).
**Pricing**
| Adapter | Context | Input credits / 1M tokens | Cached input credits / 1M tokens | Output credits / 1M tokens |
| --- | --- | ---: | ---: | ---: |
| `gpt-5.5` | Up to 272k input tokens | 625 | 62.5 | 3,750 |
| `gpt-5.5` | Above 272k input tokens | 1,250 | 125 | 5,625 |
| `gemini-3.1-pro` | Up to 200k input tokens | 250 | 25 | 1,500 |
| `gemini-3.1-pro` | Above 200k input tokens | 500 | 50 | 2,250 |
Vision requests use the same adapter token rates for image-derived input tokens and generated output tokens. Responses include `x-credits-charged` and `x-credits-remaining` headers when credits are deducted.
**Request body**
```json
{
"model": "gpt-5.5",
"messages": [
{ "role": "developer", "content": "Return concise JSON." },
{ "role": "user", "content": "Create a three-scene outline for a launch video." }
],
"response_format": { "type": "json_object" }
}
```
For vision, send OpenAI-compatible image message parts:
```json
{
"model": "gemini-3.1-pro",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Describe this image for a video prompt." },
{ "type": "image_url", "image_url": { "url": "https://example.com/frame.png" } }
]
}
]
}
```
**Sample request**
```bash
curl -X POST https://api.samsar.one/v2/external/chat/completions \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [
{ "role": "user", "content": "Write one cinematic product video theme." }
]
}'
```
**Success response (200)**
The response is an OpenAI-compatible chat completion object.
Queued mode instead returns HTTP `202` with `request_id`, `status`, and `poll_url`. The default server execution timeout is 10 minutes; polling avoids holding the HTTP connection for that entire period.
**Common errors**
- `400` missing or invalid `messages`.
- `401` invalid API key.
- `402` insufficient credits.
---
## POST /v2/external/moderation
Create an OpenAI-compatible moderation check for external deployments. The same handler is available at `/external/moderations`, `/v2/external/moderation`, and `/v2/external/moderations`.
This route requires a Samsar API key but does not charge credits.
**Request body**
```json
{
"input": "A user prompt or generated draft to moderate.",
"model": "omni-moderation-latest"
}
```
**Sample request**
```bash
curl -X POST https://api.samsar.one/v2/external/moderation \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "Create a family-safe product launch video."
}'
```
**Success response (200)**
The response includes the OpenAI-compatible moderation result plus a `decision` object with the normalized Samsar pass/fail decision.
**Common errors**
- `400` missing or invalid `input`.
- `401` invalid API key.
- `500` moderation provider is not configured.
---
## POST /chat/create_embedding
Create a new embedding template from either a JSON array or a URL list. JSON records are embedded directly. URL inputs are first crawled with Firecrawl, minimally cleaned, normalized into the same compatible record structure, and then embedded using `text-embedding-3-large`.
**Request body**
```json
{
"name": "listings",
"records": [
{
"id": "A1",
"city": "Austin",
"price": 1800,
"duration": 12,
"description": "Modern 1BR close to downtown."
}
]
}
```
- `records` required when embedding JSON input.
- `urls` or `url` can be used instead of `records` to build embeddings from crawled web pages.
- `name` optional string. Also accepted: `embedding_name`, `template_name`.
- Each record should include an `id`, `_id`, or equivalent identifier. If missing, the API generates one per record.
- `field_options` optional map (or array) of per-field flags:
- `searchable: false` excludes the field (and nested paths) from embedding input.
- `filterable: true` includes the field in structured filters used for search/similar queries.
- `filterable: false` excludes the field from structured filters.
- `retrievable: false` removes the field from raw `record` payloads in search results.
- Use dot notation (e.g. `owner.email`) for nested fields.
You can also inline flags in records using a `{ value, searchable, filterable, retrievable }` wrapper.
```json
{
"records": [
{
"id": "A1",
"internal_notes": { "value": "private note", "searchable": false, "retrievable": false }
}
]
}
```
**URL request body**
```json
{
"name": "product-docs",
"urls": [
"https://example.com/docs/getting-started",
"https://example.com/docs/pricing"
]
}
```
- URL mode crawls each URL with Firecrawl before embedding.
- Optional `levels` controls crawl depth for URL mode. It can be passed as a query param or body field.
- `1`: crawl only the listed seed URLs.
- `2`: crawl each seed URL plus URLs linked directly from it.
- `3`: crawl each seed URL plus two link hops outward.
- Default: `2`. Maximum: `3`.
- URL mode is capped at `50` total crawled pages per request across all seed URLs.
- Returned embeddings remain compatible with `/chat/search_against_embedding` and `/chat/similar_to_embedding`.
- URL mode responses may include `input_url_count`, `processed_url_count`, `firecrawl_credits_used`, `skipped_urls`, and `crawl_errors`.
**Sample request**
```bash
curl -X POST "https://api.samsar.one/v1/chat/create_embedding?levels=2" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "listings",
"field_options": {
"description": { "searchable": true },
"internal_notes": { "searchable": false, "retrievable": false },
"price": { "filterable": true }
},
"records": [
{ "id": "A1", "city": "Austin", "price": 1800, "description": "Modern 1BR close to downtown." },
{ "id": "A2", "city": "Dallas", "price": 2200, "description": "Spacious 2BR with a balcony." }
]
}'
```
**Success response (200)**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"template_hash": "b0e6f2d2c2f1d0c4d9f7f3a1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
"hash_link": "embedding_template:b0e6f2d2c2f1d0c4d9f7f3a1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
"record_count": 2,
"structured_fields": [
{ "key": "city", "type": "string" },
{ "key": "price", "type": "number" }
],
"unstructured_fields": ["description"]
}
```
**Common errors**
- `400` missing `records` and `urls`, or invalid URL input.
- `401` invalid API key.
- `402` insufficient credits.
---
## POST /chat/create_embedding_from_url
Create a new embedding template from one URL or a URL list. This is the explicit URL-only variant of `/chat/create_embedding` and uses the same Firecrawl-backed ingestion flow.
**Request body**
```json
{
"name": "product-docs",
"urls": [
"https://example.com/docs/getting-started",
"https://example.com/docs/pricing"
]
}
```
- `urls` required array of URL strings. `url` is also accepted for a single URL.
- Optional `levels` controls crawl depth for URL mode. It can be sent as a query param or body field.
- `1`: crawl only the listed seed URLs.
- `2`: crawl each seed URL plus URLs linked directly from it.
- `3`: crawl each seed URL plus two link hops outward.
- Default: `2`. Maximum: `3`.
- URL mode is capped at `50` total crawled pages per request across all seed URLs.
- `field_options` is optional and follows the same rules as `/chat/create_embedding`.
- Samsar crawls the URLs, minimally cleans the extracted text, stores normalized URL metadata, and creates embeddings in the same query-compatible format as JSON-record embeddings.
- Billing applies Firecrawl crawl usage at a **2.5x** multiplier based on actual pages crawled, then applies the embedding token cost with its own **2.5x** multiplier.
**Sample request**
```bash
curl -X POST "https://api.samsar.one/v1/chat/create_embedding_from_url?levels=2" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "product-docs",
"urls": [
"https://example.com/docs/getting-started",
"https://example.com/docs/pricing"
]
}'
```
**Success response (200)**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"template_hash": "b0e6f2d2c2f1d0c4d9f7f3a1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
"hash_link": "embedding_template:b0e6f2d2c2f1d0c4d9f7f3a1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
"record_count": 2,
"input_url_count": 2,
"processed_url_count": 12,
"firecrawl_credits_used": 12,
"crawl_levels": 2,
"max_links": 50,
"structured_fields": [
{ "key": "hostname", "type": "string" },
{ "key": "status_code", "type": "number" }
],
"unstructured_fields": ["title", "description", "content"]
}
```
**Common errors**
- `400` missing or invalid `urls`.
- `401` invalid API key.
- `402` insufficient credits.
- `422` URLs crawled successfully but produced no extractable content.
- `5xx` Firecrawl crawl failure or upstream processing failure.
---
## POST /chat/generate_embeddings_from_plain_text
Create a new embedding template from already cleaned plain text input. This route is the embedding-only stage of the URL ingestion flow: you provide the cleaned text directly, Samsar skips crawling, and it stores the result in the same query-compatible embedding format used by the JSON and URL endpoints.
**Request body**
```json
{
"name": "product-docs-clean",
"plain_text": [
{
"url": "https://example.com/docs/getting-started",
"title": "Getting Started",
"content": "Cleaned plain text content from the page."
}
]
}
```
- `plain_text` required. Accepted shapes:
- a single string
- a single object
- an array of strings or objects
- For object entries, the cleaned text can be provided in `plain_text`, `content`, `text`, `cleaned_text`, `markdown`, or `body`.
- Optional object fields such as `url`, `title`, `description`, and `language` are preserved and normalized into the stored record when present.
- `field_options` is optional and follows the same rules as `/chat/create_embedding`.
- Pricing uses the embedding token cost with a **2.5x** multiplier and does **not** charge any crawl credits.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/chat/generate_embeddings_from_plain_text \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "product-docs-clean",
"plain_text": [
{
"url": "https://example.com/docs/getting-started",
"title": "Getting Started",
"content": "Cleaned plain text content from the page."
},
{
"url": "https://example.com/docs/pricing",
"title": "Pricing",
"content": "Cleaned pricing page text."
}
]
}'
```
**Success response (200)**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"template_hash": "b0e6f2d2c2f1d0c4d9f7f3a1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
"hash_link": "embedding_template:b0e6f2d2c2f1d0c4d9f7f3a1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
"record_count": 2,
"structured_fields": [
{ "key": "hostname", "type": "string" }
],
"unstructured_fields": ["title", "content"]
}
```
**Common errors**
- `400` missing or invalid `plain_text`.
- `401` invalid API key.
- `402` insufficient credits.
---
## POST /chat/update_embedding
Append or update records for an existing template. New records are embedded and merged into the template schema; matching `sourceId` records are upserted.
**Request body**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"field_options": {
"description": { "searchable": true },
"internal_notes": { "searchable": false, "retrievable": false },
"price": { "filterable": true }
},
"records": [
{
"id": "A3",
"city": "Houston",
"price": 2000,
"description": "Bright 2BR near the park."
}
]
}
```
`field_options` follows the same rules as `/chat/create_embedding`. If omitted, the template's existing field options are reused.
**Success response (200)**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"template_hash": "9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1908",
"record_count": 3,
"structured_fields": [
{ "key": "city", "type": "string" },
{ "key": "price", "type": "number" }
],
"unstructured_fields": ["description"]
}
```
**Common errors**
- `400` missing `template_id` or `records`.
- `401` invalid API key.
- `402` insufficient credits.
- `404` template not found.
---
## POST /chat/delete_embeddings
Delete all embeddings for a template. The template remains, but its record count becomes 0.
**Request body**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3"
}
```
**Success response (200)**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"deleted_count": 250,
"record_count": 0,
"status": "empty"
}
```
**Common errors**
- `400` missing `template_id`.
- `401` invalid API key.
- `404` template not found.
---
## POST /chat/delete_embedding
Delete one or more embeddings from a template by record id. Use the `id` returned in search results or the `id` you supplied in the original records.
**Request body**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"source_id": "A2"
}
```
- `source_id` optional string. Also accepted: `sourceId`, `id`, `record_id`.
- `source_ids` optional array of strings. Also accepted: `sourceIds`, `ids`, `record_ids`.
- Provide either `source_id` or `source_ids`.
**Success response (200)**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"deleted_count": 1,
"record_count": 249,
"status": "ready"
}
```
**Common errors**
- `400` missing `template_id` or `source_id`/`source_ids`.
- `401` invalid API key.
- `404` template not found.
---
## POST /chat/search_against_embedding
Search within a template using a natural language query. The API extracts structured filters first, then performs vector similarity. Optional rerank can re-order top results.
**Request body**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"search_term": "2 bedroom apartment in Dallas under 2500",
"search_params": { "city": "Dallas", "price": { "max": 2500 } },
"limit": 10,
"num_candidates": 50,
"rerank": true,
"include_raw": true
}
```
**Success response (200)**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"template_name": "listings",
"structured_filters": { "city": "Dallas", "price": 2500 },
"results": [
{
"id": "A2",
"score": 0.92,
"structured_filters": { "city": "Dallas", "price": 2200 },
"record": {
"id": "A2",
"city": "Dallas",
"price": 2200,
"description": "Spacious 2BR with a balcony."
}
}
]
}
```
- Optional `structured_filters` (or `filters`) can be supplied to force filters.
- Optional `search_params` (or `searchParams`) supplies a filter payload validated against the template schema and applied before vector search. `structured_filters` overrides overlapping keys.
- Use `include_raw: false` to omit the original record payloads.
**Common errors**
- `400` missing `template_id` or `search_term`.
- `401` invalid API key.
- `402` insufficient credits.
- `404` template not found.
---
## POST /chat/similar_to_embedding
Return similar results for a query within a template (vector similarity only). You can pass a natural language `search_term` or a JSON payload in `search_json` which is converted into the template's search document before embedding.
**Request body**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"search_term": "modern downtown apartment",
"search_params": { "city": "Austin" },
"limit": 10,
"min_results": 10
}
```
**Request body (JSON search)**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"search_json": {
"city": "Austin",
"price": 2000,
"description": "modern downtown apartment"
},
"limit": 10
}
```
- Provide `search_term` or `search_json`.
- Optional `search_params` (or `searchParams`) prefilters by structured fields (validated against the template schema) before vector similarity.
- Optional `min_results` ensures at least that many results are requested (may still return fewer if the template has fewer matches).
**Success response (200)**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"structured_filters": {},
"matches": [
{ "id": "A1", "score": 0.88 },
{ "id": "A2", "score": 0.73 }
]
}
```
**Common errors**
- `400` missing `template_id` or `search_term`/`search_json`.
- `401` invalid API key.
- `402` insufficient credits.
- `404` template not found.
---
## GET /chat/embedding_templates
List embedding templates available for the API key.
**Query params**
- `limit` optional integer (default 50, max 100).
- `offset` optional integer (default 0).
**Sample request**
```bash
curl -X GET "https://api.samsar.one/v1/chat/embedding_templates?limit=25&offset=0" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
**Success response (200)**
```json
{
"templates": [
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"name": "listings",
"template_hash": "b0e6f2d2c2f1d0c4d9f7f3a1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
"hash_link": "embedding_template:b0e6f2d2c2f1d0c4d9f7f3a1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
"record_count": 3,
"structured_fields": [
{ "key": "city", "type": "string" },
{ "key": "price", "type": "number" }
],
"unstructured_fields": ["description"],
"embedding_model": "text-embedding-3-large",
"created_at": "2024-11-03T12:00:00.000Z",
"updated_at": "2024-11-03T12:10:00.000Z"
}
],
"pagination": {
"total": 1,
"limit": 25,
"offset": 0,
"has_more": false
}
}
```
**Common errors**
- `401` invalid API key.
---
## GET /chat/embedding_status
Check whether embeddings exist for a template id.
**Query params**
- `template_id` required string.
**Sample request**
```bash
curl -X GET "https://api.samsar.one/v1/chat/embedding_status?template_id=66f3b0d64c8bca9c6f2bd1a3" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
**Success response (200)**
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"has_embeddings": true,
"record_count": 3,
"status": "ready"
}
```
**Common errors**
- `400` missing `template_id`.
- `401` invalid API key.
- `404` template not found.
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/credits-api.mdx
METADATA:
title: Credits API
description: Check credit balances, create recharge links, and manage auto-recharge controls through the Samsar credits API.
image: img/og/credits-api.png
sidebar_position: 4
slug: /credits
sidebar_label: Credits API
PRIMARY_HEADING: Credits API
CONTENT_START
---
title: Credits API
description: "Check credit balances, create recharge links, and manage auto-recharge controls through the Samsar credits API."
image: img/og/credits-api.png
sidebar_position: 4
slug: /credits
sidebar_label: Credits API
---
import DocTopActions from '@site/src/components/DocTopActions';
# Credits API
- Base URL: `https://api.samsar.one/v1`
- Auth: `Authorization: Bearer `
- Content-Type: `application/json`
Credits are billed at **1 USD = 100 credits**. Use these endpoints to check remaining credits and to generate a Stripe checkout link that tops up credits through the webhook.
## GET /credits
Fetch the current credit balance and the most recent top-up (if available).
**Sample request**
```bash
curl https://api.samsar.one/v1/credits \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
**Success response (200)**
```json
{
"remainingCredits": 4920,
"lastTopUp": {
"id": "66e21f9f1f3b0f0001c7a1a2",
"amountPaidCents": 5000,
"currency": "USD",
"paymentType": "one_time",
"paymentStatus": "paid",
"billingReason": "one_time",
"creditsApplied": 5000,
"paymentDate": "2024-09-12T10:08:11.000Z",
"stripeInvoiceId": "in_1Pxxx",
"stripeInvoiceNumber": "B7A2-1",
"invoicePdfUrl": "https://...",
"hostedInvoiceUrl": "https://...",
"receiptUrl": "https://...",
"receiptAvailable": true,
"productSummary": "Purchase 5000 credits"
}
}
```
**Response headers**
- `x-credits-remaining` returns the current balance.
## POST /credits/recharge
Generate a Stripe checkout link to recharge a specific number of credits.
**Request body**
```json
{
"credits": 2500
}
```
- `credits` is required, positive, and must be an integer.
- Price is calculated as `credits / 100` in USD.
- Use `/payment_status` with `checkoutSessionId` or `paymentIntentId` to poll for completion.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/credits/recharge \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"credits": 2500}'
```
**Success response (200)**
```json
{
"url": "https://checkout.stripe.com/c/pay/cs_test_...",
"checkoutSessionId": "cs_test_123",
"paymentIntentId": "pi_123",
"paymentStatusEndpoint": "/v1/payment_status",
"credits": 2500,
"amountUsd": 25,
"amountCents": 2500,
"currency": "USD"
}
```
**Common errors**
- `400` invalid or missing `credits`.
- `401` invalid API key.
## POST /enable_autorecharge
Enable or update auto-recharge settings for the API key. If no saved payment method exists, the response includes a Stripe setup URL to complete the enablement flow.
**Request body**
```json
{
"thresholdCredits": 1000,
"amountUsd": 50,
"maxMonthlyUsd": 200,
"requestSetupSession": true
}
```
- `thresholdCredits` (optional): auto-recharge triggers when credits fall below this value.
- `amountUsd` (optional): charge amount in USD for each auto-recharge (1 USD = 100 credits).
- `maxMonthlyUsd` (optional): cap auto-recharge spend per calendar month. Set to `0` to disable the cap.
- `requestSetupSession` (optional): force a Stripe setup session if no payment method exists.
- You can also send the payload under `input` (e.g. `{ "input": { ... } }`).
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/enable_autorecharge \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"thresholdCredits": 1000,
"amountUsd": 50,
"maxMonthlyUsd": 200,
"requestSetupSession": true
}'
```
**Success response (200)**
```json
{
"autoRechargeEnabled": false,
"autoRechargeAmountUsd": 50,
"autoRechargeThreshold": 1000,
"autoRechargeMaxMonthlyUsd": 200,
"stripeCustomerId": "cus_123",
"setupSessionUrl": "https://checkout.stripe.com/c/setup/...",
"setupSessionId": "cs_test_456",
"setupIntentId": "seti_123",
"paymentStatusEndpoint": "/v1/payment_status",
"url": "https://checkout.stripe.com/c/setup/...",
"hasPaymentMethod": false,
"autoRechargeRun": null
}
```
**Notes**
- After Stripe setup completes, auto-recharge is enabled via the webhook and will trigger when credits drop below the threshold.
- If `maxMonthlyUsd` is set, auto-recharges are capped to the remaining monthly allowance; charges are skipped once the cap is reached.
- Use `/payment_status` with `setupSessionId` or `setupIntentId` to poll setup completion.
## GET /payment_status
Poll for the status of a Stripe checkout, payment intent, or setup intent created by the credits endpoints.
**Query params**
- `checkoutSessionId` (or `checkout_session_id`): Stripe Checkout Session id.
- `paymentIntentId` (or `payment_intent_id`): Stripe PaymentIntent id.
- `setupIntentId` (or `setup_intent_id`): Stripe SetupIntent id (auto-recharge setup).
**Sample request**
```bash
curl "https://api.samsar.one/v1/payment_status?checkoutSessionId=cs_test_123" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
**Success response (200)**
```json
{
"status": "pending",
"mode": "payment",
"checkoutSessionId": "cs_test_123",
"sessionStatus": "open",
"paymentStatus": "unpaid",
"paymentIntentId": "pi_123",
"paymentIntentStatus": "requires_payment_method",
"amountCents": 2500,
"currency": "usd"
}
```
**Status values**
- `pending`: still in progress.
- `succeeded`: payment/setup completed.
- `failed`: payment/setup failed or session expired.
**Common errors**
- `400` missing status identifier.
- `403` session/intent does not belong to the requesting API key.
## POST /auto_recharge/threshold
Update the auto-recharge threshold for an account that already has auto-recharge enabled.
**Request body**
```json
{
"thresholdCredits": 1500
}
```
- `thresholdCredits` is required and must be a non-negative number.
- Auto-recharge must already be enabled, otherwise the endpoint returns a 400 error.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/auto_recharge/threshold \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"thresholdCredits": 1500}'
```
**Success response (200)**
```json
{
"autoRechargeEnabled": true,
"autoRechargeThreshold": 1500,
"autoRechargeAmountUsd": 50,
"autoRechargeMaxMonthlyUsd": 200
}
```
**Common errors**
- `400` auto-recharge is not enabled (enable via `/enable_autorecharge` or the billing page).
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/external-requests-api.mdx
METADATA:
title: External Requests API
description: Use a Samsar API key as a central fallback provider for chat, image, video, and deployment onboarding requests.
image: img/og/external-requests-api.png
sidebar_position: 6
slug: /external-requests
sidebar_label: External Requests API
PRIMARY_HEADING: External Requests API
CONTENT_START
---
title: External Requests API
description: "Use a Samsar API key as a central fallback provider for chat, image, video, and deployment onboarding requests."
image: img/og/external-requests-api.png
sidebar_position: 6
slug: /external-requests
sidebar_label: External Requests API
---
import DocTopActions from '@site/src/components/DocTopActions';
# External Requests API
- Base URL: `https://api.samsar.one`
- Canonical route prefix: `/v2/external`
- Auth: `Authorization: Bearer `
- Content-Type: `application/json`
- Billing: requests are billed to the Samsar account behind the API key using the same model pricing as the corresponding Studio action.
- Compatibility: the legacy `/external/*` aliases may exist on some deployments, but new integrations should use `/v2/external/*`.
Use this surface when a deployed SamsarOne environment wants to call the central Samsar service as a fallback provider. Native provider credentials can still be used directly by a deployment; when they are present, they override the corresponding model settings for that deployment.
This is different from `/external_users/*`. External users track per-customer credits, history, and login state. External requests are one-off provider-style calls authenticated by a Samsar API key.
## Validate a Samsar API key
`GET /v2/external/api_key/validate`
```bash
curl "https://api.samsar.one/v2/external/api_key/validate" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
Success response:
```json
{
"valid": true,
"authType": "api_key",
"email": "admin@example.com",
"remainingCredits": 12000
}
```
## Provider capabilities
`GET /v2/external/providers/capabilities`
Returns the provider, model, and action capability map used by deployment onboarding.
```bash
curl "https://api.samsar.one/v2/external/providers/capabilities"
```
## Validate deployment providers
`POST /v2/external/providers/validate`
Use this from the local deployment wizard to validate optional provider credentials before enabling model settings. The SDK sends credentials only to the local processor or configured Samsar API host; do not persist provider secrets in client code.
```bash
curl -X POST "https://api.samsar.one/v2/external/providers/validate" \
-H "Content-Type: application/json" \
-d '{
"samsarApiKey": "sk_live_...",
"openaiApiKey": "sk-...",
"falApiKey": "fal_...",
"runwayApiKey": "rw_..."
}'
```
Success response:
```json
{
"providers": {
"samsar": {
"ok": true,
"status": "validated",
"remainingCredits": 12000
}
},
"available": {
"providers": ["samsar"],
"models": ["gpt-5.5", "gemini-3.1-pro"],
"actions": ["chat", "image", "video", "audio"]
}
}
```
## Chat
### External assistant and chat completions
The external assistant endpoints accept OpenAI-compatible chat completion payloads. They are intended for deployed Samsar environments and other integrations that use Samsar as a managed inference provider.
The `/assistant/*` paths below are naming aliases for the same provider-style chat handler. They are separate from the session-bound [`POST /v1/assistant/completion`](/assistant-api#post-assistantcompletion) API.
### Routes
Create a completion with any of these `POST` routes:
- `/v2/external/chat`
- `/v2/external/chat/completions` — preferred for OpenAI-compatible integrations
- `/v2/external/assistant`
- `/v2/external/assistant/completions`
Read queued request status with any of these `GET` routes:
- `/v2/external/chat/status`
- `/v2/external/chat/completions/status`
- `/v2/external/assistant/status`
The same routes are available under `/api/v2/external`. Legacy deployments may also expose the unversioned `/external` prefix. There is no `/v2/external/assistant/completions/status` route; use `/v2/external/assistant/status` or the canonical `/v2/external/chat/status` path.
### Synchronous completion
Polling is opt-in. Without a polling control, the endpoint waits for inference and returns the raw OpenAI-compatible completion with HTTP `200`.
```bash
curl -X POST "https://api.samsar.one/v2/external/chat/completions" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.6-sol",
"messages": [
{ "role": "user", "content": "Write a launch caption for a travel reel." }
],
"max_tokens": 300
}'
```
The default server execution timeout is 10 minutes. `stream: true` is not supported. When available, billing is returned through these headers:
- `x-credits-charged`
- `x-credits-remaining`
### Queued completion with polling
Use polling for long-running assistant work such as narrative or theme generation. It avoids holding one HTTP connection open for the entire model call and allows the request to recover after a client disconnect, network reset, or processor restart.
Generate and persist a stable `client_request_id` before submitting. Retry the same logical submission with the same ID if the initial response is lost.
```bash
curl -X POST "https://api.samsar.one/v2/external/assistant/completions" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.6-sol",
"messages": [
{ "role": "user", "content": "Create a cinematic theme for this video." }
],
"response_mode": "polling",
"client_request_id": "video-67df0d8e:theme:v1",
"client_session_id": "video-67df0d8e",
"client_request_key": "theme"
}'
```
`async: true`, `poll: true`, or `response_mode: "async" | "poll" | "polling"` all enable queued mode. Prefer `response_mode: "polling"` for new integrations.
The canonical fields are:
| Field | Purpose |
| --- | --- |
| `client_request_id` | Optional idempotency key. Make it unique for one logical request and reuse it only when retrying that request. |
| `client_session_id` | Optional local session correlation value. It is persisted and echoed only when `client_request_id` is present. |
| `client_request_key` | Optional local stage or operation name, such as `theme` or `narrative`. It is persisted and echoed only when `client_request_id` is present. |
| `timeout` / `timeoutMs` | Optional server execution timeout in milliseconds. The default is `600000` (10 minutes). |
A queued request returns HTTP `202`:
```json
{
"request_id": "67df0d8ebc4f9d0b7f4fd123",
"requestId": "67df0d8ebc4f9d0b7f4fd123",
"status": "PENDING",
"poll_url": "/v2/external/chat/status?request_id=67df0d8ebc4f9d0b7f4fd123",
"client_request_id": "video-67df0d8e:theme:v1",
"client_session_id": "video-67df0d8e",
"client_request_key": "theme",
"created_at": "2026-07-16T13:00:00.000Z",
"updated_at": "2026-07-16T13:00:00.000Z"
}
```
`poll_url` is a relative canonical path. Store `request_id` as soon as it is returned. If the submit response is lost, you can still look up the request by the persisted `client_request_id`.
Poll by provider request ID:
```bash
curl "https://api.samsar.one/v2/external/chat/status?request_id=67df0d8ebc4f9d0b7f4fd123" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
Or recover by client request ID:
```bash
curl "https://api.samsar.one/v2/external/chat/status?client_request_id=video-67df0d8e%3Atheme%3Av1" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
Status reads are immediate rather than long-polling. Poll every 1–2 seconds until the request reaches a terminal state:
| Status | Meaning |
| --- | --- |
| `PENDING` | Persisted and waiting to be claimed. Polling also repairs a request persisted immediately before a processor restart. |
| `PROCESSING` | Claimed by the hosted processor. An expired worker lease can be reclaimed safely. |
| `COMPLETED` | Terminal success. Read the OpenAI-compatible completion from `response`. |
| `FAILED` | Terminal failure. Read the provider-safe details from `error`. |
Completed status response:
```json
{
"request_id": "67df0d8ebc4f9d0b7f4fd123",
"status": "COMPLETED",
"response": {
"id": "chatcmpl_123",
"object": "chat.completion",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "A precise cinematic theme..."
},
"finish_reason": "stop"
}
]
},
"creditsCharged": 12,
"remainingCredits": 9988
}
```
Failed status response (HTTP `200`):
```json
{
"request_id": "67df0d8ebc4f9d0b7f4fd123",
"status": "FAILED",
"error": {
"message": "External assistant request failed.",
"code": null,
"status": 500
}
}
```
Request status is retained for 24 hours from creation. Idempotent submission replays return the original request and do not replace its payload; use a new `client_request_id` for an intentional retry after `FAILED` or `COMPLETED`. Fetch status to obtain completion credit headers and body fields, including after an idempotent replay.
Common errors:
- `400` missing authentication headers, missing or invalid `messages`, unsupported `stream: true`, or an invalid status lookup ID.
- `401` invalid or expired credentials.
- `402` insufficient credits for a synchronous request.
- `403` the supplied credential type is not supported by this provider route.
- `404` request not found for the authenticated account.
- Async provider failures are represented by HTTP `200` with `status: "FAILED"` and the nested `error` object.
## Image
`POST /v2/external/image/{image_route}`
The external image surface mirrors the existing Image API under the `/v2/external/image` prefix. Common routes include:
- `assign_title`
- `enhance`
- `remove_branding`
- `add_image_set`
- `receipt_templates/create`
- `receipt_templates/query`
- `create_rollup_banner`
Example:
```bash
curl -X POST "https://api.samsar.one/v2/external/image/enhance" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image_url": "https://cdn.example.com/photo.png",
"resolution": "1k"
}'
```
Async image routes return a `request_id`. Poll:
```bash
curl "https://api.samsar.one/v2/external/image/status?request_id=img_enh_123" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
## Video
`POST /v2/external/video/{video_route}`
The external video surface exposes standalone media-generation routes for remote deployments that use a Samsar API key as a provider. These routes do not use the internal `/v2/video/step/*` pipeline and do not expose generic video-session mutation routes.
| Route | Purpose |
| --- | --- |
| `POST /v2/external/video/text_to_video` | Create an express text-to-video request. |
| `POST /v2/external/video/image_to_video` | Create an express image-to-video request from one or more public image URLs. |
| `POST /v2/external/video/lip_sync` | Create a lip-sync video-to-video request from public `video_url` and `audio_url` inputs. |
| `POST /v2/external/video/sound_effect` | Create a sound-effect video-to-video request from a public `video_url` input and prompt. |
| `GET /v2/external/video/status` | Poll by `request_id` or `session_id`. |
| `GET /v2/external/video/{request_id}/status` | Path-param status polling alias. |
| `GET /v2/external/video/status_detailed` | Poll detailed status plus normalized video preview data. |
| `GET /v2/external/video/{request_id}/status_detailed` | Path-param detailed status polling alias. |
`status` and `status_detailed` also accept `POST` with `request_id` or `session_id` in the JSON body.
For `lip_sync` and `sound_effect`, media inputs must be publicly reachable HTTP(S) URLs. Raw bytes, base64/data URLs, local filesystem paths, localhost URLs, and private network URLs are rejected. Docker deployments should publish local media through configured S3-compatible storage, CloudFront, or another signed/public URL strategy before calling these hosted external routes.
Example:
```bash
curl -X POST "https://api.samsar.one/v2/external/video/text_to_video" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"prompt": "A cinematic product launch reel",
"image_model": "GPTIMAGE2",
"video_model": "RUNWAYML",
"duration": 10,
"aspect_ratio": "9:16",
"inference_model": "gpt-5.5"
},
"webhookUrl": "https://example.com/webhook"
}'
```
Image-to-video example:
```bash
curl -X POST "https://api.samsar.one/v2/external/video/image_to_video" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"image_urls": [
"https://cdn.example.com/scene-1.png",
"https://cdn.example.com/scene-2.png"
],
"prompt": "A polished destination reel",
"video_model": "RUNWAYML",
"aspect_ratio": "16:9"
}
}'
```
Lip-sync example:
```bash
curl -X POST "https://api.samsar.one/v2/external/video/lip_sync" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"video_url": "https://cdn.example.com/clip.mp4",
"audio_url": "https://cdn.example.com/dialogue.wav",
"lip_sync_model": "SYNCLIPSYNC",
"duration": 8,
"prompt": "Match the speaker performance to the supplied audio."
}
}'
```
Sound-effect example:
```bash
curl -X POST "https://api.samsar.one/v2/external/video/sound_effect" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"video_url": "https://cdn.example.com/clip.mp4",
"prompt": "Soft ocean waves and distant city ambience",
"sound_effect_model": "MIRELOAI",
"duration": 8
}
}'
```
Poll:
```bash
curl "https://api.samsar.one/v2/external/video/status?request_id=66ff..." \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
Detailed status:
```bash
curl "https://api.samsar.one/v2/external/video/status_detailed?request_id=66ff..." \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
## Audio
`GET /v2/external/audio/voices`
List available audio/TTS voices keyed by provider. This endpoint is intended for external deployments that use a Samsar API key instead of native provider credentials.
This route requires a Samsar API key and does not charge credits.
Query params:
- `languageCode`: optional Google TTS language filter, for example `en-US`.
- `refresh`: optional `true` to refresh the Google voice catalog when the serving deployment has Google credentials.
```bash
curl "https://api.samsar.one/v2/external/audio/voices?languageCode=en-US" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
The response contains `providers` and `voicesByProvider` objects keyed by provider names such as `OPENAI`, `ELEVENLABS`, `PLAYAI`, and `GOOGLE`. The Google catalog includes `voices`, `voiceMap`, `count`, `source`, and `generatedAt`.
`POST /v2/external/audio/text_to_music`
Create a hosted text-to-music request. This is intended for external or Docker deployments that use a Samsar API key instead of native ElevenLabs/FAL or Google Lyria credentials.
Supported models:
- `ELEVENLABS_MUSIC`
- `LYRIA3` (`LYRIA2` is accepted as an alias)
Pricing follows Studio music pricing:
| Model | Credits |
| --- | ---: |
| `ELEVENLABS_MUSIC` | 3 |
| `LYRIA3` | 3 |
```bash
curl -X POST "https://api.samsar.one/v2/external/audio/text_to_music" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"prompt": "Warm cinematic synth backing track, instrumental only",
"model": "ELEVENLABS_MUSIC",
"duration": 30,
"is_instrumental": true
}
}'
```
Poll:
```bash
curl "https://api.samsar.one/v2/external/audio/status?request_id=66ff..." \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
## samsar-js
```ts
import SamsarClient from 'samsar-js';
const samsar = new SamsarClient({ apiKey: process.env.SAMSAR_API_KEY! });
await samsar.validateV2ExternalSamsarApiKey();
const chat = await samsar.createV2ExternalChatCompletion({
model: 'gpt-5.5',
messages: [{ role: 'user', content: 'Write a concise product caption.' }],
});
const video = await samsar.createV2ExternalVideoRequestFromText({
prompt: 'A cinematic product launch reel',
image_model: 'GPTIMAGE2',
video_model: 'RUNWAYML',
duration: 10,
});
const status = await samsar.getV2ExternalVideoStatus(video.data.request_id!);
const imageVideo = await samsar.createV2ExternalVideoRequestFromImage({
image_url: 'https://cdn.example.com/scene-1.png',
video_model: 'RUNWAYML',
});
const lipSync = await samsar.createV2ExternalLipSyncVideo({
video_url: 'https://cdn.example.com/clip.mp4',
audio_url: 'https://cdn.example.com/dialogue.wav',
lip_sync_model: 'SYNCLIPSYNC',
});
const soundEffect = await samsar.createV2ExternalSoundEffectVideo({
video_url: 'https://cdn.example.com/clip.mp4',
prompt: 'Soft ocean waves and distant city ambience',
sound_effect_model: 'MIRELOAI',
});
const detailed = await samsar.getV2ExternalVideoStatusDetailed(lipSync.data.request_id!);
await samsar.requestV2ExternalImage('enhance', {
image_url: 'https://cdn.example.com/photo.png',
resolution: '1k',
});
const voices = await samsar.listV2ExternalAudioVoices({ languageCode: 'en-US' });
const music = await samsar.createV2ExternalTextToMusicAudio({
prompt: 'Warm cinematic synth backing track, instrumental only',
model: 'ELEVENLABS_MUSIC',
duration: 30,
is_instrumental: true,
});
const audioStatus = await samsar.getV2ExternalAudioStatus(music.data.request_id!);
```
The shorter aliases also use the v2 external routes:
- `validateSamsarApiKey`
- `getExternalProviderCapabilities`
- `validateDeploymentProviders`
- `createExternalChat`
- `createExternalChatCompletion`
- `requestExternalImage`
- `requestExternalVideo`
- `createExternalVideoRequestFromText`
- `createExternalVideoRequestFromImageList`
- `createExternalVideoRequestFromImage`
- `createExternalLipSyncVideo`
- `createExternalSoundEffectVideo`
- `getExternalImageStatus`
- `getExternalVideoStatus`
- `getExternalVideoStatusDetailed`
- `getExternalVideoDetailedStatus`
- `listExternalAudioVoices`
- `requestExternalAudio`
- `requestExternalAudioRoute`
- `createExternalTextToMusicAudio`
- `getExternalAudioStatus`
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/external-users-api.mdx
METADATA:
title: External Users API
description: Attribute shared Samsar usage to external platform users with scoped credits, login tokens, and request history.
image: img/og/external-users-api.png
sidebar_position: 5
slug: /external-users
sidebar_label: External Users API
PRIMARY_HEADING: External Users API
CONTENT_START
---
title: External Users API
description: "Attribute shared Samsar usage to external platform users with scoped credits, login tokens, and request history."
image: img/og/external-users-api.png
sidebar_position: 5
slug: /external-users
sidebar_label: External Users API
---
import DocTopActions from '@site/src/components/DocTopActions';
# External Users API
- Base URL: `https://api.samsar.one/v1`
- Platform auth: `Authorization: Bearer `
- Scoped external auth: `x-external-user-api-key: `
- Client auth after login exchange: `Authorization: Bearer `
- Content-Type: `application/json`
- Billing: upstream model calls still execute against the owning Samsar account internally, while credits, purchases, refunds, request history, and library actions are tracked on each `external_user`
Use this surface when many users share one central Samsar account, but each external user still needs their own credits, request history, payments, publishing controls, and login session.
## External user identity
Most external endpoints accept an `external_user` object:
```json
{
"external_user": {
"provider": "whop",
"unique_key": "whop:usr_123",
"external_user_id": "usr_123",
"external_app_id": "app_abc",
"external_company_id": "biz_456",
"email": "member@example.com",
"username": "member_handle",
"display_name": "Member Name",
"metadata": {
"membership_id": "mem_123"
}
}
}
```
Required fields:
- `provider`
- `external_user_id`
Optional stable reference:
- `unique_key`: a globally unique reference for the external user, such as a wallet address, email address, or platform user key. If omitted, Samsar stores `external_user_id` as the `unique_key`.
Recommended for multi-app providers:
- `external_app_id`
- `external_company_id`
The first successful bootstrap request upserts an `ExternalUser` record mapped to the owning Samsar account internally. The stored record keeps both the owning internal user id and the external user's `unique_key`.
For V2 clients that only need to register and reference an external user by a single key, use `POST /v2/user/create_external_user` with an internal API key, user auth token, or APP_KEY:
```bash
curl -X POST https://api.samsar.one/v2/user/create_external_user \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"unique_key": "wallet:0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"email": "member@example.com",
"display_name": "Member Name"
}'
```
The response includes `external_user.unique_key` and a `reference` object. Future V2 calls can pass that `unique_key` to resolve the external user.
## Auth model
There are two supported auth patterns.
### 1. Server-to-server platform flow
1. Your backend calls `POST /external_users/session` with the shared platform API key plus an `external_user` object.
2. Samsar upserts the external user and returns an `external_api_key`.
3. Subsequent external-user scoped requests include:
- `Authorization: Bearer `
- `x-external-user-api-key: `
### 2. Client login flow
1. Your backend calls `POST /external_users/create_login_token`.
2. Samsar returns a short-lived `loginToken` and a ready-to-open `loginUrl`.
3. The client exchanges that `loginToken` through `GET /users/verify_token?loginToken=...`.
4. The response contains a long-lived `authToken`.
5. The client can call `/v1/external_users/*` routes with:
- `Authorization: Bearer `
The platform API key should stay server-side. Only the external auth token should reach the browser.
## Response conventions
External generation endpoints return external request ids:
```json
{
"request_id": "extreq_...",
"session_id": "extreq_...",
"external_request_id": "extreq_...",
"external_session_id": "extreq_...",
"upstream_request_id": "66ff...",
"upstream_session_id": "66ff...",
"status_endpoint": "/v1/external_users/status?request_id=extreq_..."
}
```
- `request_id` / `session_id` are the external ids you should store client-side.
- `upstream_request_id` / `upstream_session_id` are the internal Samsar session ids.
- Poll `GET /external_users/status` with the external request id.
## POST /external_users/session
Create or refresh an external-user session and receive that user’s dedicated API key plus current credits.
```bash
curl -X POST https://api.samsar.one/v1/external_users/session \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"external_user": {
"provider": "whop",
"unique_key": "whop:usr_123",
"external_user_id": "usr_123",
"external_app_id": "app_abc"
}
}'
```
Success response:
```json
{
"external_api_key": "sxu_********************************",
"remainingCredits": 1000,
"external_user": {
"provider": "whop",
"unique_key": "whop:usr_123",
"external_user_id": "usr_123",
"generation_credits": 1000,
"has_external_api_key": true
}
}
```
## POST /external_users/create_login_token
Create a short-lived external-user login token and a ready-to-open client login URL.
```bash
curl -X POST https://api.samsar.one/v1/external_users/create_login_token \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"redirect": "/external/studio"
}'
```
Success response:
```json
{
"loginToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresInSeconds": 600,
"expiresAt": "2026-03-21T12:00:00.000Z",
"loginUrl": "https://app.samsar.one/verify?loginToken=...&redirect=%2Fexternal%2Fstudio",
"external_user": {
"provider": "whop",
"external_user_id": "usr_123"
}
}
```
## GET /users/verify_token
Exchange the external `loginToken` for a long-lived external `authToken`.
```bash
curl "https://api.samsar.one/users/verify_token?loginToken=LOGIN_TOKEN_****************"
```
Success response:
```json
{
"_id": "69bbb64111f47422c1083360",
"authToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"isExternalUser": true,
"provider": "whop",
"externalUserId": "user_46cF7yVLkPms5",
"externalAppId": "app_8jnV5WOvSLgyLr",
"generationCredits": 3000
}
```
Once you have this `authToken`, call external-user routes with:
```bash
-H "Authorization: Bearer EXTERNAL_AUTH_TOKEN_****************"
```
## POST /external_users/assistant/set_system_prompt
Store or clear an assistant system prompt for one external user. This prompt is applied to that external user’s future assistant requests and overrides the owning account prompt for that external user only.
```bash
curl -X POST https://api.samsar.one/v1/external_users/assistant/set_system_prompt \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"system_prompt": "You are the storefront assistant for this creator. Keep answers concise, visually aware, and commercially practical."
}'
```
Success response:
```json
{
"system_prompt": "You are the storefront assistant for this creator. Keep answers concise, visually aware, and commercially practical.",
"model": "gpt-5.5",
"selected_assistant_model": "gpt-5.5",
"external_user": {
"provider": "whop",
"external_user_id": "usr_123"
}
}
```
Pass `null` or an empty string to clear the external-user-specific prompt and fall back to the owning account prompt or Samsar’s minimal default prompt.
## POST /external_users/assistant/completion
Create a synchronous assistant response for an external user. Credits are deducted from the external user balance, while the owning Samsar account’s configured assistant model is used internally.
- `session_id` is required.
- You can send the external request/session id returned by external generation routes, or the upstream internal session id.
- Request and response format match the standard Assistant API, including multimodal input and image-generation tool calls.
- Pricing follows the same assistant logic as the main Assistant API: actual usage is converted with **100 credits = $1** and multiplied by **2.5x**.
```bash
curl -X POST https://api.samsar.one/v1/external_users/assistant/completion \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"session_id": "extreq_123",
"input": "Write a product caption for this video and suggest a headline.",
"max_output_tokens": 250
}'
```
Success response:
```json
{
"id": "resp_123",
"object": "response",
"created_at": 1773651000,
"status": "completed",
"model": "gpt-5.5",
"output_text": "Built to move fast: bold design, clean energy, and a launch-ready look in one line.",
"output": [
{
"id": "resp_123_message",
"type": "message",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Built to move fast: bold design, clean energy, and a launch-ready look in one line.",
"annotations": []
}
]
}
],
"usage": {
"input_tokens": 482,
"output_tokens": 43,
"total_tokens": 525
}
}
```
Response headers:
- `x-credits-charged`
- `x-credits-remaining`
## POST /external_users/generate_embeddings_from_plain_text
Create embeddings for one external user from already cleaned plain text. This route skips crawling entirely and uses the same compatible embedding template format as the main chat embedding endpoints.
- Request body accepts the same cleaned text shapes as `/chat/generate_embeddings_from_plain_text`.
- External-user credits are charged on the external balance only.
- Pricing is the embedding token cost with a **2.5x** multiplier and no crawl charges.
```bash
curl -X POST https://api.samsar.one/v1/external_users/generate_embeddings_from_plain_text \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"input": {
"name": "product-docs-clean",
"plain_text": [
{
"url": "https://example.com/docs/getting-started",
"title": "Getting Started",
"content": "Cleaned plain text content from the page."
}
]
}
}'
```
Success response:
```json
{
"template_id": "66f3b0d64c8bca9c6f2bd1a3",
"template_hash": "b0e6f2d2c2f1d0c4d9f7f3a1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
"hash_link": "embedding_template:b0e6f2d2c2f1d0c4d9f7f3a1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9",
"record_count": 1,
"structured_fields": [
{ "key": "hostname", "type": "string" }
],
"unstructured_fields": ["title", "content"],
"external_user": {
"provider": "whop",
"external_user_id": "usr_123",
"generation_credits": 980
}
}
```
Response headers:
- `x-credits-charged`
- `x-credits-remaining`
## POST /external_users/text_to_video
Create a text-to-video request attributed to an external user.
```bash
curl -X POST https://api.samsar.one/v1/external_users/text_to_video \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"external_user": {
"provider": "whop",
"external_user_id": "usr_123",
"external_app_id": "app_abc"
},
"input": {
"prompt": "A cinematic teaser for a new sneaker drop",
"image_model": "GPTIMAGE2",
"video_model": "RUNWAYML",
"duration": 10,
"tone": "grounded",
"aspect_ratio": "16:9",
"language": "en",
"enable_subtitles": true,
"font_key": "Poppins",
"generate_outro_image": true,
"cta_url": "https://example.com/drop",
"cta_text_top": "Scan to shop",
"cta_text_bottom": "Limited release",
"add_footer_animation": true,
"footer_metadata": [
{ "url": "https://example.com/drop", "title": "Shop the drop" }
]
}
}'
```
`input` accepts the same text-to-video parameters as `POST /video/text_to_video`, including optional `backingtrack_model` / `music_provider`, optional `tts_model` / `tts_provider`, optional `speakerOptions` / `speaker_options` for request-level speaker selection, optional `inference_model` / `inferenceModel` (`gpt-5.5` or `gemini-3.1-pro`; omitted uses the account default), provided outro image fields, generated CTA outro fields (`generate_outro_image` with `cta_url` for a QR center image or `outro_cta_image` for a supplied center logo/CTA image), and bottom CTA footer fields (`add_footer_animation` with `footer_metadata`). `outro_cta_image` uses `{ "top_text", "middle_image", "bottom_text" }`. `video_model_sub_type` is not part of the request shape. Credits are reserved on the external user immediately after validation. Any unused amount is refunded when the request is cancelled, fails, or finishes below the provisional estimate.
## POST /external_users/upload_image_data
Upload image data URLs for an external user before calling `image_list_to_video`.
```json
{
"external_user": {
"provider": "whop",
"external_user_id": "usr_123",
"external_app_id": "app_abc"
},
"input": {
"image_data": [
"data:image/png;base64,..."
]
}
}
```
Success response:
```json
{
"image_urls": [
"https://static.samsar.one/..."
]
}
```
## POST /external_users/image_list_to_video
Create a video from an ordered list of images attributed to an external user.
```bash
curl -X POST https://api.samsar.one/v1/external_users/image_list_to_video \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"external_user": {
"provider": "whop",
"external_user_id": "usr_123",
"external_app_id": "app_abc"
},
"input": {
"image_urls": [
"https://cdn.example.com/frame1.png",
"https://cdn.example.com/frame2.png"
],
"prompt": "Fast product reveal pacing",
"video_model": "RUNWAYML",
"aspect_ratio": "9:16",
"metadata": {
"campaign": "drop-07"
},
"language": "en",
"enable_subtitles": true,
"font_key": "Poppins",
"limit_single_narrator": false,
"add_narrator_avatar": false
}
}'
```
`input` accepts the same image-list parameters as `POST /video/image_list_to_video`, including URL-string or object `image_urls`, `video_model`, `aspect_ratio`, `language`, `backingtrack_model` / `music_provider` (`LYRIA3` or `ELEVENLABS_MUSIC`), `tts_model` / `tts_provider` (`ELEVENLABS`, `OPENAI`, or `GOOGLE`), `speakerOptions` / `speaker_options` for request-level speaker selection, `inference_model` / `inferenceModel` (`gpt-5.5` or `gemini-3.1-pro`; omitted uses the account default), `enable_subtitles`, `font_key`, provided outro image fields, generated CTA outro fields (`generate_outro_image` with `cta_url` or `outro_cta_image`), express CTA generation (`express_cta_generation` with one `cta_url`), per-scene footer QR fields (`add_footer_animation` with `footer_metadata`), `limit_single_narrator`, and `add_narrator_avatar`. `video_model_sub_type` is not part of the request shape. Legacy `LYRIA2` backing-track values are treated as `LYRIA3`. `video_model` is optional and supports `VEO3.1I2V`, `VEO3.1I2VFAST`, `COSMOS3SUPERI2V`, `SEEDANCEI2V`, `KLINGIMGTOVID3PRO`, `KLINGIMGTOVIDTURBO`, `HAPPYHORSEI2V`, and `RUNWAYML`; if omitted, it defaults to `RUNWAYML`. `aspect_ratio` supports `16:9` and `9:16` and defaults to `16:9`. `add_narrator_avatar` automatically enables `limit_single_narrator`.
Use only one outro mode in a request: either `outro_image_url` with optional `add_outro_animation` / `add_outro_focus_area`, `generate_outro_image: true` with `cta_url` or `outro_cta_image`, or `express_cta_generation: true` with one `cta_url`. `outro_cta_image` uses `{ "top_text", "middle_image", "bottom_text" }`; `middle_image` accepts a public image URL or image data and is rendered in the same center area used by generated QR outros. Express CTA generation omits manual CTA text and footer metadata; Samsar generates concise footer CTA titles from each scene and clean outro CTA text from the session context before rendering. Billing uses the same standard image-list rates as the standard route: `VEO3.1I2V` is 60 credits/sec, `VEO3.1I2VFAST` is 36 credits/sec, `COSMOS3SUPERI2V` is 20 credits/sec, `SEEDANCEI2V` is 30 credits/sec, `KLINGIMGTOVID3PRO` is 36 credits/sec, `KLINGIMGTOVIDTURBO` is 36 credits/sec, `HAPPYHORSEI2V` is 36 credits/sec, and `RUNWAYML` is 30 credits/sec. Narrator avatar generation adds 4 credits/sec when `add_narrator_avatar` is true; express CTA generation adds 1 credit/sec when `express_cta_generation` is true. `x-credits-charged` and `x-credits-remaining` are returned on acceptance when available, just like the standard image-list flow.
## GET /external_users/status
Poll an external request until completion.
```bash
curl "https://api.samsar.one/v1/external_users/status?request_id=extreq_123" \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************"
```
Success response:
```json
{
"request_id": "extreq_123",
"session_id": "extreq_123",
"external_request_id": "extreq_123",
"upstream_session_id": "66ff...",
"status": "COMPLETED",
"result_url": "https://static.samsar.one/...",
"has_subtitles": true,
"result_language": "en",
"creditsCharged": 750,
"creditsRefunded": 0,
"remainingCredits": 4250
}
```
Completed video responses include `has_subtitles` and `result_language` when the upstream video session has that metadata. If a generation fails or completes with a refund, the external request record is updated to reflect the refund.
## GET /external_users/requests
Fetch recent render requests for one external user.
```bash
curl "https://api.samsar.one/v1/external_users/requests?provider=whop&external_user_id=usr_123&external_app_id=app_abc&limit=12" \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************"
```
Success response:
```json
{
"requests": [
{
"request_id": "extreq_123",
"route_key": "text_to_video",
"status": "COMPLETED",
"prompt": "A cinematic teaser for a new sneaker drop",
"video_url": "https://static.samsar.one/...",
"credits_charged": 750,
"credits_refunded": 0,
"remaining_credits": 4250,
"is_published": true,
"published_title": "Sneaker teaser"
}
],
"external_user": {
"provider": "whop",
"external_user_id": "usr_123"
}
}
```
## POST /external_users/publish
Publish a completed external-user request to the public library/feed.
```bash
curl -X POST https://api.samsar.one/v1/external_users/publish \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"request_id": "extreq_123",
"title": "Sneaker teaser",
"description": "Launch-day vertical cut",
"tags": ["launch", "footwear"]
}'
```
Success response:
```json
{
"request": {
"request_id": "extreq_123",
"is_published": true,
"published_title": "Sneaker teaser"
},
"publication": {
"_id": "pub_123"
}
}
```
## POST /external_users/archive
Archive an external-user request. This costs no credits and also removes the video from publications if it was already published.
```bash
curl -X POST https://api.samsar.one/v1/external_users/archive \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"request_id": "extreq_123"
}'
```
Success response:
```json
{
"request": {
"request_id": "extreq_123",
"status": "ARCHIVED"
}
}
```
## GET /external_users/credits
Fetch the remaining credits for one external user plus their attribution summary.
```bash
curl "https://api.samsar.one/v1/external_users/credits?provider=whop&external_user_id=usr_123&external_app_id=app_abc" \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************"
```
Success response:
```json
{
"remainingCredits": 4920,
"lastTopUp": {
"amountPaidCents": 5000,
"creditsApplied": 5000
},
"externalUser": {
"provider": "whop",
"external_user_id": "usr_123",
"generation_credits": 4920,
"total_requests": 4,
"total_credits_used": 1820,
"total_credits_refunded": 150,
"total_credits_purchased": 5000
}
}
```
## POST /external_users/credits/grant
Manually grant credits to an external user. This also credits the shared platform account internally so upstream requests stay in sync.
```bash
curl -X POST https://api.samsar.one/v1/external_users/credits/grant \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "Content-Type: application/json" \
-d '{
"external_user": {
"provider": "whop",
"external_user_id": "usr_123",
"external_app_id": "app_abc"
},
"input": {
"credits": 1000
}
}'
```
Success response:
```json
{
"creditsGranted": 1000,
"remainingCredits": 1000,
"internalRemainingCredits": 12000,
"externalUser": {
"provider": "whop",
"external_user_id": "usr_123",
"generation_credits": 1000
}
}
```
## POST /external_users/credits/recharge
Create a Stripe checkout link for an external-user credit purchase.
```json
{
"external_user": {
"provider": "whop",
"external_user_id": "usr_123",
"external_app_id": "app_abc"
},
"input": {
"credits": 2500
}
}
```
Success response:
```json
{
"external_payment_id": "extpay_123",
"url": "https://checkout.stripe.com/c/pay/...",
"checkoutSessionId": "cs_test_123",
"paymentIntentId": "pi_123",
"paymentStatusEndpoint": "/v1/external_users/payment_status",
"credits": 2500,
"amountUsd": 25,
"amountCents": 2500,
"currency": "USD"
}
```
## GET /external_users/payment_status
Poll the status of an external-user-attributed recharge.
```bash
curl "https://api.samsar.one/v1/external_users/payment_status?external_payment_id=extpay_123" \
-H "Authorization: Bearer YOUR_PLATFORM_API_KEY_****************" \
-H "x-external-user-api-key: YOUR_EXTERNAL_USER_API_KEY_****************"
```
Success response:
```json
{
"external_payment_id": "extpay_123",
"status": "succeeded",
"mode": "payment",
"checkoutSessionId": "cs_test_123",
"paymentIntentId": "pi_123",
"amountCents": 2500,
"currency": "usd",
"remainingCredits": 5000
}
```
When payment succeeds, credits are applied to the external user’s `generationCredits`. The underlying platform API key is not returned to the caller.
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/fonts.mdx
METADATA:
title: Fonts
description: Preview supported subtitle fonts by language.
sidebar_label: Fonts
slug: /fonts
PRIMARY_HEADING: Fonts
CONTENT_START
---
title: Fonts
description: Preview supported subtitle fonts by language.
sidebar_label: Fonts
slug: /fonts
---
import FontSample from '@site/src/components/FontSample';
# Fonts
Use these previews to choose subtitle fonts by language. Font keys are shown inside each image and match the values returned by `GET /supported_fonts`. Samples automatically switch between light and dark assets based on the docs theme.
## Default (default) {#default}
- **Rampart One**
- **Montserrat**
- **Arial**
- **sans-serif**
## English (en) {#en}
- **Poppins**
- **Montserrat**
- **Arial**
- **sans-serif**
## French (fr) {#fr}
- **Poppins**
- **Montserrat**
- **Arial**
- **sans-serif**
## Spanish (es) {#es}
- **Rampart One**
- **Montserrat**
- **Arial**
- **sans-serif**
## German (de) {#de}
- **Rampart One**
- **Montserrat**
- **Arial**
- **sans-serif**
## Portuguese (pt) {#pt}
- **Rampart One**
- **Montserrat**
- **Arial**
- **sans-serif**
## Italian (it) {#it}
- **Rampart One**
- **Montserrat**
- **Arial**
- **sans-serif**
## Dutch (nl) {#nl}
- **Rampart One**
- **Montserrat**
- **Arial**
- **sans-serif**
## Swedish (sv) {#sv}
- **Rampart One**
- **Montserrat**
- **Arial**
- **sans-serif**
## Russian (ru) {#ru}
- **Arial**
- **sans-serif**
## Japanese (ja) {#ja}
- **Noto Sans JP**
- **M PLUS Rounded 1c**
- **Hiragino Sans**
- **Yu Gothic UI**
- **sans-serif**
## Korean (ko) {#ko}
- **Noto Sans KR**
- **Pretendard**
- **Apple SD Gothic Neo**
- **Malgun Gothic**
- **sans-serif**
## Thai (th) {#th}
- **Sarabun**
- **Tahoma**
- **Leelawadee UI**
- **sans-serif**
## Arabic (ar) {#ar}
- **Noto Sans Arabic**
- **Cairo**
- **Geeza Pro**
- **Segoe UI**
- **sans-serif**
## Bengali (bn) {#bn}
- **Noto Sans Bengali**
- **Hind Siliguri**
- **Noto Serif Bengali**
- **sans-serif**
## Hindi (hi) {#hi}
- **Noto Sans Devanagari**
- **Mukta**
- **Hind**
- **Noto Serif Devanagari**
- **Kohinoor Devanagari**
- **sans-serif**
## Hebrew (he) {#he}
- **Noto Sans Hebrew**
- **Rubik**
- **Arial Hebrew**
- **sans-serif**
## Latin (la) {#la}
- **Rampart One**
- **Montserrat**
- **Arial**
- **sans-serif**
## Sanskrit (sa) {#sa}
- **Noto Sans Devanagari**
- **Mukta**
- **Hind**
- **Noto Serif Devanagari**
- **Kohinoor Devanagari**
- **sans-serif**
## Chinese (zh) {#zh}
- **Noto Sans SC**
- **Source Han Sans SC**
- **PingFang SC**
- **Microsoft YaHei**
- **sans-serif**
## Chinese (Simplified) (zh-cn) {#zh-cn}
- **Noto Sans SC**
- **Source Han Sans SC**
- **PingFang SC**
- **Microsoft YaHei**
- **sans-serif**
## Chinese (Traditional) (zh-tw) {#zh-tw}
- **Noto Sans TC**
- **Source Han Sans TC**
- **PingFang TC**
- **Microsoft JhengHei**
- **sans-serif**
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/image-api.mdx
METADATA:
title: Image API
description: Enhance, edit, white-label, validate receipts, and manage generated image sets with SamsarOne's image endpoints.
image: img/og/image-api.png
sidebar_position: 2
slug: /image-api
sidebar_label: Image API
PRIMARY_HEADING: Image API
CONTENT_START
---
title: Image API
description: "Enhance, edit, white-label, validate receipts, and manage generated image sets with SamsarOne's image endpoints."
image: img/og/image-api.png
sidebar_position: 2
slug: /image-api
sidebar_label: Image API
---
import DocTopActions from '@site/src/components/DocTopActions';
import PlaygroundRail from '@site/src/components/PlaygroundRail';
# Image API
Run enhance or branding removal calls in the Playground and poll their status before wiring them into code.
- Base URL: `https://api.samsar.one/v1`
- Auth: `Authorization: Bearer `
- Responses include `x-credits-charged` and `x-credits-remaining` when credits are deducted.
- Image generation and editing endpoints enqueue work; poll `/image/status?request_id=...` (or `/v1/status`) for completion, or use `/image/list` to fetch history.
- `POST /image/assign_title` is synchronous and returns a text-style `{ "content": "..." }` payload.
## Pricing
| Endpoint | Credits | Notes |
| ------------------------ | ------------------------------------------------- | ------------------------------------------------------------- |
| `POST /image/assign_title` | Usage-based credits | Image title generation. Returns synchronously. |
| `POST /image/enhance` | 11 (`0.5k`) / 15 (`1k`) / 22 (`2k`) / 29 (`4k`) | Based on `resolution` (defaults to `1k`). |
| `POST /image/remove_branding` | 15 | Remove visible text from images for whitelabel marketplace listings. |
| `POST /image/add_image_set` | `15 × requested_images` | `requested_images = num_images`. |
| `POST /image/receipt_templates/create` | Free | Builds and saves a receipt template + normalized schema from one sample image. |
| `GET /image/template_json` | Free | Returns the saved structured template JSON for a `template_id` owned by your API key. |
| `POST /image/receipt_templates/query` | 50 | Validates receipt against template and returns standardized JSON fields. |
| `POST /image/create_rollup_banner` | 30 | Flat per request. |
| `POST /image/create_rollup_banner_with_enhance` | Variable | Enhance credits for low-res images + 30 credits per rollup request. |
## POST /image/assign_title
Assign a short SEO-friendly title to an image. The title is suitable for an image title or for client-side filename generation after camel-casing.
This endpoint uses two generation steps:
1. Analyze the image in low-detail vision mode for naming-relevant visual cues.
2. Combine that description with optional metadata JSON to create one short title.
**JSON request body**
```json
{
"image_url": "https://cdn.example.com/product-photo.png",
"metadata": {
"product": "linen travel shirt",
"collection": "spring essentials"
}
}
```
You can also send `image_data` as a base64 image data URL.
**Multipart request**
```bash
curl -X POST "https://api.samsar.one/v1/image/assign_title" \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-F "image=@./product-photo.png" \
-F 'metadata={"product":"linen travel shirt","collection":"spring essentials"}'
```
Supported image file types are PNG, JPEG, WEBP, and non-animated GIF.
**Success response (200)**
```json
{
"content": "Linen Travel Shirt"
}
```
Response headers include:
- `x-credits-charged: `
- `x-credits-remaining: `
## POST /image/enhance
Upscale and denoise an image.
**Request body**
```json
{
"image_url": "https://cdn.example.com/photo.png",
"resolution": "0.5k | 1k | 2k | 4k"
}
```
- `image_url` required, non-empty URL.
- `resolution` optional; defaults to `1k`.
**Success response (200)**
```json
{
"status": "queued",
"request_id": "img_enh_123",
"session_id": "img_enh_123",
"global_status_id": "img_enh_123",
"case_type": "image_enhance",
"image_url": "https://cdn.example.com/photo.png",
"resolution": "1K",
"aspect_ratio": "16:9",
"creditsCharged": 15,
"remainingCredits": 4985
}
```
## POST /image/remove_branding
Remove visible text from an image.
**Request body**
```json
{
"image_url": "https://cdn.example.com/watermarked.png"
}
```
**Success response (200)**
```json
{
"status": "queued",
"request_id": "img_rm_123",
"session_id": "img_rm_123",
"global_status_id": "img_rm_123",
"case_type": "logo_remove",
"image_url": "https://cdn.example.com/watermarked.png",
"creditsCharged": 15,
"remainingCredits": 4985
}
```
## POST /image/add_image_set
Extend or save a set of images for downstream generation.
**Request body**
```json
{
"image_urls": [
"https://cdn.example.com/look1.png",
"https://cdn.example.com/look2.png"
],
"metadata": { "campaign": "spring" },
"prompt": "Optional creative direction",
"num_images": 4
}
```
- `image_urls` required, non-empty array of strings.
- `num_images` optional positive number; defaults to `image_urls.length` when omitted.
- `prompt` optional string; `metadata` optional object.
**Success response (200)**
```json
{
"status": "queued",
"request_id": "img_set_123",
"session_id": "img_set_123",
"global_status_id": "img_set_123",
"case_type": "image_list_to_image_set",
"image_urls": [
"https://cdn.example.com/look1.png",
"https://cdn.example.com/look2.png"
],
"metadata": { "campaign": "spring" },
"prompt": "Optional creative direction",
"num_images": 4,
"creditsCharged": 60,
"remainingCredits": 4940
}
```
## POST /image/receipt_templates/create
Create and persist a reusable receipt extraction template from a sample receipt image.
This endpoint is free (no credits deducted).
**Request body**
```json
{
"image_url": "https://cdn.example.com/receipt-template.png",
"template_name": "kbank-transfer-template"
}
```
- `image_url` required, public image URL.
- `template_name` optional.
**Success response (200)**
```json
{
"template_id": "receipt_tpl_0e4f9f1dca744ec9b3a3f8df",
"template_hash": "f8b8f4e6e7d9b66d3f4a9e6f35b17c4d7ac4dce2f0d63f15e2d55b9872e3be35",
"template_name": "kbank-transfer-template",
"normalized_template": {
"schema_version": "1.0",
"merchant_hint": "Kasikornbank",
"currency_hint": "THB",
"rois": [
{ "id": "meta_header", "label": "Header and Meta", "left": 0, "top": 0, "width": 1, "height": 0.35 },
{ "id": "line_items", "label": "Line Items/Table", "left": 0, "top": 0.28, "width": 1, "height": 0.5 },
{ "id": "totals", "label": "Totals", "left": 0.45, "top": 0.7, "width": 0.55, "height": 0.3 }
],
"fields": [
{ "key": "merchant_name", "type": "string", "required": true },
{ "key": "transaction_date", "type": "date", "required": true },
{ "key": "total", "type": "currency", "required": true }
],
"validation_rules": {
"arithmetic": true,
"amount_tolerance": 0.05
}
},
"sample_receipt": {
"merchant_name": "Kasikornbank",
"transaction_date": "2026-01-14",
"total": 786
}
}
```
## POST /image/receipt_templates/query
Extract receipt fields by validating an input receipt image against an existing template.
This endpoint charges **50 credits** per request.
**Request body**
```json
{
"template_id": "receipt_tpl_0e4f9f1dca744ec9b3a3f8df",
"image_url": "https://cdn.example.com/receipt-instance.png"
}
```
- `template_id` required.
- `image_url` required.
- The template must belong to the same API key. Cross-account template access returns `404`.
**Success response (200)**
```json
{
"template_id": "receipt_tpl_0e4f9f1dca744ec9b3a3f8df",
"template_hash": "f8b8f4e6e7d9b66d3f4a9e6f35b17c4d7ac4dce2f0d63f15e2d55b9872e3be35",
"template_name": "kbank-transfer-template",
"receipt_json": {
"merchant_name": "Kasikornbank",
"transaction_date": "2026-01-14",
"transaction_id": "TRBS260114764844309",
"currency": "THB",
"fee": 0,
"total": 786
},
"standardized_receipt": {
"merchant_name": "Kasikornbank",
"transaction_date": "2026-01-14",
"transaction_time": "17:25",
"transaction_id": "TRBS260114764844309",
"currency": "THB",
"subtotal": 786,
"tax": null,
"fee": 0,
"discount": null,
"total": 786,
"payment_method": "transfer",
"from_account": "xxx-x-xxxx-x",
"to_account": "xxx-x-xxxx-x"
},
"items": [],
"unreadable_fields": [],
"confidence": 0.93,
"validation": {
"is_valid": true,
"issues": []
},
"attempts": 1
}
```
Response headers include:
- `x-credits-charged: 50`
- `x-credits-remaining: `
## GET /image/template_json
Fetch a saved structured receipt template by `template_id`.
This endpoint validates ownership against your API key and returns the template JSON only when the template belongs to that user.
Primary route: `GET /image/template_json`
Alias route: `GET /image/receipt_templates/template_json`
**Query params**
- `template_id` required.
**Sample request**
```bash
curl "https://api.samsar.one/v1/image/template_json?template_id=receipt_tpl_0e4f9f1dca744ec9b3a3f8df" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
**Success response (200)**
```json
{
"template_id": "receipt_tpl_0e4f9f1dca744ec9b3a3f8df",
"template_hash": "f8b8f4e6e7d9b66d3f4a9e6f35b17c4d7ac4dce2f0d63f15e2d55b9872e3be35",
"template_name": "kbank-transfer-template",
"source_image_url": "https://cdn.example.com/receipt-template.png",
"normalized_template": {
"schema_version": "1.0",
"merchant_hint": "Kasikornbank",
"currency_hint": "THB",
"rois": [
{ "id": "meta_header", "label": "Header and Meta", "left": 0, "top": 0, "width": 1, "height": 0.35 },
{ "id": "line_items", "label": "Line Items/Table", "left": 0, "top": 0.28, "width": 1, "height": 0.5 },
{ "id": "totals", "label": "Totals", "left": 0.45, "top": 0.7, "width": 0.55, "height": 0.3 }
],
"fields": [
{ "key": "merchant_name", "type": "string", "required": true },
{ "key": "transaction_date", "type": "date", "required": true },
{ "key": "total", "type": "currency", "required": true }
],
"validation_rules": {
"arithmetic": true,
"amount_tolerance": 0.05
}
},
"template_json": {
"schema_version": "1.0"
},
"sample_receipt": {
"merchant_name": "Kasikornbank",
"transaction_date": "2026-01-14",
"total": 786
},
"created_at": "2026-03-01T00:26:29.478Z",
"updated_at": "2026-03-01T00:26:29.478Z"
}
```
- `template_json` mirrors `normalized_template` and is returned as a compatibility alias.
**Error responses**
- `400` missing `template_id`.
- `401` invalid API key.
- `404` template does not exist or does not belong to this API key.
### JavaScript SDK (samsar-js)
```ts
import SamsarClient from 'samsar-js';
const samsar = new SamsarClient({ apiKey: process.env.SAMSAR_API_KEY! });
const templateJson = await samsar.getReceiptTemplateJson(
'receipt_tpl_0e4f9f1dca744ec9b3a3f8df',
);
console.log(templateJson.data.template_json);
```
## GET /image/status
Check the latest state for a single image request (accepts `request_id` or `session_id`).
```bash
curl "https://api.samsar.one/v1/image/status?request_id=img_enh_123" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
**Queued / processing**
```json
{
"session_id": "img_enh_123",
"request_id": "img_enh_123",
"status": "PENDING",
"type": "image",
"case_type": "image_enhance"
}
```
**Completed**
```json
{
"session_id": "img_enh_123",
"request_id": "img_enh_123",
"status": "COMPLETED",
"type": "image",
"case_type": "image_enhance",
"result_url": "https://cdn.samsar.one/images/img_enh_123.png"
}
```
- `result_urls` is present when multiple assets are produced.
- `thumbnail_url` is included for rollup banner completions and points to a downscaled preview.
- A `404` is returned if the request does not belong to your API key or is unknown.
## GET /image/list
Fetch a list of your recent image API sessions, including their current status and any available results. Use `limit` (max 500) to trim the response.
```bash
curl "https://api.samsar.one/v1/image/list?limit=50" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
**Response (200)**
```json
{
"sessions": [
{
"session_id": "img_rm_123",
"request_id": "img_rm_123",
"status": "COMPLETED",
"type": "image",
"case_type": "logo_remove",
"request_type": "API",
"result_url": "https://cdn.samsar.one/images/img_rm_123.png",
"created_at": "2024-08-20T08:10:11.000Z",
"updated_at": "2024-08-20T08:12:42.000Z"
},
{
"session_id": "img_set_987",
"request_id": "img_set_987",
"status": "PENDING",
"type": "image",
"case_type": "image_list_to_image_set",
"request_type": "API",
"created_at": "2024-08-20T08:15:55.000Z",
"updated_at": "2024-08-20T08:15:55.000Z"
}
]
}
```
**Common errors**
- `400` invalid or missing fields (empty URLs, bad resolution, non-numeric `num_images`).
- `401` invalid API key.
- `402` insufficient credits.
- `404` unknown `request_id` (or a request that does not belong to the API key on `/image/status`).
## Utils
### POST /image/create_rollup_banner_with_enhance
Enhance low-resolution tiles (if needed) and generate a rollup banner. Images below the rollup minimum (2020x2526) are auto-upscaled using `1k`/`2k`/`4k`, then the list is expanded to 28 tiles before rendering.
**Request body**
```json
{
"images": [
{
"image_url": "https://cdn.example.com/tile-1.png",
"image_text": "Rooftop Bar",
"image_category": "Social",
"overlay": { "top_left": "2 hours", "top_right": "City", "footer": "Rooftop Bar" }
}
],
"header_image_url": "https://cdn.example.com/header.png",
"footer_image_url": "https://cdn.example.com/footer.png",
"columns": 4,
"max_tiles": 28,
"image_tiling_position": {
"font_key": "en",
"font_family": "Poppins, Helvetica, Arial, sans-serif",
"top_left": {
"margin_min": 28,
"margin_ratio": 0.02,
"diameter_min": 220,
"diameter_max": 320,
"diameter_ratio": 0.215,
"inner_padding_min": 12,
"inner_padding_ratio": 0.08
},
"top_right": {
"margin_min": 28,
"margin_ratio": 0.02,
"max_width_floor": 260,
"min_width": 220,
"padding_left": 36,
"padding_right": 48,
"padding_top": 30,
"padding_bottom": 24,
"overlay_height_min": 140
},
"bottom": {
"inset_min": 32,
"inset_ratio": 0.03,
"offset_min": 12,
"offset_ratio": 0.02,
"container_margin_min": 40,
"container_margin_ratio": 0.08,
"text_inset_min": 18,
"text_inset_ratio": 0.04,
"overlay_height_min": 280
}
}
}
```
- `images` (or `image_list`/`image_urls`) required, non-empty array.
- Items accept `image_url` (or `enhanced_url`) plus optional `image_text`, `image_category`, `image_duration`, `overlay`.
- `columns` optional integer 1-6; defaults to 4.
- `max_tiles` optional integer; defaults to 28.
- `header_image_url` and `footer_image_url` optional.
- `image_tiling_position` optional object for tile overlay layout and font overrides. Omit to use defaults.
**image_tiling_position (optional)**
Overrides for per-tile overlay placement and font selection. You can send only the fields you want to change.
- `font_key` required if provided; must match a supported subtitle language key (e.g., `en`, `fr`, `es`, `ja`, `ko`, `zh-cn`, `zh-tw`, `hi`, `ar`).
- `font_family` optional; comma-separated stack. The first font must be supported for the chosen `font_key`.
- `top_left` optional object for the circular badge.
- `margin_min`, `margin_ratio`
- `diameter_min`, `diameter_max`, `diameter_ratio`
- `inner_padding_min`, `inner_padding_ratio`
- `top_right` optional object for the top-right label.
- `margin_min`, `margin_ratio`
- `max_width_floor`, `min_width`
- `padding_left`, `padding_right`, `padding_top`, `padding_bottom`
- `overlay_height_min`
- `bottom` optional object for the footer label.
- `inset_min`, `inset_ratio`
- `offset_min`, `offset_ratio`
- `container_margin_min`, `container_margin_ratio`
- `text_inset_min`, `text_inset_ratio`
- `overlay_height_min`
All `*_ratio` values are fractions between 0 and 1. Values are validated; invalid ranges return `400`.
**Success response (200)**
```json
{
"status": "completed",
"session_id": "rollup_enh_123",
"request_id": "rollup_enh_123",
"rollup_session_id": "rollup_456",
"rollup_request_id": "rollup_456",
"case_type": "rollup_banner_enhance",
"result_url": "https://cdn.samsar.one/rollups/rollup_456.png",
"thumbnail_url": "https://cdn.samsar.one/rollups/rollup_456-thumb.png",
"result_urls": ["https://cdn.samsar.one/rollups/rollup_456.png"],
"input_image_urls": [
"https://cdn.example.com/tile-1-enhanced.png"
],
"creditsCharged": 59,
"remainingCredits": 4941
}
```
Poll `/image/status?request_id=rollup_enh_123` for completion. The status payload includes `case_type: rollup_banner_enhance`.
`input_image_urls` returns the enhanced URLs used for the rollup request (falls back to original URLs when no enhancement was needed), before any duplication to 28 tiles.
### POST /image/create_rollup_banner
Generate a rollup banner image from a list of tiles, with optional header/footer imagery.
**Request body**
```json
{
"image_list": [
{
"image_url": "https://cdn.example.com/tile-1.png",
"title": "Rooftop Bar",
"image_category": "Social",
"image_duration": "2 hours"
},
{
"enhanced_url": "https://cdn.example.com/tile-2.png",
"overlay": { "top_left": "Half day", "top_right": "Pool", "footer": "Cabana" }
}
],
"header_image_url": "https://cdn.example.com/header.png",
"footer_image_url": "https://cdn.example.com/footer.png",
"columns": 4,
"max_tiles": 28,
"image_tiling_position": {
"font_key": "en",
"font_family": "Poppins, Helvetica, Arial, sans-serif",
"top_left": {
"margin_min": 28,
"margin_ratio": 0.02,
"diameter_min": 220,
"diameter_max": 320,
"diameter_ratio": 0.215,
"inner_padding_min": 12,
"inner_padding_ratio": 0.08
},
"top_right": {
"margin_min": 28,
"margin_ratio": 0.02,
"max_width_floor": 260,
"min_width": 220,
"padding_left": 36,
"padding_right": 48,
"padding_top": 30,
"padding_bottom": 24,
"overlay_height_min": 140
},
"bottom": {
"inset_min": 32,
"inset_ratio": 0.03,
"offset_min": 12,
"offset_ratio": 0.02,
"container_margin_min": 40,
"container_margin_ratio": 0.08,
"text_inset_min": 18,
"text_inset_ratio": 0.04,
"overlay_height_min": 280
}
}
}
```
- `image_list` (or `images`) required, non-empty array. Each item must include `image_url` or `enhanced_url`.
- `overlay` optional; supports `top_left`, `top_right`, `footer` (fallbacks: `title`, `image_text`, `image_category`, `image_duration`).
- `columns` optional integer 1-6; defaults to 4.
- `max_tiles` optional integer; defaults to 28.
- `header_image_url` and `footer_image_url` optional.
- `image_tiling_position` optional object for tile overlay layout and font overrides. Omit to use defaults.
**image_tiling_position (optional)**
Same schema and validation as `/image/create_rollup_banner_with_enhance`. See details above.
**Success response (200)**
```json
{
"status": "completed",
"session_id": "rollup_123",
"request_id": "rollup_123",
"result_url": "https://cdn.samsar.one/rollups/rollup_123.png",
"thumbnail_url": "https://cdn.samsar.one/rollups/rollup_123-thumb.png",
"result_urls": ["https://cdn.samsar.one/rollups/rollup_123.png"],
"creditsCharged": 30,
"remainingCredits": 4970
}
```
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/intro.md
METADATA:
title: SamsarOne API Docs
description: Explore SamsarOne's unified generative APIs for video, image, chat, assistant workflows, credits, and external-user integrations.
image: img/og/samsar-docs-overview.png
sidebar_position: 0
sidebar_label: Overview
slug: /intro
PRIMARY_HEADING: Generative API overview
CONTENT_START
---
title: SamsarOne API Docs
description: "Explore SamsarOne's unified generative APIs for video, image, chat, assistant workflows, credits, and external-user integrations."
image: img/og/samsar-docs-overview.png
sidebar_position: 0
sidebar_label: Overview
slug: /intro
---
# Generative API overview
SamsarOne offers unified generative endpoints for video, image, chat, and assistant workflows. Use the `/video` API to generate clips from prompts, `/image` to enhance or edit assets (whitelabel assets, upscale, manage image sets), `/chat` to improve copy or work with embeddings, and `/assistant` for OpenAI-compatible assistant completions tied to existing Samsar sessions. Requests return credit headers so you can track usage in real time.
**Credits**
Authenticate with `Authorization: Bearer `. Credits are debited per endpoint as documented on each page, and responses include `x-credits-charged` / `x-credits-remaining` when applicable. Manage and purchase credits in the Samsar app billing settings.
**Base URL**
`https://api.samsar.one/v1`
**Jump to API docs**
- [Login Tokens](/login-tokens)
- [Video API](/video)
- [Image API](/image-api)
- [Chat API](/chat-api)
- [Assistant API](/assistant-api)
- [Credits API](/credits)
- [Publications API](/publications)
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/login-tokens.mdx
METADATA:
title: Login Tokens
description: Mint short-lived login tokens and exchange them for client auth tokens in Samsar applications.
image: img/og/login-tokens.png
sidebar_position: 1
slug: /login-tokens
sidebar_label: Login Tokens
PRIMARY_HEADING: Login Tokens
CONTENT_START
---
title: Login Tokens
description: "Mint short-lived login tokens and exchange them for client auth tokens in Samsar applications."
image: img/og/login-tokens.png
sidebar_position: 1
slug: /login-tokens
sidebar_label: Login Tokens
---
# Login Tokens
Use this endpoint to mint a short-lived `loginToken` from your API key. The login token can be exchanged for a long-lived client `authToken` via `/users/verify_token`.
For external platform users such as Whop members, use [`POST /external_users/create_login_token`](/external-users#post-external_userscreate_login_token) instead.
- Base URL: `https://api.samsar.one/v1`
- Auth: `Authorization: Bearer `
## POST /create_login_token
Create a short-lived `loginToken` for a user associated with your API key.
**Request body**
No body is required.
**Sample request**
```bash
curl -X POST https://api.samsar.one/v1/create_login_token \
-H "Authorization: Bearer YOUR_API_KEY_****************"
```
**Success response (200)**
```json
{
"loginToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresInSeconds": 600,
"expiresAt": "2026-02-02T12:00:00.000Z"
}
```
**Notes**
- The token is short-lived and intended for handoff to the client app.
- `GET /create_login_token` is also supported and returns the same payload.
## Exchange login token for client auth token
Once the client receives a `loginToken`, exchange it for a long-lived `authToken` using the user verification endpoint.
**Sample request**
```bash
curl "https://api.samsar.one/users/verify_token?loginToken=$LOGIN_TOKEN"
```
**Success response (200)**
```json
{
"authToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"_id": "...",
"email": "user@example.com"
}
```
For external-user login tokens, the response includes `isExternalUser: true` plus the external user profile fields and the external-user-scoped `authToken`.
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/publications-api.mdx
METADATA:
title: Publications API
description: Publish, edit, and revoke completed Samsar video sessions on the public feed through free API routes.
image: img/og/publications-api.png
sidebar_position: 6
slug: /publications
sidebar_label: Publications API
PRIMARY_HEADING: Publications API
CONTENT_START
---
title: Publications API
description: "Publish, edit, and revoke completed Samsar video sessions on the public feed through free API routes."
image: img/og/publications-api.png
sidebar_position: 6
slug: /publications
sidebar_label: Publications API
---
import DocTopActions from '@site/src/components/DocTopActions';
# Publications API
- Base URL: `https://api.samsar.one/v1`
- Auth: `Authorization: Bearer `
- Customer sub-account auth: `x-customer-sub-account-api-key: `
- Content-Type: `application/json`
- Billing: free. These endpoints do not deduct credits.
Use these endpoints when an external application needs to publish, edit, or revoke a public publication for a completed `samsar-js` video session. The authenticated account must own the target video session. A session can only be published after it has a final video URL.
## Publication fields
Samsar infers the final video URL and many defaults from the session. Send the fields your app owns or cannot infer from the session:
```json
{
"session_id": "66ff...",
"title": "Running shoe teaser",
"description": "Launch-day vertical cut",
"tags": ["launch", "footwear"],
"creator_handle": "acme",
"slug": "running-shoe-teaser",
"image_hash": "optional-image-hash",
"splash_image": "https://cdn.example.com/splash.png",
"image_model": "GPTIMAGE2",
"video_model": "RUNWAYML",
"original_prompt": "A fast launch video for a new running shoe",
"aspect_ratio": "9:16",
"has_subtitles": true,
"session_language": "en",
"language_string": "English"
}
```
Alias support:
- `session_id`, `sessionId`, `video_session_id`, `videoSessionId`, or `id`
- `aspect_ratio` or `aspectRatio`
- `creator_handle` or `creatorHandle`
- `image_hash` or `imageHash`
- `splash_image` or `splashImage`
- `image_model` or `imageModel`
- `video_model` or `videoModel`
- `original_prompt`, `originalPrompt`, or `prompt`
- `session_language`, `sessionLanguage`, `language`, or `language_code`
- `language_string` or `languageString`
- `has_subtitles`, `hasSubtitles`, `enable_subtitles`, or `enableSubtitles`
Request bodies can be sent flat or nested under `input`.
## POST /publications/publish
Create or update a public publication for a completed session.
```bash
curl -X POST https://api.samsar.one/v1/publications/publish \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "66ff...",
"title": "Running shoe teaser",
"description": "Launch-day vertical cut",
"tags": ["launch", "footwear"],
"creator_handle": "acme"
}'
```
Success response:
```json
{
"created": true,
"publication": {
"publication_id": "67aa...",
"session_id": "66ff...",
"video_url": "https://static.samsar.one/video/66ff/final.mp4",
"title": "Running shoe teaser",
"description": "Launch-day vertical cut",
"tags": ["launch", "footwear"],
"creator_handle": "acme",
"aspect_ratio": "9:16",
"has_subtitles": true,
"language": "en",
"session_language": "en",
"language_string": "English"
},
"session": {
"session_id": "66ff...",
"is_published": true,
"published_publication_id": "67aa...",
"published_video_url": "https://static.samsar.one/video/66ff/final.mp4"
}
}
```
## POST /publications/edit
Edit an existing publication for a session. Omitted fields keep their current publication values.
```bash
curl -X POST https://api.samsar.one/v1/publications/edit \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "66ff...",
"title": "Running shoe teaser - updated",
"tags": ["launch", "footwear", "campaign"]
}'
```
`404` is returned when the session exists but has no publication yet.
## POST /publications/revoke
Remove a session publication from the public feed and clear the published fields on the session.
```bash
curl -X POST https://api.samsar.one/v1/publications/revoke \
-H "Authorization: Bearer $SAMSAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "66ff..."
}'
```
Success response:
```json
{
"revoked": true,
"publication_id": "67aa...",
"session": {
"session_id": "66ff...",
"is_published": false
}
}
```
## REST aliases
The same operations are also available as session-centric routes:
- `POST /publications/session/:sessionId`
- `PATCH /publications/session/:sessionId`
- `DELETE /publications/session/:sessionId`
## Error responses
- `400` missing or invalid `session_id`
- `401` invalid API key, customer sub-account key, or auth token
- `403` the session does not belong to the authenticated actor
- `404` session or publication not found
- `409` the session has no final video URL yet
## samsar-js
```ts
import SamsarClient from 'samsar-js';
const samsar = new SamsarClient({ apiKey: process.env.SAMSAR_API_KEY! });
await samsar.publishPublication({
session_id: '66ff...',
title: 'Running shoe teaser',
tags: ['launch', 'footwear'],
});
await samsar.editPublication({
session_id: '66ff...',
title: 'Running shoe teaser - updated',
});
await samsar.revokePublication('66ff...');
```
CONTENT_END
=== DOCUMENT END ===
=== DOCUMENT START ===
SOURCE_PATH: docs/v2-api.mdx
METADATA:
title: V2 API
description: Use additive v2 routes that unify internal, app-key, and external-user clients across media and account workflows.
image: img/og/v2-api.png
slug: /v2
sidebar_label: V2 API
PRIMARY_HEADING: V2 API
CONTENT_START
---
title: V2 API
description: "Use additive v2 routes that unify internal, app-key, and external-user clients across media and account workflows."
image: img/og/v2-api.png
slug: /v2
sidebar_label: V2 API
---
import DocTopActions from '@site/src/components/DocTopActions';
# V2 API
`/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
### Core session and request routes
| Route | Purpose |
| --- | --- |
| `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/audio/voices` | List no-charge external audio/TTS voice catalogs keyed by provider. |
| `POST /v2/external/audio/text_to_music` | Create an API-key billed hosted text-to-music request. |
| `GET /v2/external/audio/status` | Poll hosted external audio request status. |
| `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/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. |
| `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/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/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/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/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`. |
### Publishing routes
| Route | Purpose |
| --- | --- |
| `POST /v2/archive` | Archive a generated asset. |
| `POST /v2/publish` | Publish a generated asset. |
## Authentication behavior
- Internal API key or internal auth token with no `external_user` payload uses the internal account and deducts internal generation credits.
- APP_KEY authentication uses `Authorization: AppKey ` plus `x-app-secret: `. It is a third internal-account auth method for `/v2` routes after the APP_KEY has been created with a normal API key or user auth token.
- Internal API key/auth token plus an `external_user` payload 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_id` through external request records and `GlobalSession` records, so either returned `extreq_...` ids, normalized external ids, or internal video session ids can be used.
## 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**
```bash
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)**
```json
{
"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**
```bash
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:
- `amount` is a positive USD amount. Credits are calculated with the standard **1 USD = 100 credits** rule.
- `email` must be a valid email address.
- `redirect_url` must be HTTPS. The payment success webhook sends the token callback to this URL.
**Success response (200)**
```json
{
"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 `email` if 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_url` with `authToken`, `refreshToken`, and `expiryDate` query parameters.
The webhook first attempts `GET ?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**
```bash
curl -X POST "https://api.samsar.one/v2/user/refresh_token" \
-H "Content-Type: application/json" \
-d '{"refreshToken": "srf_..."}'
```
**Success response (200)**
```json
{
"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 ` 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**
```bash
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)**
```json
{
"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 ",
"secretHeader": "x-app-secret"
}
}
```
Use the APP_KEY on authenticated `/v2` routes:
```bash
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.
```bash
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 same media input shape as the corresponding `/v1/video/*` routes, either nested under `input` or sent as a flat JSON payload. Use `backingtrack_model` / `music_provider` to choose `LYRIA3` or `ELEVENLABS_MUSIC`, use `tts_model` / `tts_provider` to choose `ELEVENLABS`, `OPENAI`, or `GOOGLE` for speaker assignment, optionally use `speakerOptions` / `speaker_options` to constrain the request to specific `openAISpeakers`, `elevenLabsSpeakers`, or Google `googleSpeakers` with `googleSpeakerDetails`, and optionally use `inference_model` / `inferenceModel` to choose `gpt-5.5` or `gemini-3.1-pro` for render planning. Omit `inference_model` to use the account default inference model. `video_model_sub_type` is not part of the v2 request shape; describe style direction in `prompt` and model choices instead.
**Internal billing example**
```bash
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**
```bash
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`, provide `cta_url` to add a server-generated QR CTA outro, or provide `outro_cta_image` to add a server-generated CTA outro with a supplied center logo/CTA image. `outro_cta_image` uses `{ "top_text", "middle_image", "bottom_text" }`; `middle_image` accepts a public image URL or image data and is resized into the same center area used by QR outros without changing aspect ratio. Set `express_cta_generation: true` with one `cta_url` when you want Samsar to generate concise per-scene footer CTA titles from the image payload and clean outro CTA text from the session context. Use `backingtrack_model` or `music_provider` to choose `LYRIA3` for Google Lyria 3 Pro native backing tracks or `ELEVENLABS_MUSIC` for ElevenLabs Music; legacy `LYRIA2` values are treated as `LYRIA3`. Use `tts_model` or `tts_provider` to choose `ELEVENLABS`, `OPENAI`, or `GOOGLE` for speaker assignment, and use `speakerOptions` or `speaker_options` when the request should override account speaker settings for that TTS provider. Use `inference_model` or `inferenceModel` to choose `gpt-5.5` or `gemini-3.1-pro`; omit it to use the account default inference model. `add_outro_animation` is optional and defaults to `true`; supplied full outro image URLs and manual outro focus dimensions are not part of the v2 image-list request shape. `limit_single_narrator` keeps all narration under one narrator identity. `add_narrator_avatar` automatically enables `limit_single_narrator`, generates an influencer-style human narrator avatar suitable for the image-list narrative, overlays it bottom-center or centered in the footer row, and adds 4 credits/sec. The same standard model prices apply as `/v1`: `VEO3.1I2V` is 60 credits/sec, `VEO3.1I2VFAST` is 36 credits/sec, `COSMOS3SUPERI2V` is 20 credits/sec, `SEEDANCEI2V` is 30 credits/sec, `KLINGIMGTOVID3PRO` is 36 credits/sec, `KLINGIMGTOVIDTURBO` is 36 credits/sec, `HAPPYHORSEI2V` is 36 credits/sec, and `RUNWAYML` is 30 credits/sec; express CTA generation adds 1 credit/sec, and outro add/update plus footer update routes are 75 credits/request.
### 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: true` and `manual_step_stages: []`. The request generates the full video without waiting for user approval before `ai_video_generation`.
- **2-step render**: set `auto_render_full_video: false` and `manual_step_stages: ["ai_video_generation"]`. The request pauses after image/audio assets are ready, then waits for `process_next` before 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**
```bash
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**
```bash
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`.
```bash
curl "https://api.samsar.one/v2/video/step/65f1234567890abcdef12345/status" \
-H "Authorization: Bearer $SAMSAR_API_KEY"
```
```json
{
"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`.
```bash
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:
```json
{
"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:
```bash
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 for `process_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**
```bash
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**
```bash
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**
```bash
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=` until it returns the standard completed video payload with `result_url`, `has_subtitles`, and `result_language`.
**Reroll layers example**
```bash
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_video`
- `POST /v2/external/video/image_to_video`
- `POST /v2/external/video/lip_sync`
- `POST /v2/external/video/sound_effect`
- `GET` or `POST /v2/external/video/status`
- `GET` or `POST /v2/external/video/{request_id}/status`
- `GET` or `POST /v2/external/video/status_detailed`
- `GET` or `POST /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` accepts `image_urls` or a single `image_url`. `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.
## 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_prompt`
- `POST /v2/assistant/completion`
- `POST /v2/external/chat`
- `POST /v2/external/chat/completions`
- `POST /v2/external/assistant`
- `POST /v2/external/assistant/completions`
- `GET /v2/external/chat/status`
- `GET /v2/external/chat/completions/status`
- `GET /v2/external/assistant/status`
- `POST /v2/external/moderation`
- `POST /v2/external/moderations`
- `GET /v2/external/audio/voices`
- `POST /v2/external/audio/text_to_music`
- `GET` or `POST /v2/external/audio/status`
- `GET` or `POST /v2/external/audio/{request_id}/status`
- `POST /v2/utils/assistant_session`
- `POST /v2/utils/usage_charge`
- `POST /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](/external-requests#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. External audio voice listing returns provider-keyed voice catalogs and does not charge credits. External audio `text_to_music` accepts `ELEVENLABS_MUSIC` and `LYRIA3` (`LYRIA2` alias) and uses Studio music pricing.
## 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.
Publishing and archive routes operate on the resolved actor's accessible assets:
- `POST /v2/archive`
- `POST /v2/publish`
CONTENT_END
=== DOCUMENT END ===