second studioDocs llms.txtGet started

REST API reference

Base URL: https://second.studio.hellofaro.io · All bodies are JSON · Authenticate every /v1 request with

Authorization: Bearer ss_live_...

Conventions

{ "error": { "code": "insufficient_credits", "message": "this costs 5 credit(s); balance is 2. …" } }
Status Codes
400 bad_request (fallback), missing_field, invalid_field, invalid_json, invalid_id, invalid_url, invalid_pack, unknown_account, platform_mismatch
401 unauthorized: missing, invalid or revoked key
402 insufficient_credits
403 key_pending_approval, forbidden (needs an admin key), fetch_disabled
404 not_found
409 conflict, not_pending
429 rate_limited, too_many_jobs
502 / 503 zernio_error, not_configured

Account

GET /v1/me

{ "orgId": "js7…", "keyScope": "agent", "name": "Acme", "credits": 93, "autoPublish": false,
  "allowHostedFetch": false, "maxConcurrentJobs": 4, "apiVersion": "2026-10-08" }

`apiVersion` changes whenever the endpoint or MCP tool surface changes; MCP clients that cache the tool
list should reconnect when it does.

DELETE /v1/account

Deletes the signed-in person's own account. This is the endpoint the mobile app's Delete account button calls. Authenticate with either:

curl -X DELETE https://second.studio.hellofaro.io/v1/account \
  -H "Authorization: Bearer $SECOND_STUDIO_API_KEY" -H "Content-Type: application/json" \
  -d '{"confirm":"DELETE"}'
{ "deleted": true, "clerkDeleted": true, "memberships": 1, "orgsClosed": 1, "keysRevoked": 2 }

It removes the person from every account they belong to, revokes every API key they own (including the one used), writes account.deleted to each account's audit log, anonymizes their profile and deletes their sign-in. From a Convex client the same thing is api.account.deleteMe({ confirm: "DELETE" }).

Status Code Meaning
400 confirm_required Body must be {"confirm":"DELETE"}.
401 unauthorized No valid signed-in session, or the API key is invalid or revoked.
403 no_user_for_key The API key is not tied to a person (account-level key), so it cannot delete an account.
404 not_found Already deleted.
409 sole_owner You are the only admin of an account with other members. Make someone else an admin first.

If you are the only member of an account, that account is closed and all of its keys are revoked. Unused credits are not refunded.

Assets

An asset is any file in your account: an uploaded clip or song, a reference, a finished edit, a recording. Uploading takes three calls, so large files go straight to storage instead of through the API.

POST /v1/assets/upload-url

Returns a one-time URL: { "uploadUrl": "https://…" }. Send the raw bytes to it:

curl -X POST "$uploadUrl" -H "Content-Type: video/mp4" --data-binary @clip.mp4
# → { "storageId": "kg2…" }

POST /v1/assets

Register the upload: { "storageId": "kg2…", "filename": "clip.mp4" } → 201 with the asset:

{ "id": "j97…", "kind": "video", "role": "upload", "filename": "clip.mp4", "contentType": "video/mp4",
  "size": 991017, "url": "https://…", "source": null, "jobId": null, "createdAt": 1791328110667 }

kind is video, audio, image, json or other, from the uploaded Content-Type.

GET /v1/assets · GET /v1/assets/{id} · DELETE /v1/assets/{id}

List the 100 most recent, read one (with a download url), or delete one and its file permanently.

Styles

POST /v1/styles: analyze a reference (2 credits for the first minute, then 1 per extra minute, 10 minutes max)

{ "assetId": "j97…", "semantic": true }

semantic (default true) adds vision-model shot roles, descriptions and caption style on top of the measured analysis. When the job succeeds, result.styleId is the new style and result summarizes it:

{ "styleId": "jn7…", "aspect": "9:16", "duration": 17.8, "shot_count": 7, "bpm": 99.4,
  "cuts_on_beat_ratio": 0.71, "semantic": true, "summary": "Fast result-first hook, …",
  "pacing": { "shot_count": 7, "mean_shot": 2.54, "median_shot": 0.73, "cuts_per_minute": 20.3, … } }

GET /v1/styles · GET /v1/styles/{id}

GET /v1/styles/{id} includes the full spec: see Styles and edits.

Edits

POST /v1/edits: cut your clips to a style (5 credits plus 1 per started minute of footage)

Field Type
styleId string Required.
clipAssetIds string[] Required. 1 to 10 of your video assets, 30 minutes of footage in total at most.
musicAssetId string An audio asset. Cuts snap to its beat when the style is beat-driven.
musicStart number Seconds into the track to start (default 0).
captions string[] Caption lines in order (up to 40, 200 chars each). The first is the hook.
brief string What the video is about. Used to write captions when captions is omitted.
aspect string 9:16, 4:5, 1:1, 4:3 or 16:9. Defaults to the reference's.
duration number Target length, 3–180 s. Defaults to the reference's.
keepClipAudio boolean Mix your clips' own sound. Default: on without music, off (ducked) with music.
semantic boolean Use the vision model to pick the best moments (default true).

The finished job's outputs contain the edit (mp4) and the plan (JSON: which clip and in-point filled each shot). result.qa is the automatic quality report; result.notes explains decisions such as beat snapping and layout.

Generation

See Generating images, characters and video for every option.

Endpoint
POST /v1/characters { name, description, referenceAssetIds?, aspect?, resolution? } → a stored character whose sheet is generated; poll until ready
GET /v1/characters · GET /v1/characters/{id} · DELETE /v1/characters/{id} List, read (with sheetUrl), delete
POST /v1/images { kind, brief, characterIds?, references?, aspect?, resolution?, n?, estimate? } → job; outputs are images
POST /v1/videos { brief, characterIds?, duration?, resolution?, aspect?, imageAssetId?, referenceAssetIds?, referenceVideoAssetIds?, referenceAudioAssetIds?, estimate? } → job, or a price with estimate: true
POST /v1/voice { text, voice?, direction?, stability?, perform? } → job; output is audio
POST /v1/lipsync { videoAssetId, audioAssetId, pro? } → job

Generation results include notes on choices made. The production craft behind them stays server-side.

Models, skills, swaps, edits

GET /v1/crew · GET /v1/crew/{id} The crew: preset robots and the skills, tools and endpoints each bundles (Meet the crew). Free.
GET /v1/models The image, video and swap models you can pick, with what each supports.
POST /v1/skills · GET /v1/skills · GET/POST/PATCH/DELETE /v1/skills/{id} Image recipes, or codebase skills (kind: "codebase") that name file paths and do not quote code. GET /v1/skills?repoId=&kind= filters. skillIds on images, characters, edits, scripts, explainers and brand kits.
GET /v1/skills/{id}/versions · POST /v1/skills/{id}/versions List versions and restore one. Restoring a codebase skill deleted within 30 days brings it back.
POST /v1/skills/{id}/regenerate Rewrite one codebase skill from the stored docs. 4 credits. A pinned, custom, or edited skill gets a proposal instead of an overwrite.
POST /v1/repos · GET /v1/repos · DELETE /v1/repos/{id} Connect a GitHub repository the app can already read. Does not start an index. Delete is for an admin, the member who connected it, or that team's manager.
POST /v1/repos/{id}/index Read the repo into docs and skills, or quote it with estimate: true (free, no model call). 12 credits plus 0.12 per selected file, rounded up, 3,000 file cap, 400 credit cap. A later commit is 6 plus 0.12 per changed file. A push does not start an index.
GET /v1/repos/{id}/indexes/{sha} · GET /v1/repos/{id}/docs Index status, and the docs (kind, slug). Reading is free.
POST /v1/images/edit Masked edit of a real photo: only the mask is painted, the rest is untouched.
POST /v1/swaps object_swap / motion_transfer: keep the footage, change the subject.
POST /v1/videos/{id}/finalize · draft: true Cheap 480p drafts, then the keeper at 720p/1080p.
POST /v1/jobs/{id}/cancel Cancel with a full refund.
POST /v1/upscales Lanczos resample to 2160p.
POST /v1/dialogue · GET /v1/voices · POST /v1/voices/clone Multi-speaker tracks with per-line timings; your own ElevenLabs voices and clones when your key is connected.
GET /v1/providers · POST/DELETE /v1/providers/{higgsfield\|elevenlabs} Connect your own provider keys (admin, where enabled).
POST /v1/brands/analyze A suggested brand kit from your boards.
POST /v1/brands/import { "url": "https://example.com/brand", "brandId": "optional" }. Imports a brand guidelines page or brand book PDF: logo (saved to the kit), colors and named palette, fonts, voice and tone, taglines, do's and don'ts, imagery and logo usage notes. Fills brandId when given, otherwise creates a new kit; every field stays editable. Follows same-site guide pages and up to 2 linked PDFs. Public http(s) only, 10 imports per hour per team. 1 credit.
POST /v1/brands/from-website { "url": "https://example.com" }. Reads the homepage and a few key pages and writes brand guidelines to result.guidelines, saved to a brand kit (result.brand). Public http(s) sites only. 1 credit.

See Generation.

Explainer videos

Brand kits, scripts and renders have their own page: Explainer videos.

POST /v1/brands · GET /v1/brands · GET /v1/brands/{id} · POST /v1/brands/{id} · DELETE /v1/brands/{id} Brand kits: name, description, tone, colors, font, logo, product images with descriptions, music. Free.
POST /v1/scripts { brandId, brief, duration?, aspect?, language? } → job; result.script is an editable scene list. 2 credits.
POST /v1/explainers { brandId, brief | script, aspect?, captions?, voiceover?, musicAssetId? } → job; the video plus the rendered script. 12 credits for the first minute, 7 per extra started minute.

Recordings

POST /v1/recordings: record a public web app (3 credits)

{ "url": "https://example.com", "viewport": "mobile",
  "steps": [ { "type": "hold", "seconds": 2 }, { "type": "click", "selector": "text=Pricing" },
             { "type": "scroll", "pixels": 900 }, { "type": "hold", "seconds": 2 } ] }

See Recording for every step type. Output: a recording asset (mp4) and result.marks, the timestamp of every step for cutting.

POST /v1/repo-recordings: record a private GitHub repository (20 credits, up to 3 flows)

{ "repo": "acme/web", "ref": "main", "maxFlows": 3 }. Builds the app in a sandbox and records its key flows; needs GitHub connected (POST /v1/github/install). Optional envSetId (/v1/env-sets), loginId (/v1/logins), storageState or storageStateAssetId, flows, mode. Using an env set, login or session needs an admin key; repo must belong to the installation's GitHub account. Returns 503 github_not_configured until GitHub is switched on. Price: 4 credits per flow past 3, 3 more with a saved login or storage_state, and 5 credits per started 10 sandbox minutes past 25, charged at completion (hard stop at 45 minutes). See Recording.

Fetching

POST /v1/fetch: a reference from a social URL (1 credit)

{ "url": "https://www.tiktok.com/@…/video/…", "start": 3, "end": 18 }. url must be a public http or https address. Private hosts, link-local and metadata addresses (including IPv4-mapped IPv6), and other schemes are rejected with 400 invalid_url. start/end (seconds, optional) keep only that section: handy for a 4–30 s reference clip for POST /v1/videos. Hosted fetching is a per-account setting: if it is off you get 403 fetch_disabled. Ask us to enable it (POST /v1/reports works) and confirm you have the rights to the clips, or download it on your own machine and upload it instead. Output: a fetched asset with source attribution (and the trim applied).

Optional "then": "analyze_style" | "remix" | "none" starts the next step on the server as soon as the fetch succeeds, so the caller does not have to stay connected. Both analyze_style and remix start a style analysis of the fetched clip, charged like POST /v1/styles (2 credits for the first minute) when it starts; for remix, use the resulting styleId with your own clips or character. The fetch job's result.followUp holds { "then", "jobId" } (or { "then", "error" }, e.g. insufficient_credits, if it could not start), and the follow-up job's input.fromJobId points back at the fetch.

Templates

Social videos saved to replicate later, from the mobile app's share sheet or by your agent.

POST /v1/templates: save a template (free)

{ "url": "https://www.tiktok.com/@…/video/…", "title": "Hook style", "note": "Copy the fast cuts", "analyze": true }. Only url is required, and it has the same public http(s) rules as POST /v1/fetch. analyze: true also fetches the video and learns its style, charged like POST /v1/fetch with then: "analyze_style"; if hosted fetching is off the template is still saved and the response includes analyzeError.

GET /v1/templates · GET /v1/templates/{id} · DELETE /v1/templates/{id}

Each template has id, url, platform, title, note, status (saved, analyzing, ready, failed) and styleId once ready. To replicate one: use styleId with POST /v1/edits, or, if there is none yet, POST /v1/fetch its url with then: "analyze_style".

Jobs

GET /v1/jobs/{id} · GET /v1/jobs

{ "id": "jd7…", "kind": "replicate", "status": "succeeded", "progress": "done", "error": null,
  "result": { … }, "creditsCharged": 5, "refunded": false, "attempts": 1,
  "outputs": [ { "assetId": "j95…", "role": "edit", "kind": "video", "filename": "edit.mp4", "url": "https://…" } ],
  "createdAt": 1791469255706, "startedAt": 1791469256012, "finishedAt": 1791469281420 }

status: queued → running → succeeded | failed. A job with no word from the worker for three hours is marked failed and refunded.

Social accounts and posts

See Posting for the approval model.

Endpoint
GET /v1/social/accounts Connected accounts: { id, platform, username, displayName, status }
POST /v1/social/connect { "platform": "tiktok", "redirectUrl": "https://…" } → { "authUrl": "…" }
POST /v1/posts Draft a post (1 credit per account; 7 per X account when the caption has a link). See below.
GET /v1/posts Posts and their status
POST /v1/posts/{id}/approve Admin key. Publish or schedule a pending post.
POST /v1/posts/{id}/reject Admin key. Reject it; its credit is refunded.

POST /v1/posts:

{ "assetId": "j95…", "caption": "Dinner in one search #recipes",
  "targets": [ { "platform": "tiktok", "accountId": "6a62…" } ],
  "scheduledFor": "2026-10-12T18:00:00-04:00" }

Give scheduledFor (ISO 8601 with an offset, or with timezone) or "publishNow": true. Post statuses: pending_approval → approved → scheduled | published | failed, or rejected.

Team

Members, roles (admin, manager, creator, reviewer), keys, per-person credit caps and the shared library have their own page: Team. Teams (with invites) and budgets (with key caps) are separate switches, on for accounts created from now on; older accounts keep their behavior until we enable them, and until then those endpoints answer 403 governance_disabled.

GET /v1/members · POST /v1/members · GET/POST/DELETE /v1/members/{id} · POST /v1/members/{id}/keys People on the account, each with their own key and role.
GET /v1/keys · DELETE /v1/keys/{id} Every live key and whose it is.
GET /v1/teams · POST /v1/teams · POST/DELETE /v1/teams/{id} · POST /v1/members/{id}/team Teams and their managers (governance accounts).
GET /v1/allocations · POST /v1/allocations · DELETE /v1/allocations/{id} Member and team credit budgets, enforced when a job or post is accepted (402 budget_exceeded).
GET /v1/invites · POST /v1/invites · POST /v1/invites/links · DELETE /v1/invites/{id} Email invites that activate on Google sign-in, and expiring, limited-use invite links.
POST /v1/keys/{id}/cap Monthly credit cap on one key.
GET /v1/audit · GET /v1/me/memberships Audit log; the accounts you belong to.
GET /v1/library Brand kits, characters, styles, finished videos and uploads the team can reuse, with createdBy.

Reporting problems

Found a bug, want a feature, or have a question? File it through the API: agents can do this on their own when something goes wrong. Reports are free.

POST /v1/reports

Field Type
title string Required. Up to 200 characters.
description string Required. What happened, what you expected, steps to reproduce. Up to 10,000 characters.
kind string bug (default), feature or question.
severity string low, normal (default), high or blocking.
jobId string The job involved. Its status, error, inputs and QA report are attached automatically.
{ "kind": "bug", "severity": "high", "jobId": "jd7…",
  "title": "Captions cut off on 4:5 edits",
  "description": "The second caption line is clipped at the top. Expected it inside the frame." }

Returns 201 with the report. GET /v1/reports and GET /v1/reports/{id} show each report's status (open, acknowledged, fixed, wont_fix or closed) and the team's response.

POST /v1/feedback

Send product feedback: what to fix, add or keep. Free (0 credits), limited to 10 per hour per key. The MCP feedback tool and the /feedback prompt use this route.

Field Type
message string Required. Up to 4,000 characters.
category string bug, feature, praise or other.
contact string Optional email or handle for follow-up.
{ "message": "Let me pick the caption font per edit.", "category": "feature" }

Returns 201 with { "id": "...", "message": "Thank you for the feedback. ..." }.

Credits

Endpoint
GET /v1/billing { credits, costs, packs }
GET /v1/billing/ledger Every charge, refund and purchase, newest first

Agents: this page is also available as Markdown; the whole reference is in llms-full.txt.